summaryrefslogtreecommitdiff
path: root/controller
diff options
context:
space:
mode:
Diffstat (limited to 'controller')
-rw-r--r--controller/SqliteNetworkController.cpp755
1 files changed, 374 insertions, 381 deletions
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
index 14374eff..ddf610cb 100644
--- a/controller/SqliteNetworkController.cpp
+++ b/controller/SqliteNetworkController.cpp
@@ -418,8 +418,380 @@ SqliteNetworkController::~SqliteNetworkController()
NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,NetworkConfig &nc)
{
- Mutex::Lock _l(_lock);
- return _doNetworkConfigRequest(fromAddr,signingId,identity,nwid,metaData,nc);
+ if (((!signingId)||(!signingId.hasPrivate()))||(signingId.address().toInt() != (nwid >> 24))) {
+ return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
+ }
+
+ const uint64_t now = OSUtils::now();
+
+ NetworkRecord network;
+ memset(&network,0,sizeof(network));
+ Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid);
+
+ MemberRecord member;
+ memset(&member,0,sizeof(member));
+ Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt());
+
+ { // begin lock
+ Mutex::Lock _l(_lock);
+
+ // Check rate limit circuit breaker to prevent flooding
+ {
+ uint64_t &lrt = _lastRequestTime[std::pair<uint64_t,uint64_t>(identity.address().toInt(),nwid)];
+ if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
+ return NetworkController::NETCONF_QUERY_IGNORE;
+ lrt = now;
+ }
+
+ _backupNeeded = true;
+
+ // Create Node record or do full identity check if we already have one
+
+ sqlite3_reset(_sGetNodeIdentity);
+ sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC);
+ if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) {
+ try {
+ Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0));
+ if (alreadyKnownIdentity != identity)
+ return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
+ } catch ( ... ) { // identity stored in database is not valid or is NULL
+ return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
+ }
+ } else {
+ std::string idstr(identity.toString(false));
+ sqlite3_reset(_sCreateOrReplaceNode);
+ sqlite3_bind_text(_sCreateOrReplaceNode,1,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr.c_str(),-1,SQLITE_STATIC);
+ if (sqlite3_step(_sCreateOrReplaceNode) != SQLITE_DONE) {
+ return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ // Fetch Network record
+
+ sqlite3_reset(_sGetNetworkById);
+ sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC);
+ if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
+ network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0);
+ network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0);
+ network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0);
+ network.allowPassiveBridging = (sqlite3_column_int(_sGetNetworkById,3) > 0);
+ network.flags = sqlite3_column_int(_sGetNetworkById,4);
+ network.multicastLimit = sqlite3_column_int(_sGetNetworkById,5);
+ network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,6);
+ network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7);
+ network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8);
+ } else {
+ return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND;
+ }
+
+ // Fetch Member record
+
+ bool foundMember = false;
+ sqlite3_reset(_sGetMember);
+ sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC);
+ if (sqlite3_step(_sGetMember) == SQLITE_ROW) {
+ foundMember = true;
+ member.rowid = (int64_t)sqlite3_column_int64(_sGetMember,0);
+ member.authorized = (sqlite3_column_int(_sGetMember,1) > 0);
+ member.activeBridge = (sqlite3_column_int(_sGetMember,2) > 0);
+ }
+
+ // Create Member record for unknown nodes, auto-authorizing if network is public
+
+ if (!foundMember) {
+ member.authorized = (network.isPrivate ? false : true);
+ member.activeBridge = false;
+ sqlite3_reset(_sCreateMember);
+ sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0));
+ sqlite3_bind_text(_sCreateMember,4,network.id,16,SQLITE_STATIC);
+ if (sqlite3_step(_sCreateMember) != SQLITE_DONE) {
+ return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
+ }
+ member.rowid = (int64_t)sqlite3_last_insert_rowid(_db);
+
+ sqlite3_reset(_sIncrementMemberRevisionCounter);
+ sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC);
+ sqlite3_step(_sIncrementMemberRevisionCounter);
+ }
+
+ // Update NodeHistory with new log entry and delete expired entries
+
+ {
+ int64_t nextVC = 1;
+ sqlite3_reset(_sGetMaxNodeHistoryNetworkVisitCounter);
+ sqlite3_bind_text(_sGetMaxNodeHistoryNetworkVisitCounter,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sGetMaxNodeHistoryNetworkVisitCounter,2,member.nodeId,10,SQLITE_STATIC);
+ if (sqlite3_step(_sGetMaxNodeHistoryNetworkVisitCounter) == SQLITE_ROW) {
+ nextVC = (int64_t)sqlite3_column_int64(_sGetMaxNodeHistoryNetworkVisitCounter,0) + 1;
+ }
+
+ std::string fastr;
+ if (fromAddr)
+ fastr = fromAddr.toString();
+
+ sqlite3_reset(_sAddNodeHistoryEntry);
+ sqlite3_bind_text(_sAddNodeHistoryEntry,1,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_text(_sAddNodeHistoryEntry,2,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_int64(_sAddNodeHistoryEntry,3,nextVC);
+ sqlite3_bind_int(_sAddNodeHistoryEntry,4,(member.authorized ? 1 : 0));
+ sqlite3_bind_int64(_sAddNodeHistoryEntry,5,(long long)now);
+ sqlite3_bind_int(_sAddNodeHistoryEntry,6,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0));
+ sqlite3_bind_int(_sAddNodeHistoryEntry,7,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0));
+ sqlite3_bind_int(_sAddNodeHistoryEntry,8,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0));
+ sqlite3_bind_text(_sAddNodeHistoryEntry,9,metaData.data(),-1,SQLITE_STATIC);
+ if (fastr.length() > 0)
+ sqlite3_bind_text(_sAddNodeHistoryEntry,10,fastr.c_str(),-1,SQLITE_STATIC);
+ else sqlite3_bind_null(_sAddNodeHistoryEntry,10);
+ sqlite3_step(_sAddNodeHistoryEntry);
+
+ nextVC -= ZT_NETCONF_NODE_HISTORY_LENGTH;
+ if (nextVC >= 0) {
+ sqlite3_reset(_sDeleteOldNodeHistoryEntries);
+ sqlite3_bind_text(_sDeleteOldNodeHistoryEntries,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sDeleteOldNodeHistoryEntries,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int64(_sDeleteOldNodeHistoryEntries,3,nextVC);
+ sqlite3_step(_sDeleteOldNodeHistoryEntries);
+ }
+ }
+
+ // Check member authorization
+
+ if (!member.authorized)
+ return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
+
+ // Create network configuration -- we create both legacy and new types and send both for backward compatibility
+
+ // New network config structure
+ nc.networkId = Utils::hexStrToU64(network.id);
+ nc.type = network.isPrivate ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
+ nc.timestamp = now;
+ nc.revision = network.revision;
+ nc.issuedTo = member.nodeId;
+ if (network.enableBroadcast) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
+ if (network.allowPassiveBridging) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
+ memcpy(nc.name,network.name,std::min((unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH,(unsigned int)strlen(network.name)));
+
+ { // TODO: right now only etherTypes are supported in rules
+ std::vector<int> allowedEtherTypes;
+ sqlite3_reset(_sGetEtherTypesFromRuleTable);
+ sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC);
+ while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) {
+ if (sqlite3_column_type(_sGetEtherTypesFromRuleTable,0) == SQLITE_NULL) {
+ allowedEtherTypes.clear();
+ allowedEtherTypes.push_back(0); // NULL 'allow' matches ANY
+ break;
+ } else {
+ int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0);
+ if ((et >= 0)&&(et <= 0xffff))
+ allowedEtherTypes.push_back(et);
+ }
+ }
+ std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end());
+ allowedEtherTypes.erase(std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end()),allowedEtherTypes.end());
+
+ for(long i=0;i<(long)allowedEtherTypes.size();++i) {
+ if ((nc.ruleCount + 2) > ZT_MAX_NETWORK_RULES)
+ break;
+ if (allowedEtherTypes[i] > 0) {
+ nc.rules[nc.ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
+ nc.rules[nc.ruleCount].v.etherType = (uint16_t)allowedEtherTypes[i];
+ ++nc.ruleCount;
+ }
+ nc.rules[nc.ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
+ }
+ }
+
+ nc.multicastLimit = network.multicastLimit;
+
+ bool amActiveBridge = false;
+ {
+ sqlite3_reset(_sGetActiveBridges);
+ sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC);
+ while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) {
+ const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0);
+ if ((ab)&&(strlen(ab) == 10)) {
+ const uint64_t ab2 = Utils::hexStrToU64(ab);
+ nc.addSpecialist(Address(ab2),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
+ if (!strcmp(member.nodeId,ab))
+ amActiveBridge = true;
+ }
+ }
+ }
+
+ // Do not send relays to 1.1.0 since it had a serious bug in using them
+ // 1.1.0 will still work, it'll just fall back to roots instead of using network preferred relays
+ if (!((metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0) == 0))) {
+ sqlite3_reset(_sGetRelays);
+ sqlite3_bind_text(_sGetRelays,1,network.id,16,SQLITE_STATIC);
+ while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
+ const char *n = (const char *)sqlite3_column_text(_sGetRelays,0);
+ const char *a = (const char *)sqlite3_column_text(_sGetRelays,1);
+ if ((n)&&(a)) {
+ Address node(n);
+ InetAddress addr(a);
+ if (node)
+ nc.addSpecialist(node,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
+ }
+ }
+ }
+
+ sqlite3_reset(_sGetRoutes);
+ sqlite3_bind_text(_sGetRoutes,1,network.id,16,SQLITE_STATIC);
+ while ((sqlite3_step(_sGetRoutes) == SQLITE_ROW)&&(nc.routeCount < ZT_MAX_NETWORK_ROUTES)) {
+ ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]);
+ memset(r,0,sizeof(ZT_VirtualNetworkRoute));
+ switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
+ case 4:
+ *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,0) + 12),4,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
+ break;
+ case 6:
+ *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,0),16,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
+ break;
+ default:
+ continue;
+ }
+ if (sqlite3_column_type(_sGetRoutes,1) != SQLITE_NULL) {
+ switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
+ case 4:
+ *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,1) + 12),4,0);
+ break;
+ case 6:
+ *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,1),16,0);
+ break;
+ default:
+ continue;
+ }
+ }
+ r->flags = (uint16_t)sqlite3_column_int(_sGetRoutes,4);
+ r->metric = (uint16_t)sqlite3_column_int(_sGetRoutes,5);
+ ++nc.routeCount;
+ }
+
+ if (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193) != 0)&&(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 (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE) != 0)&&(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;
+ }
+
+ if ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4) != 0) {
+ bool haveStaticIpAssignment = false;
+
+ sqlite3_reset(_sGetIpAssignmentsForNode);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4
+ while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
+ const unsigned char *const ip = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,1);
+ if ((!ip)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,1) != 16))
+ continue;
+ int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
+ if ((ipNetmaskBits <= 0)||(ipNetmaskBits > 32))
+ continue;
+ if (sqlite3_column_int(_sGetIpAssignmentsForNode,0) == 0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/) {
+ if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
+ v4ip->sin_family = AF_INET;
+ v4ip->sin_port = Utils::hton((uint16_t)ipNetmaskBits);
+ memcpy(&(v4ip->sin_addr.s_addr),ip + 12,4);
+ }
+ haveStaticIpAssignment = true;
+ }
+ }
+
+ if ((!haveStaticIpAssignment)&&(!amActiveBridge)) {
+ // Attempt to auto-assign an IPv4 address from an available routed pool if there is one
+ sqlite3_reset(_sGetIpAssignmentPools);
+ sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4
+
+ while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
+ const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
+ const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
+ if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
+ continue;
+
+ uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12)));
+ uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12)));
+ 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,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) {
+ 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 IPv4 IP is within a local-to-Ethernet routed network
+ int routedNetmaskBits = 0;
+ for(unsigned int rk=0;rk<nc.routeCount;++rk) {
+ if ((!nc.routes[rk].via.ss_family)&&(nc.routes[rk].target.ss_family == AF_INET)) {
+ uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
+ int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(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
+ if (routedNetmaskBits > 0) {
+ uint32_t ipBlob[4]; // actually a 16-byte blob, we put IPv4s in the last 4 bytes
+ ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip);
+ sqlite3_reset(_sCheckIfIpIsAllocated);
+ sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4
+ sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
+ // No rows returned, so the IP is available
+ sqlite3_reset(_sAllocateIp);
+ sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
+ sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
+ sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
+ sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
+ sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4
+ if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
+ if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
+ struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(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);
+ }
+ haveStaticIpAssignment = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ } // end lock
+
+ // Perform signing outside lock to enable concurrency
+ if (network.isPrivate) {
+ CertificateOfMembership com(now,ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA,nwid,identity.address());
+ if (com.sign(signingId)) {
+ nc.com = com;
+ } else {
+ return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
+ }
+ }
+
+ return NetworkController::NETCONF_QUERY_OK;
}
unsigned int SqliteNetworkController::handleControlPlaneHttpGET(
@@ -1619,385 +1991,6 @@ unsigned int SqliteNetworkController::_doCPGet(
return 404;
}
-NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &metaData,NetworkConfig &nc)
-{
- // Assumes _lock is locked
-
- if (((!signingId)||(!signingId.hasPrivate()))||(signingId.address().toInt() != (nwid >> 24))) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
-
- // Note: we can't reuse prepared statements that return const char * pointers without
- // making our own copy in e.g. a std::string first.
-
- //const bool clientIs104 = (Utils::compareVersion(metaData.majorVersion,metaData.minorVersion,metaData.revision,1,0,4) >= 0);
- const uint64_t now = OSUtils::now();
-
- // Check rate limit circuit breaker to prevent flooding
- {
- uint64_t &lrt = _lastRequestTime[std::pair<uint64_t,uint64_t>(identity.address().toInt(),nwid)];
- if ((now - lrt) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
- return NetworkController::NETCONF_QUERY_IGNORE;
- lrt = now;
- }
-
- _backupNeeded = true;
-
- NetworkRecord network;
- memset(&network,0,sizeof(network));
- Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid);
-
- MemberRecord member;
- memset(&member,0,sizeof(member));
- Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt());
-
- // Create Node record or do full identity check if we already have one
-
- sqlite3_reset(_sGetNodeIdentity);
- sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) {
- try {
- Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0));
- if (alreadyKnownIdentity != identity)
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
- } catch ( ... ) { // identity stored in database is not valid or is NULL
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
- }
- } else {
- std::string idstr(identity.toString(false));
- sqlite3_reset(_sCreateOrReplaceNode);
- sqlite3_bind_text(_sCreateOrReplaceNode,1,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr.c_str(),-1,SQLITE_STATIC);
- if (sqlite3_step(_sCreateOrReplaceNode) != SQLITE_DONE) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- }
-
- // Fetch Network record
-
- sqlite3_reset(_sGetNetworkById);
- sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC);
- if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
- network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0);
- network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0);
- network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0);
- network.allowPassiveBridging = (sqlite3_column_int(_sGetNetworkById,3) > 0);
- network.flags = sqlite3_column_int(_sGetNetworkById,4);
- network.multicastLimit = sqlite3_column_int(_sGetNetworkById,5);
- network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,6);
- network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7);
- network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8);
- } else {
- return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND;
- }
-
- // Fetch Member record
-
- bool foundMember = false;
- sqlite3_reset(_sGetMember);
- sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetMember) == SQLITE_ROW) {
- foundMember = true;
- member.rowid = (int64_t)sqlite3_column_int64(_sGetMember,0);
- member.authorized = (sqlite3_column_int(_sGetMember,1) > 0);
- member.activeBridge = (sqlite3_column_int(_sGetMember,2) > 0);
- }
-
- // Create Member record for unknown nodes, auto-authorizing if network is public
-
- if (!foundMember) {
- member.authorized = (network.isPrivate ? false : true);
- member.activeBridge = false;
- sqlite3_reset(_sCreateMember);
- sqlite3_bind_text(_sCreateMember,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sCreateMember,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sCreateMember,3,(member.authorized ? 1 : 0));
- sqlite3_bind_text(_sCreateMember,4,network.id,16,SQLITE_STATIC);
- if (sqlite3_step(_sCreateMember) != SQLITE_DONE) {
- return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- member.rowid = (int64_t)sqlite3_last_insert_rowid(_db);
-
- sqlite3_reset(_sIncrementMemberRevisionCounter);
- sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC);
- sqlite3_step(_sIncrementMemberRevisionCounter);
- }
-
- // Update NodeHistory with new log entry and delete expired entries
-
- {
- int64_t nextVC = 1;
- sqlite3_reset(_sGetMaxNodeHistoryNetworkVisitCounter);
- sqlite3_bind_text(_sGetMaxNodeHistoryNetworkVisitCounter,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetMaxNodeHistoryNetworkVisitCounter,2,member.nodeId,10,SQLITE_STATIC);
- if (sqlite3_step(_sGetMaxNodeHistoryNetworkVisitCounter) == SQLITE_ROW) {
- nextVC = (int64_t)sqlite3_column_int64(_sGetMaxNodeHistoryNetworkVisitCounter,0) + 1;
- }
-
- std::string fastr;
- if (fromAddr)
- fastr = fromAddr.toString();
-
- sqlite3_reset(_sAddNodeHistoryEntry);
- sqlite3_bind_text(_sAddNodeHistoryEntry,1,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_text(_sAddNodeHistoryEntry,2,network.id,16,SQLITE_STATIC);
- sqlite3_bind_int64(_sAddNodeHistoryEntry,3,nextVC);
- sqlite3_bind_int(_sAddNodeHistoryEntry,4,(member.authorized ? 1 : 0));
- sqlite3_bind_int64(_sAddNodeHistoryEntry,5,(long long)now);
- sqlite3_bind_int(_sAddNodeHistoryEntry,6,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0));
- sqlite3_bind_int(_sAddNodeHistoryEntry,7,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0));
- sqlite3_bind_int(_sAddNodeHistoryEntry,8,(int)metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0));
- sqlite3_bind_text(_sAddNodeHistoryEntry,9,metaData.data(),-1,SQLITE_STATIC);
- if (fastr.length() > 0)
- sqlite3_bind_text(_sAddNodeHistoryEntry,10,fastr.c_str(),-1,SQLITE_STATIC);
- else sqlite3_bind_null(_sAddNodeHistoryEntry,10);
- sqlite3_step(_sAddNodeHistoryEntry);
-
- nextVC -= ZT_NETCONF_NODE_HISTORY_LENGTH;
- if (nextVC >= 0) {
- sqlite3_reset(_sDeleteOldNodeHistoryEntries);
- sqlite3_bind_text(_sDeleteOldNodeHistoryEntries,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sDeleteOldNodeHistoryEntries,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int64(_sDeleteOldNodeHistoryEntries,3,nextVC);
- sqlite3_step(_sDeleteOldNodeHistoryEntries);
- }
- }
-
- // Check member authorization
-
- if (!member.authorized)
- return NetworkController::NETCONF_QUERY_ACCESS_DENIED;
-
- // Create network configuration -- we create both legacy and new types and send both for backward compatibility
-
- // New network config structure
- nc.networkId = Utils::hexStrToU64(network.id);
- nc.type = network.isPrivate ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
- nc.timestamp = now;
- nc.revision = network.revision;
- nc.issuedTo = member.nodeId;
- if (network.enableBroadcast) nc.flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
- if (network.allowPassiveBridging) nc.flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
- memcpy(nc.name,network.name,std::min((unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH,(unsigned int)strlen(network.name)));
-
- { // TODO: right now only etherTypes are supported in rules
- std::vector<int> allowedEtherTypes;
- sqlite3_reset(_sGetEtherTypesFromRuleTable);
- sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) {
- if (sqlite3_column_type(_sGetEtherTypesFromRuleTable,0) == SQLITE_NULL) {
- allowedEtherTypes.clear();
- allowedEtherTypes.push_back(0); // NULL 'allow' matches ANY
- break;
- } else {
- int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0);
- if ((et >= 0)&&(et <= 0xffff))
- allowedEtherTypes.push_back(et);
- }
- }
- std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end());
- allowedEtherTypes.erase(std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end()),allowedEtherTypes.end());
-
- for(long i=0;i<(long)allowedEtherTypes.size();++i) {
- if ((nc.ruleCount + 2) > ZT_MAX_NETWORK_RULES)
- break;
- if (allowedEtherTypes[i] > 0) {
- nc.rules[nc.ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
- nc.rules[nc.ruleCount].v.etherType = (uint16_t)allowedEtherTypes[i];
- ++nc.ruleCount;
- }
- nc.rules[nc.ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
- }
- }
-
- nc.multicastLimit = network.multicastLimit;
-
- bool amActiveBridge = false;
- {
- sqlite3_reset(_sGetActiveBridges);
- sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) {
- const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0);
- if ((ab)&&(strlen(ab) == 10)) {
- const uint64_t ab2 = Utils::hexStrToU64(ab);
- nc.addSpecialist(Address(ab2),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
- if (!strcmp(member.nodeId,ab))
- amActiveBridge = true;
- }
- }
- }
-
- // Do not send relays to 1.1.0 since it had a serious bug in using them
- // 1.1.0 will still work, it'll just fall back to roots instead of using network preferred relays
- if (!((metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0) == 1)&&(metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0) == 0))) {
- sqlite3_reset(_sGetRelays);
- sqlite3_bind_text(_sGetRelays,1,network.id,16,SQLITE_STATIC);
- while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
- const char *n = (const char *)sqlite3_column_text(_sGetRelays,0);
- const char *a = (const char *)sqlite3_column_text(_sGetRelays,1);
- if ((n)&&(a)) {
- Address node(n);
- InetAddress addr(a);
- if (node)
- nc.addSpecialist(node,ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY);
- }
- }
- }
-
- sqlite3_reset(_sGetRoutes);
- sqlite3_bind_text(_sGetRoutes,1,network.id,16,SQLITE_STATIC);
- while ((sqlite3_step(_sGetRoutes) == SQLITE_ROW)&&(nc.routeCount < ZT_MAX_NETWORK_ROUTES)) {
- ZT_VirtualNetworkRoute *r = &(nc.routes[nc.routeCount]);
- memset(r,0,sizeof(ZT_VirtualNetworkRoute));
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,0) + 12),4,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
- break;
- case 6:
- *(reinterpret_cast<InetAddress *>(&(r->target))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,0),16,(unsigned int)sqlite3_column_int(_sGetRoutes,2));
- break;
- default:
- continue;
- }
- if (sqlite3_column_type(_sGetRoutes,1) != SQLITE_NULL) {
- switch(sqlite3_column_int(_sGetRoutes,3)) { // ipVersion
- case 4:
- *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)((const char *)sqlite3_column_blob(_sGetRoutes,1) + 12),4,0);
- break;
- case 6:
- *(reinterpret_cast<InetAddress *>(&(r->via))) = InetAddress((const void *)sqlite3_column_blob(_sGetRoutes,1),16,0);
- break;
- default:
- continue;
- }
- }
- r->flags = (uint16_t)sqlite3_column_int(_sGetRoutes,4);
- r->metric = (uint16_t)sqlite3_column_int(_sGetRoutes,5);
- ++nc.routeCount;
- }
-
- if (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_RFC4193) != 0)&&(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 (((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V6_6PLANE) != 0)&&(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;
- }
-
- if ((network.flags & ZT_DB_NETWORK_FLAG_ZT_MANAGED_V4) != 0) {
- bool haveStaticIpAssignment = false;
-
- sqlite3_reset(_sGetIpAssignmentsForNode);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4
- while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
- const unsigned char *const ip = (const unsigned char *)sqlite3_column_blob(_sGetIpAssignmentsForNode,1);
- if ((!ip)||(sqlite3_column_bytes(_sGetIpAssignmentsForNode,1) != 16))
- continue;
- int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,2);
- if ((ipNetmaskBits <= 0)||(ipNetmaskBits > 32))
- continue;
- if (sqlite3_column_int(_sGetIpAssignmentsForNode,0) == 0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(nc.staticIps[nc.staticIpCount++]));
- v4ip->sin_family = AF_INET;
- v4ip->sin_port = Utils::hton((uint16_t)ipNetmaskBits);
- memcpy(&(v4ip->sin_addr.s_addr),ip + 12,4);
- }
- haveStaticIpAssignment = true;
- }
- }
-
- if ((!haveStaticIpAssignment)&&(!amActiveBridge)) {
- // Attempt to auto-assign an IPv4 address from an available routed pool if there is one
- sqlite3_reset(_sGetIpAssignmentPools);
- sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4
-
- while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) {
- const unsigned char *ipRangeStartB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,0));
- const unsigned char *ipRangeEndB = reinterpret_cast<const unsigned char *>(sqlite3_column_blob(_sGetIpAssignmentPools,1));
- if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16))
- continue;
-
- uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeStartB + 12)));
- uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast<const uint32_t *>(ipRangeEndB + 12)));
- 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,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) {
- 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 IPv4 IP is within a local-to-Ethernet routed network
- int routedNetmaskBits = 0;
- for(unsigned int rk=0;rk<nc.routeCount;++rk) {
- if ((!nc.routes[rk].via.ss_family)&&(nc.routes[rk].target.ss_family == AF_INET)) {
- uint32_t targetIp = Utils::ntoh((uint32_t)(reinterpret_cast<const struct sockaddr_in *>(&(nc.routes[rk].target))->sin_addr.s_addr));
- int targetBits = Utils::ntoh((uint16_t)(reinterpret_cast<const struct sockaddr_in *>(&(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
- if (routedNetmaskBits > 0) {
- uint32_t ipBlob[4]; // actually a 16-byte blob, we put IPv4s in the last 4 bytes
- ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip);
- sqlite3_reset(_sCheckIfIpIsAllocated);
- sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4
- sqlite3_bind_int(_sCheckIfIpIsAllocated,4,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- if (sqlite3_step(_sCheckIfIpIsAllocated) != SQLITE_ROW) {
- // No rows returned, so the IP is available
- sqlite3_reset(_sAllocateIp);
- sqlite3_bind_text(_sAllocateIp,1,network.id,16,SQLITE_STATIC);
- sqlite3_bind_text(_sAllocateIp,2,member.nodeId,10,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,3,(int)0 /*ZT_IP_ASSIGNMENT_TYPE_ADDRESS*/);
- sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC);
- sqlite3_bind_int(_sAllocateIp,5,routedNetmaskBits); // IP netmask bits from matching route
- sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4
- if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) {
- if (nc.staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES) {
- struct sockaddr_in *const v4ip = reinterpret_cast<struct sockaddr_in *>(&(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);
- }
- haveStaticIpAssignment = true;
- break;
- }
- }
- }
- }
- }
- }
- }
-
- if (network.isPrivate) {
- CertificateOfMembership com(now,ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA,nwid,identity.address());
- if (com.sign(signingId)) {
- nc.com = com;
- } else {
- return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
- }
- }
-
- return NetworkController::NETCONF_QUERY_OK;
-}
-
void SqliteNetworkController::_circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report)
{
char tmp[65535];