From 2c682b4d1cdfd64d3a5b931bd0a67abb1f8b731e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Aug 2017 14:37:19 -0700 Subject: Small controller revisions, first run of controller API model JavaScript. --- controller/EmbeddedNetworkController.cpp | 101 ++---- controller/EmbeddedNetworkController.hpp | 5 +- controller/controller-api-model.js | 546 +++++++++++++++++++++++++++++++ 3 files changed, 577 insertions(+), 75 deletions(-) create mode 100644 controller/controller-api-model.js (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 72d47622..764b5c20 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -638,14 +638,10 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (newAuth != OSUtils::jsonBool(member["authorized"],false)) { member["authorized"] = newAuth; member[((newAuth) ? "lastAuthorizedTime" : "lastDeauthorizedTime")] = now; - - json ah; - ah["a"] = newAuth; - ah["by"] = "api"; - ah["ts"] = now; - ah["ct"] = json(); - ah["c"] = json(); - member["authHistory"].push_back(ah); + if (newAuth) { + member["lastAuthorizedCredentialType"] = "api"; + member["lastAuthorizedCredential"] = json(); + } // Member is being de-authorized, so spray Revocation objects to all online members if (!newAuth) { @@ -896,22 +892,15 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( if (b.count("authTokens")) { json &authTokens = b["authTokens"]; - if (authTokens.is_array()) { - json nat = json::array(); - for(unsigned long i=0;i 0) { - json t = json::object(); - t["token"] = tstr; - t["expires"] = OSUtils::jsonInt(token["expires"],0ULL); - t["maxUsesPerMember"] = OSUtils::jsonInt(token["maxUsesPerMember"],0ULL); - nat.push_back(t); - } - } + if (authTokens.is_object()) { + json nat; + for(json::iterator t(authTokens.begin());t!=authTokens.end();++t) { + if ((t.value().is_number())&&(t.value() >= 0)) + nat[t.key()] = t.value(); } network["authTokens"] = nat; + } else { + network["authTokens"] = {{}}; } } @@ -1268,56 +1257,29 @@ void EmbeddedNetworkController::_request( } // Determine whether and how member is authorized - const char *authorizedBy = (const char *)0; + bool authorized = false; bool autoAuthorized = false; json autoAuthCredentialType,autoAuthCredential; if (OSUtils::jsonBool(member["authorized"],false)) { - authorizedBy = "memberIsAuthorized"; + authorized = true; } else if (!OSUtils::jsonBool(network["private"],true)) { - authorizedBy = "networkIsPublic"; - json &ahist = member["authHistory"]; - if ((!ahist.is_array())||(ahist.size() == 0)) - autoAuthorized = true; + authorized = true; + autoAuthorized = true; + autoAuthCredentialType = "public"; } 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 now)) { + authorized = true; + autoAuthorized = true; + autoAuthCredentialType = "token"; + autoAuthCredential = presentedToken; } } } @@ -1325,23 +1287,16 @@ void EmbeddedNetworkController::_request( } // If we auto-authorized, update member record - if ((autoAuthorized)&&(authorizedBy)) { + if ((autoAuthorized)&&(authorized)) { 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); - + member["lastAuthorizedCredentialType"] = autoAuthCredentialType; + member["lastAuthorizedCredential"] = autoAuthCredential; json &revj = member["revision"]; member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); } - if (authorizedBy) { + if (authorized) { // Update version info and meta-data if authorized and if this is a genuine request if (requestPacketId) { const uint64_t vMajor = metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 8752922e..590a8b48 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -129,7 +129,6 @@ private: inline void _initMember(nlohmann::json &member) { if (!member.count("authorized")) member["authorized"] = false; - if (!member.count("authHistory")) member["authHistory"] = nlohmann::json::array(); if (!member.count("ipAssignments")) member["ipAssignments"] = nlohmann::json::array(); if (!member.count("activeBridge")) member["activeBridge"] = false; if (!member.count("tags")) member["tags"] = nlohmann::json::array(); @@ -139,6 +138,8 @@ private: if (!member.count("revision")) member["revision"] = 0ULL; if (!member.count("lastDeauthorizedTime")) member["lastDeauthorizedTime"] = 0ULL; if (!member.count("lastAuthorizedTime")) member["lastAuthorizedTime"] = 0ULL; + if (!member.count("lastAuthorizedCredentialType")) member["lastAuthorizedCredentialType"] = nlohmann::json(); + if (!member.count("lastAuthorizedCredential")) member["lastAuthorizedCredential"] = nlohmann::json(); if (!member.count("vMajor")) member["vMajor"] = -1; if (!member.count("vMinor")) member["vMinor"] = -1; if (!member.count("vRev")) member["vRev"] = -1; @@ -156,7 +157,7 @@ private: 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(); + if (!network.count("authTokens")) network["authTokens"] = {{}}; if (!network.count("capabilities")) network["capabilities"] = nlohmann::json::array(); if (!network.count("tags")) network["tags"] = nlohmann::json::array(); if (!network.count("routes")) network["routes"] = nlohmann::json::array(); diff --git a/controller/controller-api-model.js b/controller/controller-api-model.js new file mode 100644 index 00000000..7b61dff4 --- /dev/null +++ b/controller/controller-api-model.js @@ -0,0 +1,546 @@ +/* + * A JavaScript class based model for the ZeroTier controller microservice API + * Copyright (C) 2011-2017 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +'use strict'; + +/** + * Goes through a rule set array and makes sure it's valid, returning a canonicalized version + * + * @param {array[object]} rules Array of ZeroTier rules + * @return New array of canonicalized rules + * @throws {Error} Rule set is invalid + */ +function formatRuleSetArray(rules) +{ +} +exports.formatRuleSetArray = formatRuleSetArray; + +/** + * @param {string} IP with optional /netmask|port section + * @return 4, 6, or 0 if invalid + */ +function ipClassify(ip) +{ + if ((!ip)||(typeof ip !== 'string')) + return 0; + let ips = ip.split('/'); + if (ips.length > 0) { + if (ips.length > 1) { + if (ips[1].length === 0) + return 0; + for(let i=0;i 0) { + for(let i=0;i 0) { + for(let i=0;i= 0) { + r += c; + if (r.length === l) + break; + } + } + + while (r.length < l) + r = '0' + r; + + return r; +}; +exports.formatZeroTierIdentifier = formatZeroTierIdentifier; + +// Internal container classes +class _V4AssignMode +{ + get zt() { return (this._zt)||false; } + set zt(b) { this._zt = !!b; } + toJSON() + { + return { zt: this.zt }; + } +}; +class _v6AssignMode +{ + get ['6plane'] { return (this._6plane)||false; } + set ['6plane'](b) { this._6plane = !!b; } + get zt() { return (this._zt)||false; } + set zt(b) { this._zt = !!b; } + get rfc4193() { return (this._rfc4193)||false; } + set rfc4193(b) { this._rfc4193 = !!b; } + toJSON() + { + return { + zt: this.zt, + rfc4193: this.rfc4193, + '6plane': this['6plane'] + }; + } +} + +class Network +{ + constructor(obj) + { + this.clear(); + this.patch(obj); + } + + get objtype() { return 'network'; } + + get id() { return this._id; } + set id(x) { return (this._id = formatZeroTierIdentifier(x,16)); } + + get nwid() { return this._id; } // legacy + + get authTokens() { return this._authTokens; } + set authTokens(at) + { + this._authTokens = {}; + if ((at)&&(typeof at === 'object')&&(!Array.isArray(at))) { + for(let k in at) { + let exp = parseInt(at[k])||0; + if (exp >= 0) + this._authTokens[k] = exp; + } + } + return this._authTokens; + } + + get capabilities() { return this._capabilities; } + set capabilities(c) + { + let ca = []; + let ids = {}; + if ((c)&&(Array.isArray(c))) { + for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!ids[capId])) { + ids[capId] = true; + let capDefault = !!cap['default']; + let capRules = formatRuleSetArray(cap.rules); + ca.push({ + id: capId, + 'default': capDefault, + rules: capRules + }); + } + } + } + } + ca.sort(function(a,b) { + a = a.id; + b = b.id; + return ((a > b) ? 1 : ((a < b) ? -1 : 0)); + }); + this._capabilities = ca; + return ca; + } + + get ipAssignmentPools() return { this._ipAssignmentPools; } + set ipAssignmentPools(ipp) + { + let pa = []; + let ranges = {}; + if ((ipp)&&(Array.isArray(ipp))) { + for(let a=0;a 0)&&(stype === ipClassify(end))&&(!ranges[start+'_'+end])) { + ranges[start+'_'+end] = true; + pa.push({ ipRangeStart: start, ipRangeEnd: end }); + } + } + } + } + } + pa.sort(function(a,b) { return a.ipRangeStart.localeCompare(b.ipRangeStart); }); + this._ipAssignmentPools = pa; + return pa; + } + + get multicastLimit() return { this._multicastLimit; } + set multicastLimit(n) + { + try { + let nn = parseInt(n)||0; + this._multicastLimit = (nn >= 0) ? nn : 0; + } catch (e) { + this._multicastLimit = 0; + } + return this._multicastLimit; + } + + get routes() return { this._routes; } + set routes(r) + { + let ra = []; + let targets = {}; + if ((r)&&(Array.isArray(r))) { + for(let a=0;a 0)&&((routeVia === null)||(ipClassify(routeVia) === rtt))&&(!targets[routeTarget])) { + targets[routeTarget] = true; + ra.push({ target: routeTarget, via: routeVia }); + } + } + } + } + ra.sort(function(a,b) { return a.routeTarget.localeCompare(b.routeTarget); }); + this._routes = ra; + return ra; + } + + get tags() return { this._tags; } + set tags(t) + { + let ta = []; + if ((t)&&(Array.isArray(t))) { + for(let a=0;a= 0)||(tagId <= 0xffffffff)) { + let tagDefault = tag.default; + if (typeof tagDefault !== 'number') + tagDefault = parseInt(tagDefault)||null; + if ((tagDefault < 0)||(tagDefault > 0xffffffff)) + tagDefault = null; + ta.push({ 'id': tagId, 'default': tagDefault }); + } + } + } + } + ta.sort(function(a,b) { + a = a.id; + b = b.id; + return ((a > b) ? 1 : ((a < b) ? -1 : 0)); + }); + this._tags = ta; + return ta; + } + + get v4AssignMode() return { this._v4AssignMode; } + set v4AssignMode(m) + { + if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) { + this._v4AssignMode.zt = m.zt; + } else if (m === 'zt') { // legacy + this._v4AssignMode.zt = true; + } else { + this._v4AssignMode.zt = false; + } + } + + get v6AssignMode() return { this._v6AssignMode; } + set v6AssignMode(m) + { + if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) { + this._v6AssignMode.zt = m.zt; + this._v6AssignMode.rfc4193 = m.rfc4193; + this._v6AssignMode['6plane'] = m['6plane']; + } else if (typeof m === 'string') { // legacy + let ms = m.split(','); + this._v6AssignMode.zt = false; + this._v6AssignMode.rfc4193 = false; + this._v6AssignMode['6plane'] = false; + for(let i=0;i= 10000) mtu = 10000; // maximum as per ZT spec + this._mtu = mtu; + } + + get name() { return this._name; } + set name(n) + { + if (typeof n === 'string') + this._name = n; + else if (typeof n === 'number') + this._name = n.toString(); + else this._name = ''; + } + + get private() { return this._private; } + set private(b) + { + // This is really meaningful for security, so make true unless explicitly set to false. + this._private = (b !== false); + } + + get activeMemberCount() { return this.__activeMemberCount; } + get authorizedMemberCount() { return this.__authorizedMemberCount; } + get totalMemberCount() { return this.__totalMemberCount; } + get clock() { return this.__clock; } + get creationTime() { return this.__creationTime; } + get revision() { return this.__revision; } + + toJSONExcludeControllerGenerated() + { + return { + id: this.id, + objtype: 'network', + nwid: this.nwid, + authTokens: this.authTokens, + capabilities: this.capabilities, + ipAssignmentPools: this.ipAssignmentPools, + multicastLimit: this.multicastLimit, + routes: this.routes, + tags: this.tags, + v4AssignMode: this._v4AssignMode.toJSON(), + v6AssignMode: this._v6AssignMode.toJSON(), + rules: this.rules, + enableBroadcast: this.enableBroadcast, + mtu: this.mtu, + name: this.name, + 'private': this['private'] + }; + } + + toJSON() + { + var j = this.toJSONExcludeControllerGenerated(); + j.activeMemberCount = this.activeMemberCount; + j.authorizedMemberCount = this.authorizedMemberCount; + j.totalMemberCount = this.totalMemberCount; + j.clock = this.clock; + j.creationTime = this.creationTime; + j.revision = this.revision; + return j; + } + + clear() + { + this._id = ''; + this._authTokens = {}; + this._capabilities = []; + this._ipAssignmentPools = []; + this._multicastLimit = 32; + this._routes = []; + this._tags = []; + this._v4AssignMode = new _V4AssignMode(); + this._v6AssignMode = new _v6AssignMode(); + this._rules = []; + this._enableBroadcast = true; + this._mtu = 2800; + this._name = ''; + this._private = true; + + this.__activeMemberCount = 0; + this.__authorizedMemberCount = 0; + this.__totalMemberCount = 0; + this.__clock = 0; + this.__creationTime = 0; + this.__revision = 0; + } + + patch(obj) + { + if (obj instanceof Network) + obj = obj.toJSON(); + if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) { + for(var k in obj) { + try { + switch(k) { + case 'id': + case 'authTokens': + case 'capabilities': + case 'ipAssignmentPools': + case 'multicastLimit': + case 'routes': + case 'tags': + case 'rules': + case 'enableBroadcast': + case 'mtu': + case 'name': + case 'private': + case 'v4AssignMode': + case 'v6AssignMode': + this[k] = obj[k]; + break; + + case 'activeMemberCount': + case 'authorizedMemberCount': + case 'totalMemberCount': + case 'clock': + case 'creationTime': + case 'revision': + this['__'+k] = parseInt(obj[k])||0; + break; + } + } catch (e) {} + } + } + } +}; +exports.Network = Network; + +class Member +{ + constructor(obj) + { + this.clear(); + this.patch(obj); + } + + get objtype() { return 'member'; } + + get id() { return this._id; } + set id(x) { this._id = formatZeroTierIdentifier((typeof x === 'number') ? x.toString(16) : x,10); } + + get address() { return this._id; } // legacy + + get nwid() { return this._nwid; } + set nwid(x) { this._nwid = formatZeroTierIdentifier(x,16); } + + get controllerId() { return this.nwid.substr(0,10); } + + get authorized() { return this._authorized; } + set authorized(b) { this._authorized = (b === true); } // security critical so require explicit set to true + + get activeBridge() { return this._activeBridge; } + set activeBridge(b) { this._activeBridge = !!b; } + + get capabilities() { return this._capabilities; } + set capabilities(c) + { + } + + get identity() { return this._identity; } + set identity(istr) + { + if ((istr)&&(typeof istr === 'string')) + this._identity = istr; + else this._identity = null; + } + + get ipAssignments() { return this._ipAssignments; } + set ipAssignments(ipa) + { + } + + get noAutoAssignIps() { return this._noAutoAssignIps; } + set noAutoAssignIps(b) { this._noAutoAssignIps = !!b; } + + get tags() { return this._tags; } + set tags(t) + { + } + + clear() + { + this._id = ''; + this._nwid = ''; + this._authorized = false; + this._activeBridge = false; + this._capabilities = []; + this._identity = ''; + this._ipAssignments = []; + this._noAutoAssignIps = false; + this._tags = []; + + this.__creationTime = 0; + this.__lastAuthorizedTime = 0; + this.__lastAuthorizedCredentialType = null; + this.__lastAuthorizedCredential = null; + this.__lastDeauthorizedTime = 0; + this.__physicalAddr = ''; + this.__revision = 0; + this.__vMajor = 0; + this.__vMinor = 0; + this.__vRev = 0; + this.__vProto = 0; + } +}; +exports.Member = Member; -- cgit v1.2.3 From 1c04cc0485c58f4f84e75d090025ead2c0f8202d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 9 Aug 2017 17:42:35 -0700 Subject: . --- controller/controller-api-model.js | 286 ++++++++++++++++++++++++++++++++++--- 1 file changed, 267 insertions(+), 19 deletions(-) (limited to 'controller') diff --git a/controller/controller-api-model.js b/controller/controller-api-model.js index 7b61dff4..fbc808c0 100644 --- a/controller/controller-api-model.js +++ b/controller/controller-api-model.js @@ -26,18 +26,6 @@ 'use strict'; -/** - * Goes through a rule set array and makes sure it's valid, returning a canonicalized version - * - * @param {array[object]} rules Array of ZeroTier rules - * @return New array of canonicalized rules - * @throws {Error} Rule set is invalid - */ -function formatRuleSetArray(rules) -{ -} -exports.formatRuleSetArray = formatRuleSetArray; - /** * @param {string} IP with optional /netmask|port section * @return 4, 6, or 0 if invalid @@ -103,6 +91,135 @@ function formatZeroTierIdentifier(x,l) }; exports.formatZeroTierIdentifier = formatZeroTierIdentifier; +/** + * Goes through a rule set array and makes sure it's valid, returning a canonicalized version + * + * @param {array[object]} rules Array of ZeroTier rules + * @return New array of canonicalized rules + * @throws {Error} Rule set is invalid + */ +function formatRuleSetArray(rules) +{ + let r = []; + if ((rules)&&(Array.isArray(rules))) { + for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!caps[capId])) { + caps[capId] = true; + ca.push(capId); + } + } + } + ca.sort(); + this._capabilities = ca; + return ca; } get identity() { return this._identity; } @@ -508,6 +639,17 @@ class Member get ipAssignments() { return this._ipAssignments; } set ipAssignments(ipa) { + let ips = {}; + if ((ipa)&&(Array.isArray(ipa))) { + for(let a=0;a 0) + ips[ip] = true; + } + } + this._ipAssignments = Object.keys(ips); + this._ipAssignments.sort(); + return this._ipAssignments; } get noAutoAssignIps() { return this._noAutoAssignIps; } @@ -516,6 +658,73 @@ class Member get tags() { return this._tags; } set tags(t) { + let ta = []; + let pairs = {}; + if ((t)&&(Array.isArray(t))) { + for(let a=0;a= 0)&&(tagId <= 0xffffffff)&&(tagValue >= 0)&&(tagValue <= 0xffffffff)&&(!pairs[pk])) { + pairs[pk] = true; + ta.push([ tagId,tagValue ]); + } + } + } + } + ta.sort(function(a,b) { + return ((a[0] < b[0]) ? -1 : ((a[0] > b[0]) ? 1 : 0)); + }); + this._tags = ta; + return ta; + } + + get creationTime() { return this.__creationTime; } + get lastAuthorizedTime() { return this.__lastAuthorizedTime; } + get lastAuthorizedCredentialType() { return this.__lastAuthorizedCredentialType; } + get lastAuthorizedCredential() { return this.__lastAuthorizedCredential; } + get lastDeauthorizedTime() { return this.__lastDeauthorizedTime; } + get physicalAddr() { return this.__physicalAddr; } + get revision() { return this.__revision; } + get vMajor() { return this.__vMajor; } + get vMinor() { return this.__vMinor; } + get vRev() { return this.__vRev; } + get vProto() { return this.__vProto; } + + toJSONExcludeControllerGenerated() + { + return { + id: this.id, + nwid: this.nwid, + objtype: 'member', + address: this.id, + authorized: this.authorized, + activeBridge: this.activeBridge, + capabilities: this.capabilities, + identity: this.identity, + ipAssignments: this.ipAssignments, + noAutoAssignIps: this.noAutoAssignIps, + tags: this.tags + }; + } + + toJSON() + { + let j = this.toJSONExcludeControllerGenerated(); + j.creationTime = this.creationTime; + j.lastAuthorizedTime = this.lastAuthorizedTime; + j.lastAuthorizedCredentialType = this.lastAuthorizedCredentialType; + j.lastAuthorizedCredential = this.lastAuthorizedCredential; + j.lastDeauthorizedTime = this.lastDeauthorizedTime; + j.physicalAddr = this.physicalAddr; + j.revision = this.revision; + j.vMajor = this.vMajor; + j.vMinor = this.vMinor; + j.vRev = this.vRev; + j.vProto = this.vProto; + return j; } clear() @@ -542,5 +751,44 @@ class Member this.__vRev = 0; this.__vProto = 0; } + + patch(obj) + { + if (obj instanceof Member) + obj = obj.toJSON(); + if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) { + for(var k in obj) { + try { + switch(k) { + case 'id': + case 'nwid': + case 'authorized': + case 'activeBridge': + case 'capabilities': + case 'identity': + case 'ipAssignments': + case 'noAutoAssignIps': + case 'tags': + this[k] = obj[k]; + break; + + case 'creationTime': + case 'lastAuthorizedTime': + case 'lastAuthorizedCredentialType': + case 'lastAuthorizedCredential': + case 'lastDeauthorizedTime': + case 'physicalAddr': + case 'revision': + case 'vMajor': + case 'vMinor': + case 'vRev': + case 'vProto': + this['__'+k] = parseInt(obj[k])||0; + break; + } + } catch (e) {} + } + } + } }; exports.Member = Member; -- cgit v1.2.3 From 23fe8975e722ec0141be9c9bea9437b59e6088fa Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 14 Aug 2017 11:44:07 -0700 Subject: . --- controller/controller-api-model.js | 794 ------------------------------------- 1 file changed, 794 deletions(-) delete mode 100644 controller/controller-api-model.js (limited to 'controller') diff --git a/controller/controller-api-model.js b/controller/controller-api-model.js deleted file mode 100644 index fbc808c0..00000000 --- a/controller/controller-api-model.js +++ /dev/null @@ -1,794 +0,0 @@ -/* - * A JavaScript class based model for the ZeroTier controller microservice API - * Copyright (C) 2011-2017 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 . - * - * -- - * - * You can be released from the requirements of the license by purchasing - * a commercial license. Buying such a license is mandatory as soon as you - * develop commercial closed-source software that incorporates or links - * directly against ZeroTier software without disclosing the source code - * of your own application. - */ - -'use strict'; - -/** - * @param {string} IP with optional /netmask|port section - * @return 4, 6, or 0 if invalid - */ -function ipClassify(ip) -{ - if ((!ip)||(typeof ip !== 'string')) - return 0; - let ips = ip.split('/'); - if (ips.length > 0) { - if (ips.length > 1) { - if (ips[1].length === 0) - return 0; - for(let i=0;i 0) { - for(let i=0;i 0) { - for(let i=0;i= 0) { - r += c; - if (r.length === l) - break; - } - } - - while (r.length < l) - r = '0' + r; - - return r; -}; -exports.formatZeroTierIdentifier = formatZeroTierIdentifier; - -/** - * Goes through a rule set array and makes sure it's valid, returning a canonicalized version - * - * @param {array[object]} rules Array of ZeroTier rules - * @return New array of canonicalized rules - * @throws {Error} Rule set is invalid - */ -function formatRuleSetArray(rules) -{ - let r = []; - if ((rules)&&(Array.isArray(rules))) { - for(let a=0;a= 0) - this._authTokens[k] = exp; - } - } - return this._authTokens; - } - - get capabilities() { return this._capabilities; } - set capabilities(c) - { - let ca = []; - let ids = {}; - if ((c)&&(Array.isArray(c))) { - for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!ids[capId])) { - ids[capId] = true; - let capDefault = !!cap['default']; - let capRules = formatRuleSetArray(cap.rules); - ca.push({ - id: capId, - 'default': capDefault, - rules: capRules - }); - } - } - } - } - ca.sort(function(a,b) { - a = a.id; - b = b.id; - return ((a > b) ? 1 : ((a < b) ? -1 : 0)); - }); - this._capabilities = ca; - return ca; - } - - get ipAssignmentPools() { return this._ipAssignmentPools; } - set ipAssignmentPools(ipp) - { - let pa = []; - let ranges = {}; - if ((ipp)&&(Array.isArray(ipp))) { - for(let a=0;a 0)&&(stype === ipClassify(end))&&(!ranges[start+'_'+end])) { - ranges[start+'_'+end] = true; - pa.push({ ipRangeStart: start, ipRangeEnd: end }); - } - } - } - } - } - pa.sort(function(a,b) { return a.ipRangeStart.localeCompare(b.ipRangeStart); }); - this._ipAssignmentPools = pa; - return pa; - } - - get multicastLimit() { return this._multicastLimit; } - set multicastLimit(n) - { - try { - let nn = parseInt(n)||0; - this._multicastLimit = (nn >= 0) ? nn : 0; - } catch (e) { - this._multicastLimit = 0; - } - return this._multicastLimit; - } - - get routes() { return this._routes; } - set routes(r) - { - let ra = []; - let targets = {}; - if ((r)&&(Array.isArray(r))) { - for(let a=0;a 0)&&((routeVia === null)||(ipClassify(routeVia) === rtt))&&(!targets[routeTarget])) { - targets[routeTarget] = true; - ra.push({ target: routeTarget, via: routeVia }); - } - } - } - } - ra.sort(function(a,b) { return a.routeTarget.localeCompare(b.routeTarget); }); - this._routes = ra; - return ra; - } - - get tags() { return this._tags; } - set tags(t) - { - let ta = []; - if ((t)&&(Array.isArray(t))) { - for(let a=0;a= 0)||(tagId <= 0xffffffff)) { - let tagDefault = tag.default; - if (typeof tagDefault !== 'number') - tagDefault = parseInt(tagDefault)||null; - if ((tagDefault < 0)||(tagDefault > 0xffffffff)) - tagDefault = null; - ta.push({ 'id': tagId, 'default': tagDefault }); - } - } - } - } - ta.sort(function(a,b) { - a = a.id; - b = b.id; - return ((a > b) ? 1 : ((a < b) ? -1 : 0)); - }); - this._tags = ta; - return ta; - } - - get v4AssignMode() { return this._v4AssignMode; } - set v4AssignMode(m) - { - if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) { - this._v4AssignMode.zt = m.zt; - } else if (m === 'zt') { // legacy - this._v4AssignMode.zt = true; - } else { - this._v4AssignMode.zt = false; - } - } - - get v6AssignMode() { return this._v6AssignMode; } - set v6AssignMode(m) - { - if ((m)&&(typeof m === 'object')&&(!Array.isArray(m))) { - this._v6AssignMode.zt = m.zt; - this._v6AssignMode.rfc4193 = m.rfc4193; - this._v6AssignMode['6plane'] = m['6plane']; - } else if (typeof m === 'string') { // legacy - let ms = m.split(','); - this._v6AssignMode.zt = false; - this._v6AssignMode.rfc4193 = false; - this._v6AssignMode['6plane'] = false; - for(let i=0;i= 10000) mtu = 10000; // maximum as per ZT spec - this._mtu = mtu; - } - - get name() { return this._name; } - set name(n) - { - if (typeof n === 'string') - this._name = n; - else if (typeof n === 'number') - this._name = n.toString(); - else this._name = ''; - } - - get private() { return this._private; } - set private(b) - { - // This is really meaningful for security, so make true unless explicitly set to false. - this._private = (b !== false); - } - - get activeMemberCount() { return this.__activeMemberCount; } - get authorizedMemberCount() { return this.__authorizedMemberCount; } - get totalMemberCount() { return this.__totalMemberCount; } - get clock() { return this.__clock; } - get creationTime() { return this.__creationTime; } - get revision() { return this.__revision; } - - toJSONExcludeControllerGenerated() - { - return { - id: this.id, - objtype: 'network', - nwid: this.nwid, - authTokens: this.authTokens, - capabilities: this.capabilities, - ipAssignmentPools: this.ipAssignmentPools, - multicastLimit: this.multicastLimit, - routes: this.routes, - tags: this.tags, - v4AssignMode: this._v4AssignMode.toJSON(), - v6AssignMode: this._v6AssignMode.toJSON(), - rules: this.rules, - enableBroadcast: this.enableBroadcast, - mtu: this.mtu, - name: this.name, - 'private': this['private'] - }; - } - - toJSON() - { - var j = this.toJSONExcludeControllerGenerated(); - j.activeMemberCount = this.activeMemberCount; - j.authorizedMemberCount = this.authorizedMemberCount; - j.totalMemberCount = this.totalMemberCount; - j.clock = this.clock; - j.creationTime = this.creationTime; - j.revision = this.revision; - return j; - } - - clear() - { - this._id = ''; - this._authTokens = {}; - this._capabilities = []; - this._ipAssignmentPools = []; - this._multicastLimit = 32; - this._routes = []; - this._tags = []; - this._v4AssignMode = new _V4AssignMode(); - this._v6AssignMode = new _v6AssignMode(); - this._rules = []; - this._enableBroadcast = true; - this._mtu = 2800; - this._name = ''; - this._private = true; - - this.__activeMemberCount = 0; - this.__authorizedMemberCount = 0; - this.__totalMemberCount = 0; - this.__clock = 0; - this.__creationTime = 0; - this.__revision = 0; - } - - patch(obj) - { - if (obj instanceof Network) - obj = obj.toJSON(); - if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) { - for(var k in obj) { - try { - switch(k) { - case 'id': - case 'authTokens': - case 'capabilities': - case 'ipAssignmentPools': - case 'multicastLimit': - case 'routes': - case 'tags': - case 'rules': - case 'enableBroadcast': - case 'mtu': - case 'name': - case 'private': - case 'v4AssignMode': - case 'v6AssignMode': - this[k] = obj[k]; - break; - - case 'activeMemberCount': - case 'authorizedMemberCount': - case 'totalMemberCount': - case 'clock': - case 'creationTime': - case 'revision': - this['__'+k] = parseInt(obj[k])||0; - break; - } - } catch (e) {} - } - } - } -}; -exports.Network = Network; - -class Member -{ - constructor(obj) - { - this.clear(); - this.patch(obj); - } - - get objtype() { return 'member'; } - - get id() { return this._id; } - set id(x) { this._id = formatZeroTierIdentifier((typeof x === 'number') ? x.toString(16) : x,10); } - - get address() { return this._id; } // legacy - - get nwid() { return this._nwid; } - set nwid(x) { this._nwid = formatZeroTierIdentifier(x,16); } - - get controllerId() { return this.nwid.substr(0,10); } - - get authorized() { return this._authorized; } - set authorized(b) { this._authorized = (b === true); } // security critical so require explicit set to true - - get activeBridge() { return this._activeBridge; } - set activeBridge(b) { this._activeBridge = !!b; } - - get capabilities() { return this._capabilities; } - set capabilities(c) - { - let caps = {}; - let ca = []; - if ((c)&&(Array.isArray(c))) { - for(let a=0;a= 0)&&(capId <= 0xffffffff)&&(!caps[capId])) { - caps[capId] = true; - ca.push(capId); - } - } - } - ca.sort(); - this._capabilities = ca; - return ca; - } - - get identity() { return this._identity; } - set identity(istr) - { - if ((istr)&&(typeof istr === 'string')) - this._identity = istr; - else this._identity = null; - } - - get ipAssignments() { return this._ipAssignments; } - set ipAssignments(ipa) - { - let ips = {}; - if ((ipa)&&(Array.isArray(ipa))) { - for(let a=0;a 0) - ips[ip] = true; - } - } - this._ipAssignments = Object.keys(ips); - this._ipAssignments.sort(); - return this._ipAssignments; - } - - get noAutoAssignIps() { return this._noAutoAssignIps; } - set noAutoAssignIps(b) { this._noAutoAssignIps = !!b; } - - get tags() { return this._tags; } - set tags(t) - { - let ta = []; - let pairs = {}; - if ((t)&&(Array.isArray(t))) { - for(let a=0;a= 0)&&(tagId <= 0xffffffff)&&(tagValue >= 0)&&(tagValue <= 0xffffffff)&&(!pairs[pk])) { - pairs[pk] = true; - ta.push([ tagId,tagValue ]); - } - } - } - } - ta.sort(function(a,b) { - return ((a[0] < b[0]) ? -1 : ((a[0] > b[0]) ? 1 : 0)); - }); - this._tags = ta; - return ta; - } - - get creationTime() { return this.__creationTime; } - get lastAuthorizedTime() { return this.__lastAuthorizedTime; } - get lastAuthorizedCredentialType() { return this.__lastAuthorizedCredentialType; } - get lastAuthorizedCredential() { return this.__lastAuthorizedCredential; } - get lastDeauthorizedTime() { return this.__lastDeauthorizedTime; } - get physicalAddr() { return this.__physicalAddr; } - get revision() { return this.__revision; } - get vMajor() { return this.__vMajor; } - get vMinor() { return this.__vMinor; } - get vRev() { return this.__vRev; } - get vProto() { return this.__vProto; } - - toJSONExcludeControllerGenerated() - { - return { - id: this.id, - nwid: this.nwid, - objtype: 'member', - address: this.id, - authorized: this.authorized, - activeBridge: this.activeBridge, - capabilities: this.capabilities, - identity: this.identity, - ipAssignments: this.ipAssignments, - noAutoAssignIps: this.noAutoAssignIps, - tags: this.tags - }; - } - - toJSON() - { - let j = this.toJSONExcludeControllerGenerated(); - j.creationTime = this.creationTime; - j.lastAuthorizedTime = this.lastAuthorizedTime; - j.lastAuthorizedCredentialType = this.lastAuthorizedCredentialType; - j.lastAuthorizedCredential = this.lastAuthorizedCredential; - j.lastDeauthorizedTime = this.lastDeauthorizedTime; - j.physicalAddr = this.physicalAddr; - j.revision = this.revision; - j.vMajor = this.vMajor; - j.vMinor = this.vMinor; - j.vRev = this.vRev; - j.vProto = this.vProto; - return j; - } - - clear() - { - this._id = ''; - this._nwid = ''; - this._authorized = false; - this._activeBridge = false; - this._capabilities = []; - this._identity = ''; - this._ipAssignments = []; - this._noAutoAssignIps = false; - this._tags = []; - - this.__creationTime = 0; - this.__lastAuthorizedTime = 0; - this.__lastAuthorizedCredentialType = null; - this.__lastAuthorizedCredential = null; - this.__lastDeauthorizedTime = 0; - this.__physicalAddr = ''; - this.__revision = 0; - this.__vMajor = 0; - this.__vMinor = 0; - this.__vRev = 0; - this.__vProto = 0; - } - - patch(obj) - { - if (obj instanceof Member) - obj = obj.toJSON(); - if ((obj)&&(typeof obj === 'object')&&(!Array.isArray(obj))) { - for(var k in obj) { - try { - switch(k) { - case 'id': - case 'nwid': - case 'authorized': - case 'activeBridge': - case 'capabilities': - case 'identity': - case 'ipAssignments': - case 'noAutoAssignIps': - case 'tags': - this[k] = obj[k]; - break; - - case 'creationTime': - case 'lastAuthorizedTime': - case 'lastAuthorizedCredentialType': - case 'lastAuthorizedCredential': - case 'lastDeauthorizedTime': - case 'physicalAddr': - case 'revision': - case 'vMajor': - case 'vMinor': - case 'vRev': - case 'vProto': - this['__'+k] = parseInt(obj[k])||0; - break; - } - } catch (e) {} - } - } - } -}; -exports.Member = Member; -- cgit v1.2.3 From 50e7ea088b16314c8ad9d10757204c966155f157 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 16 Aug 2017 14:14:49 -0700 Subject: More work on controller for new Central harnessed mode, remove old http mode. --- controller/EmbeddedNetworkController.cpp | 42 ++++--- controller/EmbeddedNetworkController.hpp | 4 + controller/JSONDB.cpp | 208 +++++++++++-------------------- controller/JSONDB.hpp | 7 +- 4 files changed, 106 insertions(+), 155 deletions(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 764b5c20..257fef57 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -35,6 +35,7 @@ #include #include "../include/ZeroTierOne.h" +#include "../version.h" #include "../node/Constants.hpp" #include "EmbeddedNetworkController.hpp" @@ -430,7 +431,7 @@ EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPa _startTime(OSUtils::now()), _running(true), _lastDumpedStatus(0), - _db(dbPath), + _db(dbPath,this), _node(node) { } @@ -720,14 +721,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &revj = member["revision"]; member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetworkMember(nwid,address,member); - - // Push update to member if online - try { - Mutex::Lock _l(_memberStatus_m); - _MemberStatus &ms = _memberStatus[_MemberStatusKey(nwid,address)]; - if ((ms.online(now))&&(ms.lastRequestMetaData)) - request(nwid,InetAddress(),0,ms.identity,ms.lastRequestMetaData); - } catch ( ... ) {} } _addMemberNonPersistedFields(nwid,address,member,now); @@ -980,13 +973,6 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &revj = network["revision"]; network["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetwork(nwid,network); - - // Send an update to all members of the network that are online - Mutex::Lock _l(_memberStatus_m); - for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { - if ((i->first.networkId == nwid)&&(i->second.online(now))&&(i->second.lastRequestMetaData)) - request(nwid,InetAddress(),0,i->second.identity,i->second.lastRequestMetaData); - } } JSONDB::NetworkSummaryInfo ns; @@ -1144,6 +1130,28 @@ void EmbeddedNetworkController::handleRemoteTrace(const ZT_RemoteTrace &rt) } } +void EmbeddedNetworkController::onNetworkUpdate(const uint64_t networkId) +{ + // Send an update to all members of the network that are online + const uint64_t now = OSUtils::now(); + Mutex::Lock _l(_memberStatus_m); + for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { + if ((i->first.networkId == networkId)&&(i->second.online(now))&&(i->second.lastRequestMetaData)) + request(networkId,InetAddress(),0,i->second.identity,i->second.lastRequestMetaData); + } +} + +void EmbeddedNetworkController::onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId) +{ + // Push update to member if online + try { + Mutex::Lock _l(_memberStatus_m); + _MemberStatus &ms = _memberStatus[_MemberStatusKey(networkId,memberId)]; + if ((ms.online(OSUtils::now()))&&(ms.lastRequestMetaData)) + request(networkId,InetAddress(),0,ms.identity,ms.lastRequestMetaData); + } catch ( ... ) {} +} + void EmbeddedNetworkController::threadMain() throw() { @@ -1184,7 +1192,7 @@ void EmbeddedNetworkController::threadMain() first = false; }); } - OSUtils::ztsnprintf(tmp,sizeof(tmp),"],\"clock\":%llu,\"startTime\":%llu,\"uptime\":%llu}",(unsigned long long)now,(unsigned long long)_startTime,(unsigned long long)(now - _startTime)); + OSUtils::ztsnprintf(tmp,sizeof(tmp),"],\"clock\":%llu,\"startTime\":%llu,\"uptime\":%llu,\"vMajor\":%d,\"vMinor\":%d,\"vRev\":%d}",(unsigned long long)now,(unsigned long long)_startTime,(unsigned long long)(now - _startTime),ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION); st.append(tmp); _db.writeRaw("status",st); } diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 590a8b48..6200e910 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -93,6 +93,10 @@ public: void handleRemoteTrace(const ZT_RemoteTrace &rt); + // Called by JSONDB when networks and network members are changed + void onNetworkUpdate(const uint64_t networkId); + void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId); + void threadMain() throw(); diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp index 4b6824c2..a0dd50c2 100644 --- a/controller/JSONDB.cpp +++ b/controller/JSONDB.cpp @@ -29,75 +29,44 @@ #endif #include "JSONDB.hpp" - -#define ZT_JSONDB_HTTP_TIMEOUT 60000 +#include "EmbeddedNetworkController.hpp" namespace ZeroTier { static const nlohmann::json _EMPTY_JSON(nlohmann::json::object()); -static const std::map _ZT_JSONDB_GET_HEADERS; -JSONDB::JSONDB(const std::string &basePath) : +JSONDB::JSONDB(const std::string &basePath,EmbeddedNetworkController *parent) : + _parent(parent), _basePath(basePath), _rawInput(-1), _rawOutput(-1), _summaryThreadRun(true), _dataReady(false) { - if ((_basePath.length() > 7)&&(_basePath.substr(0,7) == "http://")) { - // If base path is http:// we run in HTTP mode - // TODO: this doesn't yet support IPv6 since bracketed address notiation isn't supported. - // Typically it's just used with 127.0.0.1 anyway. - std::string hn = _basePath.substr(7); - std::size_t hnend = hn.find_first_of('/'); - if (hnend != std::string::npos) - hn = hn.substr(0,hnend); - std::size_t hnsep = hn.find_last_of(':'); - if (hnsep != std::string::npos) - hn[hnsep] = '/'; - _httpAddr.fromString(hn.c_str()); - if (hnend != std::string::npos) - _basePath = _basePath.substr(7 + hnend); - if (_basePath.length() == 0) - _basePath = "/"; - if (_basePath[0] != '/') - _basePath = std::string("/") + _basePath; #ifndef __WINDOWS__ - } else if (_basePath == "-") { - // If base path is "-" we run in stdin/stdout mode and expect our database to be populated on startup via stdin - // Not supported on Windows + if (_basePath == "-") { + // If base path is "-" we run in Central harnessed mode. We read pseudo-http-requests from stdin and write + // them to stdout. _rawInput = STDIN_FILENO; _rawOutput = STDOUT_FILENO; fcntl(_rawInput,F_SETFL,O_NONBLOCK); -#endif } else { +#endif // Default mode of operation is to store files in the filesystem OSUtils::mkdir(_basePath.c_str()); OSUtils::lockDownFile(_basePath.c_str(),true); // networks might contain auth tokens, etc., so restrict directory permissions +#ifndef __WINDOWS__ } +#endif _networks_m.lock(); // locked until data is loaded, etc. if (_rawInput < 0) { - unsigned int cnt = 0; - while (!_load(_basePath)) { - if ((++cnt & 7) == 0) - fprintf(stderr,"WARNING: controller still waiting to read '%s'..." ZT_EOL_S,_basePath.c_str()); - Thread::sleep(250); - } - - for(std::unordered_map::iterator n(_networks.begin());n!=_networks.end();++n) - _summaryThreadToDo.push_back(n->first); - - if (_summaryThreadToDo.size() > 0) { - _summaryThread = Thread::start(this); - } else { - _dataReady = true; - _networks_m.unlock(); - } + _load(basePath); + _dataReady = true; + _networks_m.unlock(); } else { - // In IPC mode we wait for the first message to start, and we start - // this thread since this thread is responsible for reading from stdin. + // In harnessed mode we leave the lock locked and wait for our initial DB from Central. _summaryThread = Thread::start(this); } } @@ -128,16 +97,6 @@ bool JSONDB::writeRaw(const std::string &n,const std::string &obj) } else return true; #endif return false; - } else if (_httpAddr) { - std::map headers; - std::string body; - std::map reqHeaders; - char tmp[64]; - OSUtils::ztsnprintf(tmp,sizeof(tmp),"%lu",(unsigned long)obj.length()); - reqHeaders["Content-Length"] = tmp; - reqHeaders["Content-Type"] = "application/json"; - const unsigned int sc = Http::PUT(0,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast(&_httpAddr),(_basePath+"/"+n).c_str(),reqHeaders,obj.data(),(unsigned long)obj.length(),headers,body); - return (sc == 200); } else { const std::string path(_genPath(n,true)); if (!path.length()) @@ -205,10 +164,15 @@ void JSONDB::saveNetwork(const uint64_t networkId,const nlohmann::json &networkC char n[64]; OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId); writeRaw(n,OSUtils::jsonDump(networkConfig,-1)); + bool update; { Mutex::Lock _l(_networks_m); - _networks[networkId].config = nlohmann::json::to_msgpack(networkConfig); + _NW &nw = _networks[networkId]; + update = !nw.config.empty(); + nw.config = nlohmann::json::to_msgpack(networkConfig); } + if (update) + _parent->onNetworkUpdate(networkId); _recomputeSummaryInfo(networkId); } @@ -217,17 +181,25 @@ void JSONDB::saveNetworkMember(const uint64_t networkId,const uint64_t nodeId,co char n[256]; OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId); writeRaw(n,OSUtils::jsonDump(memberConfig,-1)); + bool update; { Mutex::Lock _l(_networks_m); - _networks[networkId].members[nodeId] = nlohmann::json::to_msgpack(memberConfig); + std::vector &m = _networks[networkId].members[nodeId]; + update = !m.empty(); + m = nlohmann::json::to_msgpack(memberConfig); _members[nodeId].insert(networkId); } + if (update) + _parent->onNetworkMemberUpdate(networkId,nodeId); _recomputeSummaryInfo(networkId); } nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId) { - if (!_httpAddr) { // Member deletion is done by Central in harnessed mode, and deleting the cache network entry also deletes all members + if (_rawOutput >= 0) { + // In harnessed mode, DB deletes occur in the Central database and we do + // not need to erase files. + } else { std::vector memberIds; { Mutex::Lock _l(_networks_m); @@ -239,24 +211,15 @@ nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId) } for(std::vector::iterator m(memberIds.begin());m!=memberIds.end();++m) eraseNetworkMember(networkId,*m,false); - } - char n[256]; - OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId); - - if (_rawOutput >= 0) { - // In harnessed mode, deletes occur in Central or other management - // software and do not need to be executed this way. - } else if (_httpAddr) { - std::map headers; - std::string body; - Http::DEL(0,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast(&_httpAddr),(_basePath+"/"+n).c_str(),_ZT_JSONDB_GET_HEADERS,headers,body); - } else { + char n[256]; + OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId); const std::string path(_genPath(n,false)); if (path.length()) OSUtils::rm(path.c_str()); } + // This also erases all members from the memory cache { Mutex::Lock _l(_networks_m); std::unordered_map::iterator i(_networks.find(networkId)); @@ -270,17 +233,11 @@ nlohmann::json JSONDB::eraseNetwork(const uint64_t networkId) nlohmann::json JSONDB::eraseNetworkMember(const uint64_t networkId,const uint64_t nodeId,bool recomputeSummaryInfo) { - char n[256]; - OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId); - if (_rawOutput >= 0) { - // In harnessed mode, deletes occur in Central or other management - // software and do not need to be executed this way. - } else if (_httpAddr) { - std::map headers; - std::string body; - Http::DEL(0,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast(&_httpAddr),(_basePath+"/"+n).c_str(),_ZT_JSONDB_GET_HEADERS,headers,body); + // In harnessed mode, DB deletes occur in Central and we do not remove files. } else { + char n[256]; + OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId); const std::string path(_genPath(n,false)); if (path.length()) OSUtils::rm(path.c_str()); @@ -320,7 +277,6 @@ void JSONDB::threadMain() while (_summaryThreadRun) { #ifndef __WINDOWS__ if (_rawInput < 0) { - // In HTTP and filesystem mode we just wait for summary to-do items Thread::sleep(25); } else { // In IPC mode we wait but also select() on STDIN to read database updates @@ -337,8 +293,8 @@ void JSONDB::threadMain() } else if (rawInputBuf.length() > 0) { try { const nlohmann::json obj(OSUtils::jsonParse(rawInputBuf)); - gotMessage = true; + if (!_dataReady) { _dataReady = true; _networks_m.unlock(); @@ -351,6 +307,7 @@ void JSONDB::threadMain() _add(obj); } } catch ( ... ) {} // ignore malformed JSON + rawInputBuf.clear(); } } @@ -369,7 +326,7 @@ void JSONDB::threadMain() else _summaryThreadToDo.swap(todo); } - if (!_dataReady) { + if (!_dataReady) { // sanity check _dataReady = true; _networks_m.unlock(); } @@ -460,17 +417,33 @@ bool JSONDB::_add(const nlohmann::json &j) if ((id.length() == 16)&&(objtype == "network")) { const uint64_t nwid = Utils::hexStrToU64(id.c_str()); if (nwid) { - Mutex::Lock _l(_networks_m); - _networks[nwid].config = nlohmann::json::to_msgpack(j); + bool update; + { + Mutex::Lock _l(_networks_m); + _NW &nw = _networks[nwid]; + update = !nw.config.empty(); + nw.config = nlohmann::json::to_msgpack(j); + } + if (update) + _parent->onNetworkUpdate(nwid); + _recomputeSummaryInfo(nwid); return true; } } else if ((id.length() == 10)&&(objtype == "member")) { const uint64_t mid = Utils::hexStrToU64(id.c_str()); const uint64_t nwid = Utils::hexStrToU64(OSUtils::jsonString(j["nwid"],"0").c_str()); if ((mid)&&(nwid)) { - Mutex::Lock _l(_networks_m); - _networks[nwid].members[mid] = nlohmann::json::to_msgpack(j); - _members[mid].insert(nwid); + bool update; + { + Mutex::Lock _l(_networks_m); + std::vector &m = _networks[nwid].members[mid]; + update = !m.empty(); + m = nlohmann::json::to_msgpack(j); + _members[mid].insert(nwid); + } + if (update) + _parent->onNetworkMemberUpdate(nwid,mid); + _recomputeSummaryInfo(nwid); return true; } } @@ -484,48 +457,21 @@ bool JSONDB::_load(const std::string &p) // This is not used in stdin/stdout mode. Instead data is populated by // sending it all to stdin. - if (_httpAddr) { - // In HTTP harnessed mode we download our entire working data set on startup. - - std::string body; - std::map headers; - const unsigned int sc = Http::GET(0,ZT_JSONDB_HTTP_TIMEOUT,reinterpret_cast(&_httpAddr),_basePath.c_str(),_ZT_JSONDB_GET_HEADERS,headers,body); - if (sc == 200) { - try { - nlohmann::json dbImg(OSUtils::jsonParse(body)); - std::string tmp; - if (dbImg.is_object()) { - Mutex::Lock _l(_networks_m); - for(nlohmann::json::iterator i(dbImg.begin());i!=dbImg.end();++i) { - try { - _add(i.value()); - } catch ( ... ) {} - } - return true; - } - } catch ( ... ) {} // invalid JSON, so maybe incomplete request - } - return false; - - } else { - // In regular mode we recursively read it from controller.d/ on disk - - std::vector dl(OSUtils::listDirectory(p.c_str(),true)); - for(std::vector::const_iterator di(dl.begin());di!=dl.end();++di) { - if ((di->length() > 5)&&(di->substr(di->length() - 5) == ".json")) { - std::string buf; - if (OSUtils::readFile((p + ZT_PATH_SEPARATOR_S + *di).c_str(),buf)) { - try { - _add(OSUtils::jsonParse(buf)); - } catch ( ... ) {} - } - } else { - this->_load((p + ZT_PATH_SEPARATOR_S + *di)); + std::vector dl(OSUtils::listDirectory(p.c_str(),true)); + for(std::vector::const_iterator di(dl.begin());di!=dl.end();++di) { + if ((di->length() > 5)&&(di->substr(di->length() - 5) == ".json")) { + std::string buf; + if (OSUtils::readFile((p + ZT_PATH_SEPARATOR_S + *di).c_str(),buf)) { + try { + _add(OSUtils::jsonParse(buf)); + } catch ( ... ) {} } + } else { + this->_load((p + ZT_PATH_SEPARATOR_S + *di)); } - return true; - } + + return true; } void JSONDB::_recomputeSummaryInfo(const uint64_t networkId) @@ -543,23 +489,15 @@ std::string JSONDB::_genPath(const std::string &n,bool create) if (pt.size() == 0) return std::string(); - char sep; - if (_httpAddr) { - sep = '/'; - create = false; - } else { - sep = ZT_PATH_SEPARATOR; - } - std::string p(_basePath); if (create) OSUtils::mkdir(p.c_str()); for(unsigned long i=0,j=(unsigned long)(pt.size()-1);i Date: Wed, 16 Aug 2017 14:41:42 -0700 Subject: Another Central harnessed mode fix. --- controller/EmbeddedNetworkController.cpp | 2 ++ controller/EmbeddedNetworkController.hpp | 2 +- controller/JSONDB.cpp | 8 -------- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 257fef57..3ca0f536 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -721,6 +721,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &revj = member["revision"]; member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetworkMember(nwid,address,member); + onNetworkMemberUpdate(nwid,address); } _addMemberNonPersistedFields(nwid,address,member,now); @@ -973,6 +974,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json &revj = network["revision"]; network["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetwork(nwid,network); + onNetworkUpdate(nwid); } JSONDB::NetworkSummaryInfo ns; diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index 6200e910..cbbe07ac 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -93,7 +93,7 @@ public: void handleRemoteTrace(const ZT_RemoteTrace &rt); - // Called by JSONDB when networks and network members are changed + // Called on update via POST or by JSONDB on external update of network or network member records void onNetworkUpdate(const uint64_t networkId); void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId); diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp index a0dd50c2..9813239e 100644 --- a/controller/JSONDB.cpp +++ b/controller/JSONDB.cpp @@ -164,15 +164,11 @@ void JSONDB::saveNetwork(const uint64_t networkId,const nlohmann::json &networkC char n[64]; OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx",(unsigned long long)networkId); writeRaw(n,OSUtils::jsonDump(networkConfig,-1)); - bool update; { Mutex::Lock _l(_networks_m); _NW &nw = _networks[networkId]; - update = !nw.config.empty(); nw.config = nlohmann::json::to_msgpack(networkConfig); } - if (update) - _parent->onNetworkUpdate(networkId); _recomputeSummaryInfo(networkId); } @@ -181,16 +177,12 @@ void JSONDB::saveNetworkMember(const uint64_t networkId,const uint64_t nodeId,co char n[256]; OSUtils::ztsnprintf(n,sizeof(n),"network/%.16llx/member/%.10llx",(unsigned long long)networkId,(unsigned long long)nodeId); writeRaw(n,OSUtils::jsonDump(memberConfig,-1)); - bool update; { Mutex::Lock _l(_networks_m); std::vector &m = _networks[networkId].members[nodeId]; - update = !m.empty(); m = nlohmann::json::to_msgpack(memberConfig); _members[nodeId].insert(networkId); } - if (update) - _parent->onNetworkMemberUpdate(networkId,nodeId); _recomputeSummaryInfo(networkId); } -- cgit v1.2.3 From 174ba8884ee68c3a54776ce7fe3f8249aa934ac6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 17 Aug 2017 13:10:10 -0700 Subject: Delete support in harnessed mode. --- controller/EmbeddedNetworkController.cpp | 26 +++++++++++-------- controller/EmbeddedNetworkController.hpp | 1 + controller/JSONDB.cpp | 44 +++++++++++++++++++++++++------- controller/JSONDB.hpp | 2 +- 4 files changed, 53 insertions(+), 20 deletions(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 3ca0f536..f5bfce4e 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -645,16 +645,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } // Member is being de-authorized, so spray Revocation objects to all online members - if (!newAuth) { - Revocation rev((uint32_t)_node->prng(),nwid,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(address),Revocation::CREDENTIAL_TYPE_COM); - rev.sign(_signingId); - - Mutex::Lock _l(_memberStatus_m); - for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { - if ((i->first.networkId == nwid)&&(i->second.online(now))) - _node->ncSendRevocation(Address(i->first.nodeId),rev); - } - } + if (!newAuth) + onNetworkMemberDeauthorize(nwid,address); } } @@ -1154,6 +1146,20 @@ void EmbeddedNetworkController::onNetworkMemberUpdate(const uint64_t networkId,c } catch ( ... ) {} } +void EmbeddedNetworkController::onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId) +{ + const uint64_t now = OSUtils::now(); + Revocation rev((uint32_t)_node->prng(),networkId,0,now,ZT_REVOCATION_FLAG_FAST_PROPAGATE,Address(memberId),Revocation::CREDENTIAL_TYPE_COM); + rev.sign(_signingId); + { + Mutex::Lock _l(_memberStatus_m); + for(auto i=_memberStatus.begin();i!=_memberStatus.end();++i) { + if ((i->first.networkId == networkId)&&(i->second.online(now))) + _node->ncSendRevocation(Address(i->first.nodeId),rev); + } + } +} + void EmbeddedNetworkController::threadMain() throw() { diff --git a/controller/EmbeddedNetworkController.hpp b/controller/EmbeddedNetworkController.hpp index cbbe07ac..d1217d60 100644 --- a/controller/EmbeddedNetworkController.hpp +++ b/controller/EmbeddedNetworkController.hpp @@ -96,6 +96,7 @@ public: // Called on update via POST or by JSONDB on external update of network or network member records void onNetworkUpdate(const uint64_t networkId); void onNetworkMemberUpdate(const uint64_t networkId,const uint64_t memberId); + void onNetworkMemberDeauthorize(const uint64_t networkId,const uint64_t memberId); void threadMain() throw(); diff --git a/controller/JSONDB.cpp b/controller/JSONDB.cpp index 9813239e..f362acf3 100644 --- a/controller/JSONDB.cpp +++ b/controller/JSONDB.cpp @@ -294,9 +294,9 @@ void JSONDB::threadMain() if (obj.is_array()) { for(unsigned long i=0;i &m = _networks[nwid].members[mid]; - update = !m.empty(); + if (!m.empty()) { + update = true; + nlohmann::json oldm(nlohmann::json::from_msgpack(m)); + deauth = ((OSUtils::jsonBool(oldm["authorized"],false))&&(!OSUtils::jsonBool(j["authorized"],false))); + } m = nlohmann::json::to_msgpack(j); _members[mid].insert(nwid); } - if (update) + if (update) { _parent->onNetworkMemberUpdate(nwid,mid); + if (deauth) + _parent->onNetworkMemberDeauthorize(nwid,mid); + } _recomputeSummaryInfo(nwid); return true; } + + } else if (objtype == "_delete") { // pseudo-object-type, only used in Central harnessed mode + + const std::string deleteType(OSUtils::jsonString(j["deleteType"],"")); + id = OSUtils::jsonString(j["deleteId"],""); + if ((deleteType == "network")&&(id.length() == 16)) { + eraseNetwork(Utils::hexStrToU64(id.c_str())); + } else if ((deleteType == "member")&&(id.length() == 10)) { + const std::string networkId(OSUtils::jsonString(j["deleteNetworkId"],"")); + const uint64_t nwid = Utils::hexStrToU64(networkId.c_str()); + const uint64_t mid = Utils::hexStrToU64(id.c_str()); + if (networkId.length() == 16) + eraseNetworkMember(nwid,mid,true); + _parent->onNetworkMemberDeauthorize(nwid,mid); + } + } } } catch ( ... ) {} @@ -455,7 +481,7 @@ bool JSONDB::_load(const std::string &p) std::string buf; if (OSUtils::readFile((p + ZT_PATH_SEPARATOR_S + *di).c_str(),buf)) { try { - _add(OSUtils::jsonParse(buf)); + _addOrUpdate(OSUtils::jsonParse(buf)); } catch ( ... ) {} } } else { diff --git a/controller/JSONDB.hpp b/controller/JSONDB.hpp index 66d0138a..44f4d7f5 100644 --- a/controller/JSONDB.hpp +++ b/controller/JSONDB.hpp @@ -157,7 +157,7 @@ public: throw(); private: - bool _add(const nlohmann::json &j); + bool _addOrUpdate(const nlohmann::json &j); bool _load(const std::string &p); void _recomputeSummaryInfo(const uint64_t networkId); std::string _genPath(const std::string &n,bool create); -- cgit v1.2.3 From 106dff0d5312ec7908dade36d537fdf85538413c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 18 Aug 2017 13:52:10 -0700 Subject: Make remote trace target null by default, which is probably what we want. --- controller/EmbeddedNetworkController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index f5bfce4e..0f342fa5 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1391,7 +1391,7 @@ void EmbeddedNetworkController::_request( if (rtt.length() == 10) { nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str())); } else { - nc->remoteTraceTarget = _signingId.address(); + nc->remoteTraceTarget.zero(); } } -- cgit v1.2.3 From 5bf5d5e9cbf799987897fd3ea07e212c1ff1ab6c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 30 Aug 2017 17:22:25 -0700 Subject: Minor controller stuff. --- controller/EmbeddedNetworkController.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 0f342fa5..8d5febd9 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -1242,7 +1242,7 @@ void EmbeddedNetworkController::_request( _initMember(member); { - std::string haveIdStr(OSUtils::jsonString(member["identity"],"")); + const std::string haveIdStr(OSUtils::jsonString(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 @@ -1308,8 +1308,6 @@ void EmbeddedNetworkController::_request( member["lastAuthorizedTime"] = now; member["lastAuthorizedCredentialType"] = autoAuthCredentialType; member["lastAuthorizedCredential"] = autoAuthCredential; - json &revj = member["revision"]; - member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); } if (authorized) { @@ -1338,6 +1336,7 @@ void EmbeddedNetworkController::_request( if (fromAddr) ms.physicalAddr = fromAddr; + char tmpip[64]; if (ms.physicalAddr) member["physicalAddr"] = ms.physicalAddr.toString(tmpip); @@ -1346,8 +1345,11 @@ void EmbeddedNetworkController::_request( } else { // If they are not authorized, STOP! _removeMemberNonPersistedFields(member); - if (origMember != member) + if (origMember != member) { + json &revj = member["revision"]; + member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetworkMember(nwid,identity.address().toInt(),member); + } _sender->ncSendError(nwid,requestPacketId,identity.address(),NetworkController::NC_ERROR_ACCESS_DENIED); return; } @@ -1710,8 +1712,11 @@ void EmbeddedNetworkController::_request( } _removeMemberNonPersistedFields(member); - if (member != origMember) + if (member != origMember) { + json &revj = member["revision"]; + member["revision"] = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); _db.saveNetworkMember(nwid,identity.address().toInt(),member); + } _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } -- cgit v1.2.3 From 283e8d5bc00c13f821c67ed7a431af4bd7694113 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 31 Aug 2017 18:01:21 -0400 Subject: Start threads in Central harnessed mode. --- controller/EmbeddedNetworkController.cpp | 16 ++++++++++++++++ controller/EmbeddedNetworkController.hpp | 15 +-------------- 2 files changed, 17 insertions(+), 14 deletions(-) (limited to 'controller') diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 8d5febd9..1d46d5e6 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -434,6 +434,8 @@ EmbeddedNetworkController::EmbeddedNetworkController(Node *node,const char *dbPa _db(dbPath,this), _node(node) { + if ((dbPath[0] == '-')&&(dbPath[1] == 0)) + _startThreads(); // start threads now in Central harnessed mode } EmbeddedNetworkController::~EmbeddedNetworkController() @@ -1721,4 +1723,18 @@ void EmbeddedNetworkController::_request( _sender->ncSendConfig(nwid,requestPacketId,identity.address(),*(nc.get()),metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6); } +void EmbeddedNetworkController::_startThreads() +{ + Mutex::Lock _l(_threads_m); + if (_threads.size() == 0) { + long hwc = (long)std::thread::hardware_concurrency(); + if (hwc < 1) + hwc = 1; + else if (hwc > 16) + hwc = 16; + for(long i=0;i &metaData); - - inline void _startThreads() - { - Mutex::Lock _l(_threads_m); - if (_threads.size() == 0) { - long hwc = (long)std::thread::hardware_concurrency(); - if (hwc < 1) - hwc = 1; - else if (hwc > 16) - hwc = 16; - for(long i=0;i