From faa9a06bf5302b246805ead12690b38c3036d802 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 17 Aug 2016 17:37:37 -0700 Subject: [PATCH] Controller fixes... --- controller/EmbeddedNetworkController.cpp | 219 ++++++++++++++--------- controller/README.md | 2 +- node/IncomingPacket.cpp | 6 +- service/OneService.cpp | 4 + 4 files changed, 148 insertions(+), 83 deletions(-) diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp index 30072f95f..649ff0947 100644 --- a/controller/EmbeddedNetworkController.cpp +++ b/controller/EmbeddedNetworkController.cpp @@ -60,6 +60,50 @@ using json = nlohmann::json; namespace ZeroTier { +static uint64_t _jI(const json &jv,const uint64_t dfl) +{ + if (jv.is_number()) { + return (uint64_t)jv; + } else if (jv.is_string()) { + std::string s = jv; + return Utils::strToU64(s.c_str()); + } else if (jv.is_boolean()) { + return ((bool)jv ? 1ULL : 0ULL); + } + return dfl; +} +static bool _jB(const json &jv,const bool dfl) +{ + if (jv.is_boolean()) { + return (bool)jv; + } else if (jv.is_number()) { + return ((uint64_t)jv > 0ULL); + } else if (jv.is_string()) { + std::string s = jv; + if (s.length() > 0) { + switch(s[0]) { + case 't': + case 'T': + case '1': + return true; + } + } + return false; + } + return dfl; +} +static std::string _jS(const json &jv,const char *dfl) +{ + if (jv.is_string()) { + return jv; + } else if (jv.is_number()) { + return jv; + } else if (jv.is_boolean()) { + return ((bool)jv ? std::string("1") : std::string("0")); + } + return std::string((dfl) ? dfl : ""); +} + static json _renderRule(ZT_VirtualNetworkRule &rule) { char tmp[128]; @@ -190,7 +234,7 @@ static bool _parseRule(const json &r,ZT_VirtualNetworkRule &rule) return false; std::string t = r["type"]; memset(&rule,0,sizeof(ZT_VirtualNetworkRule)); - if (r.value("not",false)) + if (_jB(r["not"],false)) rule.t = 0x80; else rule.t = 0x00; if (t == "ACTION_DROP") { @@ -201,91 +245,91 @@ static bool _parseRule(const json &r,ZT_VirtualNetworkRule &rule) return true; } else if (t == "ACTION_TEE") { rule.t |= ZT_NETWORK_RULE_ACTION_TEE; - rule.v.zt = Utils::hexStrToU64(r.value("zt","0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "ACTION_REDIRECT") { rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT; - rule.v.zt = Utils::hexStrToU64(r.value("zt","0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "MATCH_SOURCE_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(r.value("zt","0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "MATCH_DEST_ZEROTIER_ADDRESS") { rule.t |= ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS; - rule.v.zt = Utils::hexStrToU64(r.value("zt","0").c_str()) & 0xffffffffffULL; + rule.v.zt = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL; return true; } else if (t == "MATCH_VLAN_ID") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_ID; - rule.v.vlanId = (uint16_t)(r.value("vlanId",0ULL) & 0xffffULL); + rule.v.vlanId = (uint16_t)(_jI(r["vlanId"],0ULL) & 0xffffULL); return true; } else if (t == "MATCH_VLAN_PCP") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_PCP; - rule.v.vlanPcp = (uint8_t)(r.value("vlanPcp",0ULL) & 0xffULL); + rule.v.vlanPcp = (uint8_t)(_jI(r["vlanPcp"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_VLAN_DEI") { rule.t |= ZT_NETWORK_RULE_MATCH_VLAN_DEI; - rule.v.vlanDei = (uint8_t)(r.value("vlanDei",0ULL) & 0xffULL); + rule.v.vlanDei = (uint8_t)(_jI(r["vlanDei"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_ETHERTYPE") { rule.t |= ZT_NETWORK_RULE_MATCH_ETHERTYPE; - rule.v.etherType = (uint16_t)(r.value("etherType",0ULL) & 0xffffULL); + rule.v.etherType = (uint16_t)(_jI(r["etherType"],0ULL) & 0xffffULL); return true; } else if (t == "MATCH_MAC_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_SOURCE; - const std::string mac(r.value("mac","0")); + const std::string mac(_jS(r["mac"],"0")); Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; } else if (t == "MATCH_MAC_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_MAC_DEST; - const std::string mac(r.value("mac","0")); + const std::string mac(_jS(r["mac"],"0")); Utils::unhex(mac.c_str(),(unsigned int)mac.length(),rule.v.mac,6); return true; } else if (t == "MATCH_IPV4_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_SOURCE; - InetAddress ip(r.value("ip","0.0.0.0")); + InetAddress ip(_jS(r["ip"],"0.0.0.0")); rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; } else if (t == "MATCH_IPV4_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV4_DEST; - InetAddress ip(r.value("ip","0.0.0.0")); + InetAddress ip(_jS(r["ip"],"0.0.0.0")); rule.v.ipv4.ip = reinterpret_cast(&ip)->sin_addr.s_addr; rule.v.ipv4.mask = Utils::ntoh(reinterpret_cast(&ip)->sin_port) & 0xff; if (rule.v.ipv4.mask > 32) rule.v.ipv4.mask = 32; return true; } else if (t == "MATCH_IPV6_SOURCE") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_SOURCE; - InetAddress ip(r.value("ip","::0")); + InetAddress ip(_jS(r["ip"],"::0")); memcpy(rule.v.ipv6.ip,reinterpret_cast(&ip)->sin6_addr.s6_addr,16); rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; } else if (t == "MATCH_IPV6_DEST") { rule.t |= ZT_NETWORK_RULE_MATCH_IPV6_DEST; - InetAddress ip(r.value("ip","::0")); + InetAddress ip(_jS(r["ip"],"::0")); memcpy(rule.v.ipv6.ip,reinterpret_cast(&ip)->sin6_addr.s6_addr,16); rule.v.ipv6.mask = Utils::ntoh(reinterpret_cast(&ip)->sin6_port) & 0xff; if (rule.v.ipv6.mask > 128) rule.v.ipv6.mask = 128; return true; } else if (t == "MATCH_IP_TOS") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_TOS; - rule.v.ipTos = (uint8_t)(r.value("ipTos",0ULL) & 0xffULL); + rule.v.ipTos = (uint8_t)(_jI(r["ipTos"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_IP_PROTOCOL") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_PROTOCOL; - rule.v.ipProtocol = (uint8_t)(r.value("ipProtocol",0ULL) & 0xffULL); + rule.v.ipProtocol = (uint8_t)(_jI(r["ipProtocol"],0ULL) & 0xffULL); return true; } else if (t == "MATCH_IP_SOURCE_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE; - rule.v.port[0] = (uint16_t)(r.value("start",0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(r.value("end",(uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; } else if (t == "MATCH_IP_DEST_PORT_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE; - rule.v.port[0] = (uint16_t)(r.value("start",0ULL) & 0xffffULL); - rule.v.port[1] = (uint16_t)(r.value("end",(uint64_t)rule.v.port[0]) & 0xffffULL); + rule.v.port[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); + rule.v.port[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.port[0]) & 0xffffULL); return true; } else if (t == "MATCH_CHARACTERISTICS") { rule.t |= ZT_NETWORK_RULE_MATCH_CHARACTERISTICS; @@ -310,28 +354,28 @@ static bool _parseRule(const json &r,ZT_VirtualNetworkRule &rule) return true; } else if (t == "MATCH_FRAME_SIZE_RANGE") { rule.t |= ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE; - rule.v.frameSize[0] = (uint16_t)(r.value("start",0ULL) & 0xffffULL); - rule.v.frameSize[1] = (uint16_t)(r.value("end",(uint64_t)rule.v.frameSize[0]) & 0xffffULL); + rule.v.frameSize[0] = (uint16_t)(_jI(r["start"],0ULL) & 0xffffULL); + rule.v.frameSize[1] = (uint16_t)(_jI(r["end"],(uint64_t)rule.v.frameSize[0]) & 0xffffULL); return true; } else if (t == "MATCH_TAGS_SAMENESS") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_SAMENESS; - rule.v.tag.id = (uint32_t)(r.value("id",0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(r.value("value",0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_AND") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND; - rule.v.tag.id = (uint32_t)(r.value("id",0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(r.value("value",0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_OR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR; - rule.v.tag.id = (uint32_t)(r.value("id",0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(r.value("value",0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); return true; } else if (t == "MATCH_TAGS_BITWISE_XOR") { rule.t |= ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR; - rule.v.tag.id = (uint32_t)(r.value("id",0ULL) & 0xffffffffULL); - rule.v.tag.value = (uint32_t)(r.value("value",0ULL) & 0xffffffffULL); + rule.v.tag.id = (uint32_t)(_jI(r["id"],0ULL) & 0xffffffffULL); + rule.v.tag.value = (uint32_t)(_jI(r["value"],0ULL) & 0xffffffffULL); return true; } return false; @@ -613,7 +657,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( json member(_readJson(memberJP)); { - std::string haveIdStr = member.value("identity",""); + std::string haveIdStr(_jS(member["identity"],"")); if (haveIdStr.length() > 0) { try { if (Identity(haveIdStr.c_str()) != identity) @@ -630,13 +674,18 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( member["id"] = identity.address().toString(); member["address"] = member["id"]; member["nwid"] = network["id"]; - member["memberRevision"] = member.value("memberRevision",0ULL) + 1; + member["lastModified"] = now; + { + auto revj = member["revision"]; + const uint64_t rev = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + member["revision"] = rev; + } // Determine whether and how member is authorized const char *authorizedBy = (const char *)0; - if (!network.value("private",true)) { + if (!_jB(network["private"],true)) { authorizedBy = "networkIsPublic"; - } else if (member.value("authorized",false)) { + } else if (_jB(member["authorized"],false)) { authorizedBy = "memberIsAuthorized"; } else { char atok[256]; @@ -648,8 +697,8 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( for(unsigned long i=0;i now)) && (tok.length() > 0) && (tok == atok) ) { authorizedBy = "token"; break; @@ -700,14 +749,14 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( // If we made it this far, they are authorized. nc.networkId = nwid; - nc.type = network.value("private",true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; + nc.type = _jB(network["private"],true) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC; nc.timestamp = now; - nc.revision = network.value("revision",0ULL); + nc.revision = _jI(network["revision"],0ULL); nc.issuedTo = identity.address(); - if (network.value("enableBroadcast",true)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST; - if (network.value("allowPassiveBridging",false)) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING; - Utils::scopy(nc.name,sizeof(nc.name),network.value("name","").c_str()); - nc.multicastLimit = (unsigned int)network.value("multicastLimit",32ULL); + 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); bool amActiveBridge = false; { @@ -732,11 +781,11 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( auto rules = network["rules"]; if (v6AssignMode.is_object()) { - if ((v6AssignMode.value("rfc4193",false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + 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 ((v6AssignMode.value("6plane",false))&&(nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) { + 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; } @@ -757,8 +806,8 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( if (nc.routeCount >= ZT_MAX_NETWORK_ROUTES) break; auto route = routes[i]; - InetAddress t(route.value("target","")); - InetAddress v(route.value("via","")); + InetAddress t(_jS(route["target"],"")); + InetAddress v(_jS(route["via"],"")); if ((t)&&(v)&&(t.ss_family == v.ss_family)) { ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]); *(reinterpret_cast(&(r->target))) = t; @@ -803,13 +852,13 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( std::set allocatedIps; bool allocatedIpsLoaded = false; - if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(v6AssignMode.value("zt",false))) && (!haveManagedIpv6AutoAssignment) && (!amActiveBridge) ) { + if ( (ipAssignmentPools.is_array()) && ((v6AssignMode.is_object())&&(_jB(v6AssignMode["zt"],false))) && (!haveManagedIpv6AutoAssignment) && (!amActiveBridge) ) { if (!allocatedIpsLoaded) allocatedIps = _getAlreadyAllocatedIps(nwid); for(unsigned long p=0;((p(&ipRangeStart)->sin_addr.s_addr)); uint32_t ipRangeEnd = Utils::ntoh((uint32_t)(reinterpret_cast(&ipRangeEnd)->sin_addr.s_addr)); @@ -921,7 +970,7 @@ NetworkController::ResultCode EmbeddedNetworkController::doNetworkConfigRequest( } } - if (network.value("private",true)) { + if (_jB(network["private"],true)) { CertificateOfMembership com(now,ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA,nwid,identity.address()); if (com.sign(signingId)) { nc.com = com; @@ -983,8 +1032,10 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); responseBody.append(*i); responseBody.append("\":"); - const std::string rc = member.value("memberRevision","0"); - responseBody.append(rc); + auto rev = member["revision"]; + if (rev.is_number()) + responseBody.append(rev); + else responseBody.push_back('0'); } } } @@ -1006,7 +1057,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpGET( auto recentLog = member["recentLog"]; if ((recentLog.is_array())&&(recentLog.size() > 0)) { auto mostRecentLog = recentLog[0]; - if ((mostRecentLog.is_object())&&((uint64_t)mostRecentLog.value("ts",0ULL) >= threshold)) { + if ((mostRecentLog.is_object())&&(_jI(mostRecentLog["ts"],0ULL) >= threshold)) { responseBody.append((responseBody.length() == 1) ? "\"" : ",\""); responseBody.append(*i); responseBody.append("\":"); @@ -1116,8 +1167,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json member(_readJson(_memberJP(nwid,Address(address),true))); try { - if (b.count("authorized")) member["authorized"] = b.value("authorized",false); - if ((b.count("identity"))&&(!member.count("identity"))) member["identity"] = b.value("identity",""); // allow identity to be populated only if not already known + if (b.count("authorized")) member["authorized"] = _jB(b["authorized"],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("ipAssignments")) { auto ipa = b["ipAssignments"]; @@ -1144,12 +1195,17 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( member["id"] = addrs; member["address"] = addrs; // legacy member["nwid"] = nwids; - member["memberRevision"] = member.value("memberRevision",0ULL) + 1; member["objtype"] = "member"; + member["lastModified"] = OSUtils::now(); + { + auto revj = member["revision"]; + const uint64_t rev = (revj.is_number() ? ((uint64_t)revj + 1ULL) : 1ULL); + member["revision"] = rev; + } _writeJson(_memberJP(nwid,Address(address),true).c_str(),member); - member["clock"] = OSUtils::now(); + member["clock"] = member["lastModified"]; responseBody = member.dump(2); responseContentType = "application/json"; return 200; @@ -1178,7 +1234,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } } } - test->reportAtEveryHop = (b.value("reportAtEveryHop",true) ? 1 : 0); + test->reportAtEveryHop = (_jB(b["reportAtEveryHop"],true) ? 1 : 0); if (!test->hopCount) { ::free((void *)test); @@ -1226,11 +1282,11 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( json network(_readJson(_networkJP(nwid,true))); try { - if (b.count("name")) network["name"] = b.value("name",""); - if (b.count("private")) network["private"] = b.value("private",true); - if (b.count("enableBroadcast")) network["enableBroadcast"] = b.value("enableBroadcast",false); - if (b.count("allowPassiveBridging")) network["allowPassiveBridging"] = b.value("allowPassiveBridging",false); - if (b.count("multicastLimit")) network["multicastLimit"] = b.value("multicastLimit",32ULL); + if (b.count("name")) network["name"] = _jS(b["name"],""); + if (b.count("private")) network["private"] = _jB(b["private"],true); + if (b.count("enableBroadcast")) network["enableBroadcast"] = _jB(b["enableBroadcast"],false); + if (b.count("allowPassiveBridging")) network["allowPassiveBridging"] = _jB(b["allowPassiveBridging"],false); + if (b.count("multicastLimit")) network["multicastLimit"] = _jI(b["multicastLimit"],32ULL); if (b.count("activeBridges")) { auto ab = b["activeBridges"]; @@ -1249,10 +1305,10 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( auto nv4m = network["v4AssignMode"]; if (!nv4m.is_object()) nv4m = json::object(); if (b["v4AssignMode"].is_string()) { // backward compatibility - nv4m["zt"] = (b.value("v4AssignMode","") == "zt"); + nv4m["zt"] = (_jS(b["v4AssignMode"],"") == "zt"); } else if (b["v4AssignMode"].is_object()) { auto v4m = b["v4AssignMode"]; - if (v4m.count("zt")) nv4m["zt"] = v4m.value("zt",false); + if (v4m.count("zt")) nv4m["zt"] = _jB(v4m["zt"],false); } if (!nv4m.count("zt")) nv4m["zt"] = false; } @@ -1261,7 +1317,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( auto nv6m = network["v6AssignMode"]; if (!nv6m.is_object()) nv6m = json::object(); if (b["v6AssignMode"].is_string()) { // backward compatibility - std::vector v6m(Utils::split(b.value("v6AssignMode","").c_str(),",","","")); + std::vector v6m(Utils::split(_jS(b["v6AssignMode"],"").c_str(),",","","")); std::sort(v6m.begin(),v6m.end()); v6m.erase(std::unique(v6m.begin(),v6m.end()),v6m.end()); for(std::vector::iterator i(v6m.begin());i!=v6m.end();++i) { @@ -1274,9 +1330,9 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( } } else if (b["v6AssignMode"].is_object()) { auto v6m = b["v6AssignMode"]; - if (v6m.count("rfc4193")) nv6m["rfc4193"] = v6m.value("rfc4193",false); - if (v6m.count("zt")) nv6m["rfc4193"] = v6m.value("zt",false); - if (v6m.count("6plane")) nv6m["rfc4193"] = v6m.value("6plane",false); + if (v6m.count("rfc4193")) nv6m["rfc4193"] = _jB(v6m["rfc4193"],false); + if (v6m.count("zt")) nv6m["rfc4193"] = _jB(v6m["zt"],false); + if (v6m.count("6plane")) nv6m["rfc4193"] = _jB(v6m["6plane"],false); } if (!nv6m.count("rfc4193")) nv6m["rfc4193"] = false; if (!nv6m.count("zt")) nv6m["zt"] = false; @@ -1289,8 +1345,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( for(unsigned long i=0;i 0) { json t = json::object(); t["token"] = tstr; - t["expires"] = token.value("expires",0ULL); + t["expires"] = _jI(token["expires"],0ULL); nat.push_back(t); } } @@ -1372,8 +1428,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( 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("v4AssignMode")) network["v4AssignMode"] = "{\"zt\":false}"_json; - if (!network.count("v6AssignMode")) network["v6AssignMode"] = "{\"rfc4193\":false,\"zt\":false,\"6plane\":false}"_json; + if (!network.count("v4AssignMode")) network["v4AssignMode"] = {{"zt",false}}; + if (!network.count("v6AssignMode")) network["v6AssignMode"] = {{"rfc4193",false},{"zt",false},{"6plane",false}}; if (!network.count("activeBridges")) network["activeBridges"] = json::array(); if (!network.count("authTokens")) network["authTokens"] = json::array(); @@ -1387,7 +1443,8 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST( network["id"] = nwids; network["nwid"] = nwids; // legacy - network["revision"] = network.value("revision",0ULL) + 1ULL; + auto rev = network["revision"]; + network["revision"] = (rev.is_number() ? ((uint64_t)rev + 1ULL) : 1ULL); network["objtype"] = "network"; _writeJson(_networkJP(nwid,true),network); diff --git a/controller/README.md b/controller/README.md index 2c7541ae3..0b57dd25d 100644 --- a/controller/README.md +++ b/controller/README.md @@ -5,7 +5,7 @@ ZeroTier's 16-digit network IDs are really just a concatenation of the 10-digit This code implements the *node/NetworkController.hpp* interface to provide an embedded microservice configurable via the same local HTTP control plane as ZeroTier One iteself. It is built by default in ZeroTier One in desktop and server builds. This is the same code we use to run [my.zerotier.com](https://my.zerotier.com/), which is a web UI and API that runs in front of a pool of controllers. -Data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, tar'd, placed in `git`, or edited in place, though we do not recommend doing the latter while the controller is running. Also take care if editing in place that you do not save corrupted JSON since the controller may then lose data when it attempts to load, modify, and save. +Data is stored in JSON format under `controller.d` in the ZeroTier working directory. It can be copied, tar'd, placed in `git`, etc. Technically the JSON files under `controller.d` can also be edited in place, but we do not recommend doing this under a running controller since data loss or corruption might result. Also take care to keep JSON values of the correct types or data loss may also result. ### Scalability and Reliability diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 5c9e80f80..e4f091060 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -804,8 +804,12 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.armor(peer->key(),true); RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size()); } + } catch (std::exception &exc) { + 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(),_remoteAddress.toString().c_str(),exc.what()); } catch ( ... ) { - TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str()); + fprintf(stderr,"WARNING: network config request failed with exception: unknown exception" ZT_EOL_S); + TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): unknown exception",source().toString().c_str(),_remoteAddress.toString().c_str()); } return true; } diff --git a/service/OneService.cpp b/service/OneService.cpp index 746281684..7ce45bebc 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -1705,7 +1705,11 @@ public: 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; }