From 0d386f1c3119b869197bafd5f2ad75ba2179850f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 8 Sep 2015 11:35:55 -0700 Subject: Add a bit of useful testing instrumentation to SqliteNetworkController. --- controller/SqliteNetworkController.cpp | 43 +++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 2a004bda..6a786ce4 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -301,7 +301,7 @@ SqliteNetworkController::~SqliteNetworkController() } } -NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) +NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,Dictionary &netconf) { // Decode some stuff from metaData const unsigned int clientMajorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); @@ -1355,8 +1355,44 @@ unsigned int SqliteNetworkController::_doCPGet( sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC); sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC); if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { + const char *memberIdStr = (const char *)sqlite3_column_text(_sGetMember2,3); + + // If testSingingId is included in the URL or X-ZT1-TestSigningId in the headers + // and if it contains an identity with a secret portion, the resturned JSON + // will contain an extra field called _testConf. This will contain several + // fields that report the result of doNetworkConfigRequest() for this member. + std::string testFields; + { + Identity testOutputSigningId; + std::map::const_iterator sid(urlArgs.find("testSigningId")); + if (sid != urlArgs.end()) { + testOutputSigningId.fromString(sid->second.c_str()); + } else { + sid = headers.find("x-zt1-testsigningid"); + if (sid != headers.end()) + testOutputSigningId.fromString(sid->second.c_str()); + } + + if ((testOutputSigningId.hasPrivate())&&(memberIdStr)) { + Dictionary testNetconf; + NetworkController::ResultCode rc = this->doNetworkConfigRequest( + InetAddress(), + testOutputSigningId, + Identity(memberIdStr), + nwid, + Dictionary(), // TODO: allow passing of meta-data for testing + testNetconf); + char rcs[16]; + Utils::snprintf(rcs,sizeof(rcs),"%d",(int)rc); + testFields.append("\t\"_test\": {\n"); + testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); testFields.append(",\n"); + testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\""); + testFields.append("\t}\n"); + } + } + Utils::snprintf(json,sizeof(json), - "{\n" + "{\n%s" "\t\"nwid\": \"%s\",\n" "\t\"address\": \"%s\",\n" "\t\"controllerInstanceId\": \"%s\",\n" @@ -1366,6 +1402,7 @@ unsigned int SqliteNetworkController::_doCPGet( "\t\"clock\": %llu,\n" "\t\"identity\": \"%s\",\n" "\t\"ipAssignments\": [", + testFields.c_str(), nwids, addrs, _instanceId.c_str(), @@ -1373,7 +1410,7 @@ unsigned int SqliteNetworkController::_doCPGet( (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", (unsigned long long)sqlite3_column_int64(_sGetMember2,2), (unsigned long long)OSUtils::now(), - _jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str()); + _jsonEscape(memberIdStr).c_str()); responseBody = json; sqlite3_reset(_sGetIpAssignmentsForNode2); -- cgit v1.2.3 From 4fbcad246850d7bf00289b898f4a26065276d6e2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 8 Sep 2015 13:02:42 -0700 Subject: Allow identity to be populated for newly inserted Member objects to permit transfer from old network controller and testing. --- controller/SqliteNetworkController.cpp | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 6a786ce4..e0567fed 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -166,7 +166,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : /* Node */ ||(sqlite3_prepare_v2(_db,"SELECT identity FROM Node WHERE id = ?",-1,&_sGetNodeIdentity,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"INSERT INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK) /* Rule */ ||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK) @@ -870,6 +870,27 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( } addToNetworkRevision = 1; } + } else if (!strcmp(j->u.object.values[k].name,"identity")) { + // Identity is technically an immutable field, but if the member's Node has + // no identity we allow it to be populated. This is primarily for migrating + // node data from another controller. + json_value *idstr = j->u.object.values[k].value; + if (idstr->type == json_string) { + sqlite3_reset(_sGetNodeIdentity); + sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC); + if ((sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW)&&(!sqlite3_column_text(_sGetNodeIdentity,0))) { + try { + Identity id2(idstr->u.string.ptr); + if (id2) { + std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC + sqlite3_reset(_sCreateNode); + sqlite3_bind_text(_sCreateNode,1,addrs,10,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNode,2,idstr2.c_str(),-1,SQLITE_STATIC); + sqlite3_step(_sCreateNode); + } + } catch ( ... ) {} // ignore invalid identities + } + } } } @@ -1383,9 +1404,9 @@ unsigned int SqliteNetworkController::_doCPGet( Dictionary(), // TODO: allow passing of meta-data for testing testNetconf); char rcs[16]; - Utils::snprintf(rcs,sizeof(rcs),"%d",(int)rc); + Utils::snprintf(rcs,sizeof(rcs),"%d,\n",(int)rc); testFields.append("\t\"_test\": {\n"); - testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); testFields.append(",\n"); + testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\""); testFields.append("\t}\n"); } -- cgit v1.2.3 From 1f7a41cff886bb97cab8ea43a1a1ca8c0a45873c Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Sep 2015 14:37:34 -0700 Subject: Fix to allowing identity to be populated if not present. --- controller/SqliteNetworkController.cpp | 30 +++++++++++++++++++----------- controller/SqliteNetworkController.hpp | 2 +- 2 files changed, 20 insertions(+), 12 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index e0567fed..c4a5fcc4 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -166,7 +166,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : /* Node */ ||(sqlite3_prepare_v2(_db,"SELECT identity FROM Node WHERE id = ?",-1,&_sGetNodeIdentity,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO Node (id,identity) VALUES (?,?)",-1,&_sCreateOrReplaceNode,(const char **)0) != SQLITE_OK) /* Rule */ ||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK) @@ -256,7 +256,7 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sGetMember); sqlite3_finalize(_sCreateMember); sqlite3_finalize(_sGetNodeIdentity); - sqlite3_finalize(_sCreateNode); + sqlite3_finalize(_sCreateOrReplaceNode); sqlite3_finalize(_sUpdateNode); sqlite3_finalize(_sUpdateNode2); sqlite3_finalize(_sGetEtherTypesFromRuleTable); @@ -354,10 +354,10 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co } } else { std::string idstr(identity.toString(false)); - sqlite3_reset(_sCreateNode); - sqlite3_bind_text(_sCreateNode,1,member.nodeId,10,SQLITE_STATIC); - sqlite3_bind_text(_sCreateNode,2,idstr.c_str(),-1,SQLITE_STATIC); - if (sqlite3_step(_sCreateNode) != SQLITE_DONE) { + 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) { netconf["error"] = "unable to create new Node record"; return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; } @@ -876,17 +876,25 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( // node data from another controller. json_value *idstr = j->u.object.values[k].value; if (idstr->type == json_string) { + bool alreadyHaveIdentity = false; + sqlite3_reset(_sGetNodeIdentity); sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC); - if ((sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW)&&(!sqlite3_column_text(_sGetNodeIdentity,0))) { + if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { + const char *tmp2 = (const char *)sqlite3_column_text(_sGetNodeIdentity,0); + if ((tmp2)&&(tmp2[0])) + alreadyHaveIdentity = true; + } + + if (!alreadyHaveIdentity) { try { Identity id2(idstr->u.string.ptr); if (id2) { std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC - sqlite3_reset(_sCreateNode); - sqlite3_bind_text(_sCreateNode,1,addrs,10,SQLITE_STATIC); - sqlite3_bind_text(_sCreateNode,2,idstr2.c_str(),-1,SQLITE_STATIC); - sqlite3_step(_sCreateNode); + sqlite3_reset(_sCreateOrReplaceNode); + sqlite3_bind_text(_sCreateOrReplaceNode,1,addrs,10,SQLITE_STATIC); + sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr2.c_str(),-1,SQLITE_STATIC); + sqlite3_step(_sCreateOrReplaceNode); } } catch ( ... ) {} // ignore invalid identities } diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 26a06968..ce4a512c 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -105,7 +105,7 @@ private: sqlite3_stmt *_sGetMember; sqlite3_stmt *_sCreateMember; sqlite3_stmt *_sGetNodeIdentity; - sqlite3_stmt *_sCreateNode; + sqlite3_stmt *_sCreateOrReplaceNode; sqlite3_stmt *_sUpdateNode; sqlite3_stmt *_sUpdateNode2; sqlite3_stmt *_sGetEtherTypesFromRuleTable; -- cgit v1.2.3 From bebe3d7cfa8eef282513448848f2d95caf73eecc Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Sep 2015 14:47:04 -0700 Subject: Fix deadlock in test mode. --- controller/SqliteNetworkController.cpp | 2656 ++++++++++++++++---------------- controller/SqliteNetworkController.hpp | 7 + 2 files changed, 1338 insertions(+), 1325 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index c4a5fcc4..0905c99f 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -303,431 +303,580 @@ SqliteNetworkController::~SqliteNetworkController() NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,Dictionary &netconf) { - // Decode some stuff from metaData - const unsigned int clientMajorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); - const unsigned int clientMinorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); - const unsigned int clientRevision = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); - const bool clientIs104 = (Utils::compareVersion(clientMajorVersion,clientMinorVersion,clientRevision,1,0,4) >= 0); + Mutex::Lock _l(_lock); + return _doNetworkConfigRequest(fromAddr,signingId,identity,nwid,metaData,netconf); +} +unsigned int SqliteNetworkController::handleControlPlaneHttpGET( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ Mutex::Lock _l(_lock); + return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType); +} - // Note: we can't reuse prepared statements that return const char * pointers without - // making our own copy in e.g. a std::string first. +unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + if (path.empty()) + return 404; + Mutex::Lock _l(_lock); - if ((!signingId)||(!signingId.hasPrivate())) { - netconf["error"] = "signing identity invalid or lacks private key"; - return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } - if (signingId.address().toInt() != (nwid >> 24)) { - netconf["error"] = "signing identity address does not match most significant 40 bits of network ID"; - return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } + if (path[0] == "network") { - // Check rate limit + if ((path.size() >= 2)&&(path[1].length() == 16)) { + uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + char nwids[24]; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - { - uint64_t &lrt = _lastRequestTime[std::pair(identity.address(),nwid)]; - uint64_t lrt2 = lrt; - if (((lrt = OSUtils::now()) - lrt2) <= ZT_NETCONF_MIN_REQUEST_PERIOD) - return NetworkController::NETCONF_QUERY_IGNORE; - } + int64_t revision = 0; + sqlite3_reset(_sGetNetworkRevision); + sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC); + bool networkExists = false; + if (sqlite3_step(_sGetNetworkRevision) == SQLITE_ROW) { + networkExists = true; + revision = sqlite3_column_int64(_sGetNetworkRevision,0); + } - NetworkRecord network; - memset(&network,0,sizeof(network)); - Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); + if (path.size() >= 3) { - MemberRecord member; - memset(&member,0,sizeof(member)); - Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); + if (!networkExists) + return 404; - // Create Node record or do full identity check if we already have one + if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { + uint64_t address = Utils::hexStrToU64(path[3].c_str()); + char addrs[24]; + Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); - 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) { - netconf["error"] = "unable to create new Node record"; - return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } - } + int64_t addToNetworkRevision = 0; - // Fetch Network record + int64_t memberRowId = 0; + sqlite3_reset(_sGetMember); + sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC); + bool memberExists = false; + if (sqlite3_step(_sGetMember) == SQLITE_ROW) { + memberExists = true; + memberRowId = sqlite3_column_int64(_sGetMember,0); + } - 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.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4); - network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5); - network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6); - network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); - network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8); - network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,9); - } else { - return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; - } + if (!memberExists) { + sqlite3_reset(_sCreateMember); + sqlite3_bind_text(_sCreateMember,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sCreateMember,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sCreateMember,3,0); + sqlite3_bind_text(_sCreateMember,4,nwids,16,SQLITE_STATIC); + if (sqlite3_step(_sCreateMember) != SQLITE_DONE) + return 500; + memberRowId = (int64_t)sqlite3_last_insert_rowid(_db); - // Fetch Member record + sqlite3_reset(_sIncrementMemberRevisionCounter); + sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sIncrementMemberRevisionCounter); + addToNetworkRevision = 1; + } - 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); - } + json_value *j = json_parse(body.c_str(),body.length()); + if (j) { + if (j->type == json_object) { + for(unsigned int k=0;ku.object.length;++k) { - // Create Member record for unknown nodes, auto-authorizing if network is public + if (!strcmp(j->u.object.values[k].name,"authorized")) { + if (j->u.object.values[k].value->type == json_boolean) { + sqlite3_reset(_sUpdateMemberAuthorized); + sqlite3_bind_int(_sUpdateMemberAuthorized,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); + sqlite3_bind_text(_sUpdateMemberAuthorized,2,nwids,16,SQLITE_STATIC); + sqlite3_bind_int64(_sUpdateMemberAuthorized,3,memberRowId); + if (sqlite3_step(_sUpdateMemberAuthorized) != SQLITE_DONE) + return 500; - 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) { - netconf["error"] = "unable to create new member record"; - return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } - member.rowid = (int64_t)sqlite3_last_insert_rowid(_db); + sqlite3_reset(_sIncrementMemberRevisionCounter); + sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sIncrementMemberRevisionCounter); + addToNetworkRevision = 1; + } + } else if (!strcmp(j->u.object.values[k].name,"activeBridge")) { + if (j->u.object.values[k].value->type == json_boolean) { + sqlite3_reset(_sUpdateMemberActiveBridge); + sqlite3_bind_int(_sUpdateMemberActiveBridge,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); + sqlite3_bind_text(_sUpdateMemberActiveBridge,2,nwids,16,SQLITE_STATIC); + sqlite3_bind_int64(_sUpdateMemberActiveBridge,3,memberRowId); + if (sqlite3_step(_sUpdateMemberActiveBridge) != SQLITE_DONE) + return 500; - sqlite3_reset(_sIncrementMemberRevisionCounter); - sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC); - sqlite3_step(_sIncrementMemberRevisionCounter); - } + sqlite3_reset(_sIncrementMemberRevisionCounter); + sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sIncrementMemberRevisionCounter); + addToNetworkRevision = 1; + } + } else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) { + if (j->u.object.values[k].value->type == json_array) { + sqlite3_reset(_sDeleteIpAllocations); + sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sDeleteIpAllocations,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + if (sqlite3_step(_sDeleteIpAllocations) != SQLITE_DONE) + return 500; + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *ipalloc = j->u.object.values[k].value->u.array.values[kk]; + if (ipalloc->type == json_string) { + InetAddress a(ipalloc->u.string.ptr); + char ipBlob[16]; + int ipVersion = 0; + switch(a.ss_family) { + case AF_INET: + if ((a.netmaskBits() > 0)&&(a.netmaskBits() <= 32)) { + memset(ipBlob,0,12); + memcpy(ipBlob + 12,a.rawIpData(),4); + ipVersion = 4; + } + break; + case AF_INET6: + if ((a.netmaskBits() > 0)&&(a.netmaskBits() <= 128)) { + memcpy(ipBlob,a.rawIpData(),16); + ipVersion = 6; + } + break; + } + if (ipVersion > 0) { + sqlite3_reset(_sAllocateIp); + sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sAllocateIp,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,5,(int)a.netmaskBits()); + sqlite3_bind_int(_sAllocateIp,6,ipVersion); + if (sqlite3_step(_sAllocateIp) != SQLITE_DONE) + return 500; + } + } + } + addToNetworkRevision = 1; + } + } else if (!strcmp(j->u.object.values[k].name,"identity")) { + // Identity is technically an immutable field, but if the member's Node has + // no identity we allow it to be populated. This is primarily for migrating + // node data from another controller. + json_value *idstr = j->u.object.values[k].value; + if (idstr->type == json_string) { + bool alreadyHaveIdentity = false; - // Add log entry - { - char ver[16]; - std::string fa; - if (fromAddr) { - fa = fromAddr.toString(); - if (fa.length() > 64) - fa = fa.substr(0,64); - } - sqlite3_reset(_sPutLog); - sqlite3_bind_text(_sPutLog,1,network.id,16,SQLITE_STATIC); - sqlite3_bind_text(_sPutLog,2,member.nodeId,10,SQLITE_STATIC); - sqlite3_bind_int64(_sPutLog,3,(long long)OSUtils::now()); - sqlite3_bind_int(_sPutLog,4,member.authorized ? 1 : 0); - if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) { - Utils::snprintf(ver,sizeof(ver),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); - sqlite3_bind_text(_sPutLog,5,ver,-1,SQLITE_STATIC); - } else sqlite3_bind_null(_sPutLog,5); - if (fa.length() > 0) - sqlite3_bind_text(_sPutLog,6,fa.c_str(),-1,SQLITE_STATIC); - else sqlite3_bind_null(_sPutLog,6); - sqlite3_step(_sPutLog); - } - - // Check member authorization + sqlite3_reset(_sGetNodeIdentity); + sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC); + if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { + const char *tmp2 = (const char *)sqlite3_column_text(_sGetNodeIdentity,0); + if ((tmp2)&&(tmp2[0])) + alreadyHaveIdentity = true; + } - if (!member.authorized) - return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + if (!alreadyHaveIdentity) { + try { + Identity id2(idstr->u.string.ptr); + if (id2) { + std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC + sqlite3_reset(_sCreateOrReplaceNode); + sqlite3_bind_text(_sCreateOrReplaceNode,1,addrs,10,SQLITE_STATIC); + sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr2.c_str(),-1,SQLITE_STATIC); + sqlite3_step(_sCreateOrReplaceNode); + } + } catch ( ... ) {} // ignore invalid identities + } + } + } - // Create and sign netconf + } + } + json_value_free(j); + } - netconf.clear(); - { - char tss[24],rs[24]; - Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)OSUtils::now()); - Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision); - netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; - netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs; - netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = network.id; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = member.nodeId; - netconf[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = network.isPrivate ? "1" : "0"; - netconf[ZT_NETWORKCONFIG_DICT_KEY_NAME] = (network.name) ? network.name : ""; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = network.enableBroadcast ? "1" : "0"; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = network.allowPassiveBridging ? "1" : "0"; + if ((addToNetworkRevision > 0)&&(revision > 0)) { + sqlite3_reset(_sSetNetworkRevision); + sqlite3_bind_int64(_sSetNetworkRevision,1,revision + addToNetworkRevision); + sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC); + sqlite3_step(_sSetNetworkRevision); + } - { // TODO: right now only etherTypes are supported in rules - std::vector 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()); - std::string allowedEtherTypesCsv; - for(std::vector::const_iterator i(allowedEtherTypes.begin());i!=allowedEtherTypes.end();++i) { - if (allowedEtherTypesCsv.length()) - allowedEtherTypesCsv.push_back(','); - char tmp[16]; - Utils::snprintf(tmp,sizeof(tmp),"%.4x",(unsigned int)*i); - allowedEtherTypesCsv.append(tmp); - } - netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv; - } + return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType); + } // else 404 - if (network.multicastLimit > 0) { - char ml[16]; - Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit); - netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml; - } + } else { + std::vector path_copy(path); - { - std::string activeBridges; - 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)) { - if (activeBridges.length()) - activeBridges.push_back(','); - activeBridges.append(ab); - } - if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment - break; - } - if (activeBridges.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; - } + if (!networkExists) { + if (path[1].substr(10) == "______") { + // A special POST /network/##########______ feature lets users create a network + // with an arbitrary unused network number at this controller. + nwid = 0; - { - std::string relays; - 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)&&(addr)) { - if (relays.length()) - relays.push_back(','); - relays.append(node.toString()); - relays.push_back(';'); - relays.append(addr.toString()); - } - } - } - if (relays.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_RELAYS] = relays; - } + uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL; + uint64_t nwidPostfix = 0; + Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix)); + uint64_t nwidOriginalPostfix = nwidPostfix; + do { + uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL); + if (!nwidPostfix) + tryNwid |= 1; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)tryNwid); - { - char tmp[128]; - std::string gateways; - sqlite3_reset(_sGetGateways); - sqlite3_bind_text(_sGetGateways,1,network.id,16,SQLITE_STATIC); - while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { - const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); - switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion - case 4: - Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d", - (gateways.length() > 0) ? "," : "", - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetGateways,2)); // metric - gateways.append(tmp); - break; - case 6: - Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d", - (gateways.length() > 0) ? "," : "", - (int)ip[0], - (int)ip[1], - (int)ip[2], - (int)ip[3], - (int)ip[4], - (int)ip[5], - (int)ip[6], - (int)ip[7], - (int)ip[8], - (int)ip[9], - (int)ip[10], - (int)ip[11], - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetGateways,2)); // metric - gateways.append(tmp); - break; - } - } - if (gateways.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS] = gateways; - } + sqlite3_reset(_sGetNetworkRevision); + sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC); + if (sqlite3_step(_sGetNetworkRevision) != SQLITE_ROW) { + nwid = tryNwid; + break; + } - if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { - std::string v4s; + ++nwidPostfix; + } while (nwidPostfix != nwidOriginalPostfix); - // Get existing IPv4 IP assignments and network routes -- keep routes in a - // vector for use in auto-assign if we need them. - std::vector< std::pair > routedNetworks; - 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 *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; + // 503 means we have no more free IDs for this prefix. You shouldn't host anywhere + // near 16 million networks on the same controller, so shouldn't happen. + if (!nwid) + return 503; + } - const IpAssignmentType ipt = (IpAssignmentType)sqlite3_column_int(_sGetIpAssignmentsForNode,0); - switch(ipt) { - case ZT_IP_ASSIGNMENT_TYPE_ADDRESS: - haveStaticIpAssignment = true; - break; - case ZT_IP_ASSIGNMENT_TYPE_NETWORK: - routedNetworks.push_back(std::pair(Utils::ntoh(*(reinterpret_cast(ip + 12))),ipNetmaskBits)); - break; - default: - continue; + sqlite3_reset(_sCreateNetwork); + sqlite3_bind_text(_sCreateNetwork,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNetwork,2,"",0,SQLITE_STATIC); + sqlite3_bind_int64(_sCreateNetwork,3,(long long)OSUtils::now()); + if (sqlite3_step(_sCreateNetwork) != SQLITE_DONE) + return 500; + path_copy[1].assign(nwids); } - // 1.0.4 or newer clients support network routes in addition to IPs. - // Older clients only support IP address / netmask entries. - if ((clientIs104)||(ipt == ZT_IP_ASSIGNMENT_TYPE_ADDRESS)) { - char tmp[32]; - Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],ipNetmaskBits); - if (v4s.length()) - v4s.push_back(','); - v4s.append(tmp); - } - } + json_value *j = json_parse(body.c_str(),body.length()); + if (j) { + if (j->type == json_object) { + for(unsigned int k=0;ku.object.length;++k) { + sqlite3_stmt *stmt = (sqlite3_stmt *)0; - if (!haveStaticIpAssignment) { - // Attempt to auto-assign an IPv4 address from an available routed pool - 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(sqlite3_column_blob(_sGetIpAssignmentPools,0)); - const unsigned char *ipRangeEndB = reinterpret_cast(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(ipRangeStartB + 12))); - uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast(ipRangeEndB + 12))); - if (ipRangeEnd < ipRangeStart) - continue; - uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - - // Start with the LSB of the member's address - uint32_t ipTrialCounter = (uint32_t)(identity.address().toInt() & 0xffffffff); + if (!strcmp(j->u.object.values[k].name,"name")) { + if ((j->u.object.values[k].value->type == json_string)&&(j->u.object.values[k].value->u.string.ptr[0])) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"name\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); + } + } else if (!strcmp(j->u.object.values[k].name,"private")) { + if (j->u.object.values[k].value->type == json_boolean) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"private\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); + } + } else if (!strcmp(j->u.object.values[k].name,"enableBroadcast")) { + if (j->u.object.values[k].value->type == json_boolean) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET enableBroadcast = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); + } + } else if (!strcmp(j->u.object.values[k].name,"allowPassiveBridging")) { + if (j->u.object.values[k].value->type == json_boolean) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET allowPassiveBridging = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); + } + } else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) { + if (j->u.object.values[k].value->type == json_string) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET v4AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); + } + } else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) { + if (j->u.object.values[k].value->type == json_string) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET v6AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); + } + } else if (!strcmp(j->u.object.values[k].name,"multicastLimit")) { + if (j->u.object.values[k].value->type == json_integer) { + if (sqlite3_prepare_v2(_db,"UPDATE Network SET multicastLimit = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) + sqlite3_bind_int(stmt,1,(int)j->u.object.values[k].value->u.integer); + } + } else if (!strcmp(j->u.object.values[k].name,"relays")) { + if (j->u.object.values[k].value->type == json_array) { + std::map nodeIdToPhyAddress; + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *relay = j->u.object.values[k].value->u.array.values[kk]; + const char *address = (const char *)0; + const char *phyAddress = (const char *)0; + if ((relay)&&(relay->type == json_object)) { + for(unsigned int rk=0;rku.object.length;++rk) { + if ((!strcmp(relay->u.object.values[rk].name,"address"))&&(relay->u.object.values[rk].value->type == json_string)) + address = relay->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(relay->u.object.values[rk].name,"phyAddress"))&&(relay->u.object.values[rk].value->type == json_string)) + phyAddress = relay->u.object.values[rk].value->u.string.ptr; + } + } + if ((address)&&(phyAddress)) + nodeIdToPhyAddress[Address(address)] = InetAddress(phyAddress); + } - for(uint32_t k=ipRangeStart,l=0;(k<=ipRangeEnd)&&(l < 1000000);++k,++l) { - uint32_t ip = (ipRangeLen > 0) ? (ipRangeStart + (ipTrialCounter % ipRangeLen)) : ipRangeStart; - ++ipTrialCounter; + sqlite3_reset(_sDeleteRelaysForNetwork); + sqlite3_bind_text(_sDeleteRelaysForNetwork,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteRelaysForNetwork); - for(std::vector< std::pair >::const_iterator r(routedNetworks.begin());r!=routedNetworks.end();++r) { - if ((ip & (0xffffffff << (32 - r->second))) == r->first) { - // IP is included in a routed network, so check if it's allocated + for(std::map::iterator rl(nodeIdToPhyAddress.begin());rl!=nodeIdToPhyAddress.end();++rl) { + sqlite3_reset(_sCreateRelay); + sqlite3_bind_text(_sCreateRelay,1,nwids,16,SQLITE_STATIC); + std::string a(rl->first.toString()),b(rl->second.toString()); // don't destroy strings until sqlite3_step() + sqlite3_bind_text(_sCreateRelay,2,a.c_str(),-1,SQLITE_STATIC); + sqlite3_bind_text(_sCreateRelay,3,b.c_str(),-1,SQLITE_STATIC); + sqlite3_step(_sCreateRelay); + } + } + } else if (!strcmp(j->u.object.values[k].name,"gateways")) { + sqlite3_reset(_sDeleteGateways); + sqlite3_bind_text(_sDeleteGateways,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteGateways); + if (j->u.object.values[k].value->type == json_array) { + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *gateway = j->u.object.values[k].value->u.array.values[kk]; + if ((gateway)&&(gateway->type == json_string)) { + InetAddress gwip(gateway->u.string.ptr); + sqlite3_reset(_sCreateGateway); + sqlite3_bind_text(_sCreateGateway,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_int(_sCreateGateway,4,(int)gwip.metric()); + if (gwip.ss_family == AF_INET) { + char ipBlob[16]; + memset(ipBlob,0,12); + memcpy(ipBlob + 12,gwip.rawIpData(),4); + sqlite3_bind_blob(_sCreateGateway,2,(const void *)ipBlob,16,SQLITE_STATIC); + sqlite3_bind_int(_sCreateGateway,3,4); + sqlite3_step(_sCreateGateway); + } else if (gwip.ss_family == AF_INET6) { + sqlite3_bind_blob(_sCreateGateway,2,gwip.rawIpData(),16,SQLITE_STATIC); + sqlite3_bind_int(_sCreateGateway,3,6); + sqlite3_step(_sCreateGateway); + } + } + } + } + } else if (!strcmp(j->u.object.values[k].name,"ipLocalRoutes")) { + sqlite3_reset(_sDeleteLocalRoutes); + sqlite3_bind_text(_sDeleteLocalRoutes,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_int(_sDeleteLocalRoutes,2,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); + sqlite3_step(_sDeleteLocalRoutes); + if (j->u.object.values[k].value->type == json_array) { + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *localRoute = j->u.object.values[k].value->u.array.values[kk]; + if ((localRoute)&&(localRoute->type == json_string)) { + InetAddress lr(localRoute->u.string.ptr); + if (lr.ss_family == AF_INET) { + char ipBlob[16]; + memset(ipBlob,0,12); + memcpy(ipBlob + 12,lr.rawIpData(),4); + sqlite3_reset(_sAllocateIp); + sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_null(_sAllocateIp,2); + sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); + sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,5,lr.netmaskBits()); + sqlite3_bind_int(_sAllocateIp,6,4); + sqlite3_step(_sAllocateIp); + } else if (lr.ss_family == AF_INET6) { + sqlite3_reset(_sAllocateIp); + sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_null(_sAllocateIp,2); + sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); + sqlite3_bind_blob(_sAllocateIp,4,lr.rawIpData(),16,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,5,lr.netmaskBits()); + sqlite3_bind_int(_sAllocateIp,6,6); + sqlite3_step(_sAllocateIp); + } + } + } + } + } else if (!strcmp(j->u.object.values[k].name,"ipAssignmentPools")) { + if (j->u.object.values[k].value->type == json_array) { + std::vector< std::pair > pools; + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *pool = j->u.object.values[k].value->u.array.values[kk]; + const char *iprs = (const char *)0; + const char *ipre = (const char *)0; + if ((pool)&&(pool->type == json_object)) { + for(unsigned int rk=0;rku.object.length;++rk) { + if ((!strcmp(pool->u.object.values[rk].name,"ipRangeStart"))&&(pool->u.object.values[rk].value->type == json_string)) + iprs = pool->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(pool->u.object.values[rk].name,"ipRangeEnd"))&&(pool->u.object.values[rk].value->type == json_string)) + ipre = pool->u.object.values[rk].value->u.string.ptr; + } + } + if ((iprs)&&(ipre)) { + InetAddress iprs2(iprs); + InetAddress ipre2(ipre); + if (iprs2.ss_family == ipre2.ss_family) { + iprs2.setPort(0); + ipre2.setPort(0); + pools.push_back(std::pair(iprs2,ipre2)); + } + } + } + std::sort(pools.begin(),pools.end()); + pools.erase(std::unique(pools.begin(),pools.end()),pools.end()); - uint32_t ipBlob[4]; - ipBlob[0] = 0; ipBlob[1] = 0; ipBlob[2] = 0; ipBlob[3] = Utils::hton(ip); + sqlite3_reset(_sDeleteIpAssignmentPoolsForNetwork); + sqlite3_bind_text(_sDeleteIpAssignmentPoolsForNetwork,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteIpAssignmentPoolsForNetwork); - 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)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)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); - sqlite3_bind_int(_sAllocateIp,5,r->second); // IP netmask bits from matching route - sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4 - if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) { - char tmp[32]; - Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)((ip >> 24) & 0xff),(int)((ip >> 16) & 0xff),(int)((ip >> 8) & 0xff),(int)(ip & 0xff),r->second); - if (v4s.length()) - v4s.push_back(','); - v4s.append(tmp); - haveStaticIpAssignment = true; // break outer loop + for(std::vector< std::pair >::const_iterator p(pools.begin());p!=pools.end();++p) { + char ipBlob1[16],ipBlob2[16]; + sqlite3_reset(_sCreateIpAssignmentPool); + sqlite3_bind_text(_sCreateIpAssignmentPool,1,nwids,16,SQLITE_STATIC); + if (p->first.ss_family == AF_INET) { + memset(ipBlob1,0,12); + memcpy(ipBlob1 + 12,p->first.rawIpData(),4); + memset(ipBlob2,0,12); + memcpy(ipBlob2 + 12,p->second.rawIpData(),4); + sqlite3_bind_blob(_sCreateIpAssignmentPool,2,(const void *)ipBlob1,16,SQLITE_STATIC); + sqlite3_bind_blob(_sCreateIpAssignmentPool,3,(const void *)ipBlob2,16,SQLITE_STATIC); + sqlite3_bind_int(_sCreateIpAssignmentPool,4,4); + } else if (p->first.ss_family == AF_INET6) { + sqlite3_bind_blob(_sCreateIpAssignmentPool,2,p->first.rawIpData(),16,SQLITE_STATIC); + sqlite3_bind_blob(_sCreateIpAssignmentPool,3,p->second.rawIpData(),16,SQLITE_STATIC); + sqlite3_bind_int(_sCreateIpAssignmentPool,4,6); + } else continue; + sqlite3_step(_sCreateIpAssignmentPool); } } + } else if (!strcmp(j->u.object.values[k].name,"rules")) { + if (j->u.object.values[k].value->type == json_array) { + sqlite3_reset(_sDeleteRulesForNetwork); + sqlite3_bind_text(_sDeleteRulesForNetwork,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteRulesForNetwork); - break; // stop checking routed networks - } - } + for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { + json_value *rj = j->u.object.values[k].value->u.array.values[kk]; + if ((rj)&&(rj->type == json_object)) { + struct { // NULL pointers indicate missing or NULL -- wildcards + const json_int_t *ruleNo; + const char *nodeId; + const char *sourcePort; + const char *destPort; + const json_int_t *vlanId; + const json_int_t *vlanPcp; + const json_int_t *etherType; + const char *macSource; + const char *macDest; + const char *ipSource; + const char *ipDest; + const json_int_t *ipTos; + const json_int_t *ipProtocol; + const json_int_t *ipSourcePort; + const json_int_t *ipDestPort; + const json_int_t *flags; + const json_int_t *invFlags; + const char *action; + } rule; + memset(&rule,0,sizeof(rule)); - if (haveStaticIpAssignment) - break; + for(unsigned int rk=0;rku.object.length;++rk) { + if ((!strcmp(rj->u.object.values[rk].name,"ruleNo"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ruleNo = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"nodeId"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.nodeId = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"sourcePort"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.sourcePort = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"destPort"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.destPort = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"vlanId"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.vlanId = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"vlanPcp"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.vlanPcp = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"etherType"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.etherType = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"macSource"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.macSource = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"macDest"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.macDest = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"ipSource"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.ipSource = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"ipDest"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.ipDest = rj->u.object.values[rk].value->u.string.ptr; + else if ((!strcmp(rj->u.object.values[rk].name,"ipTos"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ipTos = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"ipProtocol"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ipProtocol = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"ipSourcePort"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ipSourcePort = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"ipDestPort"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.ipDestPort = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"flags"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.flags = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"invFlags"))&&(rj->u.object.values[rk].value->type == json_integer)) + rule.invFlags = &(rj->u.object.values[rk].value->u.integer); + else if ((!strcmp(rj->u.object.values[rk].name,"action"))&&(rj->u.object.values[rk].value->type == json_string)) + rule.action = rj->u.object.values[rk].value->u.string.ptr; + } + + if ((rule.ruleNo)&&(rule.action)&&(rule.action[0])) { + char mactmp1[16],mactmp2[16]; + sqlite3_reset(_sCreateRule); + sqlite3_bind_text(_sCreateRule,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_int64(_sCreateRule,2,*rule.ruleNo); + + // Optional values: null by default + for(int i=3;i<=18;++i) + sqlite3_bind_null(_sCreateRule,i); + if ((rule.nodeId)&&(strlen(rule.nodeId) == 10)) sqlite3_bind_text(_sCreateRule,3,rule.nodeId,10,SQLITE_STATIC); + if ((rule.sourcePort)&&(strlen(rule.sourcePort) == 10)) sqlite3_bind_text(_sCreateRule,4,rule.sourcePort,10,SQLITE_STATIC); + if ((rule.destPort)&&(strlen(rule.destPort) == 10)) sqlite3_bind_text(_sCreateRule,5,rule.destPort,10,SQLITE_STATIC); + if (rule.vlanId) sqlite3_bind_int(_sCreateRule,6,(int)*rule.vlanId); + if (rule.vlanPcp) sqlite3_bind_int(_sCreateRule,7,(int)*rule.vlanPcp); + if (rule.etherType) sqlite3_bind_int(_sCreateRule,8,(int)*rule.etherType & (int)0xffff); + if (rule.macSource) { + MAC m(rule.macSource); + Utils::snprintf(mactmp1,sizeof(mactmp1),"%.12llx",(unsigned long long)m.toInt()); + sqlite3_bind_text(_sCreateRule,9,mactmp1,-1,SQLITE_STATIC); + } + if (rule.macDest) { + MAC m(rule.macDest); + Utils::snprintf(mactmp2,sizeof(mactmp2),"%.12llx",(unsigned long long)m.toInt()); + sqlite3_bind_text(_sCreateRule,10,mactmp2,-1,SQLITE_STATIC); + } + if (rule.ipSource) sqlite3_bind_text(_sCreateRule,11,rule.ipSource,-1,SQLITE_STATIC); + if (rule.ipDest) sqlite3_bind_text(_sCreateRule,12,rule.ipDest,-1,SQLITE_STATIC); + if (rule.ipTos) sqlite3_bind_int(_sCreateRule,13,(int)*rule.ipTos); + if (rule.ipProtocol) sqlite3_bind_int(_sCreateRule,14,(int)*rule.ipProtocol); + if (rule.ipSourcePort) sqlite3_bind_int(_sCreateRule,15,(int)*rule.ipSourcePort & (int)0xffff); + if (rule.ipDestPort) sqlite3_bind_int(_sCreateRule,16,(int)*rule.ipDestPort & (int)0xffff); + if (rule.flags) sqlite3_bind_int64(_sCreateRule,17,(int64_t)*rule.flags); + if (rule.invFlags) sqlite3_bind_int64(_sCreateRule,18,(int64_t)*rule.invFlags); + + sqlite3_bind_text(_sCreateRule,19,rule.action,-1,SQLITE_STATIC); + sqlite3_step(_sCreateRule); + } + } + } + } + } + + if (stmt) { + sqlite3_bind_text(stmt,2,nwids,16,SQLITE_STATIC); + sqlite3_step(stmt); + sqlite3_finalize(stmt); + } + } } + json_value_free(j); } - } - - if (v4s.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; - } - // TODO: IPv6 auto-assign once it's supported in UI + sqlite3_reset(_sSetNetworkRevision); + sqlite3_bind_int64(_sSetNetworkRevision,1,revision += 1); + sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC); + sqlite3_step(_sSetNetworkRevision); - if (network.isPrivate) { - CertificateOfMembership com(OSUtils::now(),ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); - if (com.sign(signingId)) // basically can't fail unless our identity is invalid - netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); - else { - netconf["error"] = "unable to sign COM"; - return NETCONF_QUERY_INTERNAL_SERVER_ERROR; + return _doCPGet(path_copy,urlArgs,headers,body,responseBody,responseContentType); } - } - if (!netconf.sign(signingId,OSUtils::now())) { - netconf["error"] = "unable to sign netconf dictionary"; - return NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } - } + } // else 404 - return NetworkController::NETCONF_QUERY_OK; -} + } // else 404 -unsigned int SqliteNetworkController::handleControlPlaneHttpGET( - const std::vector &path, - const std::map &urlArgs, - const std::map &headers, - const std::string &body, - std::string &responseBody, - std::string &responseContentType) -{ - Mutex::Lock _l(_lock); - return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType); + return 404; } -unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( +unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( const std::vector &path, const std::map &urlArgs, const std::map &headers, @@ -746,1056 +895,913 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpPOST( char nwids[24]; Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - int64_t revision = 0; - sqlite3_reset(_sGetNetworkRevision); - sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC); - bool networkExists = false; - if (sqlite3_step(_sGetNetworkRevision) == SQLITE_ROW) { - networkExists = true; - revision = sqlite3_column_int64(_sGetNetworkRevision,0); - } + sqlite3_reset(_sGetNetworkById); + sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC); + if (sqlite3_step(_sGetNetworkById) != SQLITE_ROW) + return 404; if (path.size() >= 3) { - if (!networkExists) - return 404; - if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { uint64_t address = Utils::hexStrToU64(path[3].c_str()); char addrs[24]; Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); - int64_t addToNetworkRevision = 0; - - int64_t memberRowId = 0; sqlite3_reset(_sGetMember); sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC); sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC); - bool memberExists = false; - if (sqlite3_step(_sGetMember) == SQLITE_ROW) { - memberExists = true; - memberRowId = sqlite3_column_int64(_sGetMember,0); - } + if (sqlite3_step(_sGetMember) != SQLITE_ROW) + return 404; - if (!memberExists) { - sqlite3_reset(_sCreateMember); - sqlite3_bind_text(_sCreateMember,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sCreateMember,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sCreateMember,3,0); - sqlite3_bind_text(_sCreateMember,4,nwids,16,SQLITE_STATIC); - if (sqlite3_step(_sCreateMember) != SQLITE_DONE) + sqlite3_reset(_sDeleteIpAllocations); + sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sDeleteIpAllocations,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + if (sqlite3_step(_sDeleteIpAllocations) == SQLITE_DONE) { + sqlite3_reset(_sDeleteMember); + sqlite3_bind_text(_sDeleteMember,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sDeleteMember,2,addrs,10,SQLITE_STATIC); + if (sqlite3_step(_sDeleteMember) != SQLITE_DONE) return 500; - memberRowId = (int64_t)sqlite3_last_insert_rowid(_db); + } else return 500; - sqlite3_reset(_sIncrementMemberRevisionCounter); - sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sIncrementMemberRevisionCounter); - addToNetworkRevision = 1; - } + return 200; + } - json_value *j = json_parse(body.c_str(),body.length()); - if (j) { - if (j->type == json_object) { - for(unsigned int k=0;ku.object.length;++k) { + } else { - if (!strcmp(j->u.object.values[k].name,"authorized")) { - if (j->u.object.values[k].value->type == json_boolean) { - sqlite3_reset(_sUpdateMemberAuthorized); - sqlite3_bind_int(_sUpdateMemberAuthorized,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); - sqlite3_bind_text(_sUpdateMemberAuthorized,2,nwids,16,SQLITE_STATIC); - sqlite3_bind_int64(_sUpdateMemberAuthorized,3,memberRowId); - if (sqlite3_step(_sUpdateMemberAuthorized) != SQLITE_DONE) - return 500; + sqlite3_reset(_sDeleteNetwork); + sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC); + return ((sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) ? 200 : 500); - sqlite3_reset(_sIncrementMemberRevisionCounter); - sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sIncrementMemberRevisionCounter); - addToNetworkRevision = 1; - } - } else if (!strcmp(j->u.object.values[k].name,"activeBridge")) { - if (j->u.object.values[k].value->type == json_boolean) { - sqlite3_reset(_sUpdateMemberActiveBridge); - sqlite3_bind_int(_sUpdateMemberActiveBridge,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); - sqlite3_bind_text(_sUpdateMemberActiveBridge,2,nwids,16,SQLITE_STATIC); - sqlite3_bind_int64(_sUpdateMemberActiveBridge,3,memberRowId); - if (sqlite3_step(_sUpdateMemberActiveBridge) != SQLITE_DONE) - return 500; + } + } // else 404 - sqlite3_reset(_sIncrementMemberRevisionCounter); - sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sIncrementMemberRevisionCounter); - addToNetworkRevision = 1; - } - } else if (!strcmp(j->u.object.values[k].name,"ipAssignments")) { - if (j->u.object.values[k].value->type == json_array) { - sqlite3_reset(_sDeleteIpAllocations); - sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sDeleteIpAllocations,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - if (sqlite3_step(_sDeleteIpAllocations) != SQLITE_DONE) - return 500; - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *ipalloc = j->u.object.values[k].value->u.array.values[kk]; - if (ipalloc->type == json_string) { - InetAddress a(ipalloc->u.string.ptr); - char ipBlob[16]; - int ipVersion = 0; - switch(a.ss_family) { - case AF_INET: - if ((a.netmaskBits() > 0)&&(a.netmaskBits() <= 32)) { - memset(ipBlob,0,12); - memcpy(ipBlob + 12,a.rawIpData(),4); - ipVersion = 4; - } - break; - case AF_INET6: - if ((a.netmaskBits() > 0)&&(a.netmaskBits() <= 128)) { - memcpy(ipBlob,a.rawIpData(),16); - ipVersion = 6; - } - break; - } - if (ipVersion > 0) { - sqlite3_reset(_sAllocateIp); - sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sAllocateIp,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); - sqlite3_bind_int(_sAllocateIp,5,(int)a.netmaskBits()); - sqlite3_bind_int(_sAllocateIp,6,ipVersion); - if (sqlite3_step(_sAllocateIp) != SQLITE_DONE) - return 500; - } - } - } - addToNetworkRevision = 1; - } - } else if (!strcmp(j->u.object.values[k].name,"identity")) { - // Identity is technically an immutable field, but if the member's Node has - // no identity we allow it to be populated. This is primarily for migrating - // node data from another controller. - json_value *idstr = j->u.object.values[k].value; - if (idstr->type == json_string) { - bool alreadyHaveIdentity = false; + } // else 404 - sqlite3_reset(_sGetNodeIdentity); - sqlite3_bind_text(_sGetNodeIdentity,1,addrs,10,SQLITE_STATIC); - if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) { - const char *tmp2 = (const char *)sqlite3_column_text(_sGetNodeIdentity,0); - if ((tmp2)&&(tmp2[0])) - alreadyHaveIdentity = true; - } + return 404; +} - if (!alreadyHaveIdentity) { - try { - Identity id2(idstr->u.string.ptr); - if (id2) { - std::string idstr2(id2.toString(false)); // object must persist until after sqlite3_step() for SQLITE_STATIC - sqlite3_reset(_sCreateOrReplaceNode); - sqlite3_bind_text(_sCreateOrReplaceNode,1,addrs,10,SQLITE_STATIC); - sqlite3_bind_text(_sCreateOrReplaceNode,2,idstr2.c_str(),-1,SQLITE_STATIC); - sqlite3_step(_sCreateOrReplaceNode); - } - } catch ( ... ) {} // ignore invalid identities - } - } +unsigned int SqliteNetworkController::_doCPGet( + const std::vector &path, + const std::map &urlArgs, + const std::map &headers, + const std::string &body, + std::string &responseBody, + std::string &responseContentType) +{ + // Assumes _lock is locked + char json[65536]; + + if ((path.size() > 0)&&(path[0] == "network")) { + + if ((path.size() >= 2)&&(path[1].length() == 16)) { + uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); + char nwids[24]; + Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); + + if (path.size() >= 3) { + // /network//... + + if (path[2] == "member") { + + if (path.size() >= 4) { + // Get specific member info + + uint64_t address = Utils::hexStrToU64(path[3].c_str()); + char addrs[24]; + Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); + + sqlite3_reset(_sGetMember2); + sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC); + if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { + const char *memberIdStr = (const char *)sqlite3_column_text(_sGetMember2,3); + + // If testSingingId is included in the URL or X-ZT1-TestSigningId in the headers + // and if it contains an identity with a secret portion, the resturned JSON + // will contain an extra field called _testConf. This will contain several + // fields that report the result of doNetworkConfigRequest() for this member. + std::string testFields; + { + Identity testOutputSigningId; + std::map::const_iterator sid(urlArgs.find("testSigningId")); + if (sid != urlArgs.end()) { + testOutputSigningId.fromString(sid->second.c_str()); + } else { + sid = headers.find("x-zt1-testsigningid"); + if (sid != headers.end()) + testOutputSigningId.fromString(sid->second.c_str()); } + if ((testOutputSigningId.hasPrivate())&&(memberIdStr)) { + Dictionary testNetconf; + NetworkController::ResultCode rc = this->_doNetworkConfigRequest( + InetAddress(), + testOutputSigningId, + Identity(memberIdStr), + nwid, + Dictionary(), // TODO: allow passing of meta-data for testing + testNetconf); + char rcs[16]; + Utils::snprintf(rcs,sizeof(rcs),"%d,\n",(int)rc); + testFields.append("\t\"_test\": {\n"); + testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); + testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\""); + testFields.append("\t}\n"); + } } - } - json_value_free(j); - } - if ((addToNetworkRevision > 0)&&(revision > 0)) { - sqlite3_reset(_sSetNetworkRevision); - sqlite3_bind_int64(_sSetNetworkRevision,1,revision + addToNetworkRevision); - sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC); - sqlite3_step(_sSetNetworkRevision); - } + Utils::snprintf(json,sizeof(json), + "{\n%s" + "\t\"nwid\": \"%s\",\n" + "\t\"address\": \"%s\",\n" + "\t\"controllerInstanceId\": \"%s\",\n" + "\t\"authorized\": %s,\n" + "\t\"activeBridge\": %s,\n" + "\t\"memberRevision\": %llu,\n" + "\t\"clock\": %llu,\n" + "\t\"identity\": \"%s\",\n" + "\t\"ipAssignments\": [", + testFields.c_str(), + nwids, + addrs, + _instanceId.c_str(), + (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false", + (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", + (unsigned long long)sqlite3_column_int64(_sGetMember2,2), + (unsigned long long)OSUtils::now(), + _jsonEscape(memberIdStr).c_str()); + responseBody = json; - return _doCPGet(path,urlArgs,headers,body,responseBody,responseContentType); - } // else 404 + sqlite3_reset(_sGetIpAssignmentsForNode2); + sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC); + sqlite3_bind_int(_sGetIpAssignmentsForNode2,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + bool firstIp = true; + while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) { + int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2); + char ipBlob[16]; + memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16); + InetAddress ip( + (const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]), + (ipversion == 6 ? 16 : 4), + (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1) + ); + responseBody.append(firstIp ? "\"" : ",\""); + firstIp = false; + responseBody.append(_jsonEscape(ip.toString())); + responseBody.push_back('"'); + } - } else { - std::vector path_copy(path); + responseBody.append("],\n\t\"recentLog\": ["); - if (!networkExists) { - if (path[1].substr(10) == "______") { - // A special POST /network/##########______ feature lets users create a network - // with an arbitrary unused network number at this controller. - nwid = 0; + sqlite3_reset(_sGetRecentMemberLog); + sqlite3_bind_text(_sGetRecentMemberLog,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetRecentMemberLog,2,addrs,10,SQLITE_STATIC); + bool firstLog = true; + while (sqlite3_step(_sGetRecentMemberLog) == SQLITE_ROW) { + responseBody.append(firstLog ? "{" : ",{"); + firstLog = false; + responseBody.append("\"ts\":"); + responseBody.append(reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,0))); + responseBody.append((sqlite3_column_int(_sGetRecentMemberLog,1) == 0) ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":"); + const char *ver = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,2)); + if ((ver)&&(ver[0])) { + responseBody.push_back('"'); + responseBody.append(_jsonEscape(ver)); + responseBody.append("\",\"fromAddr\":"); + } else responseBody.append("null,\"fromAddr\":"); + const char *fa = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,3)); + if ((fa)&&(fa[0])) { + responseBody.push_back('"'); + responseBody.append(_jsonEscape(fa)); + responseBody.append("\"}"); + } else responseBody.append("null}"); + } - uint64_t nwidPrefix = (Utils::hexStrToU64(path[1].substr(0,10).c_str()) << 24) & 0xffffffffff000000ULL; - uint64_t nwidPostfix = 0; - Utils::getSecureRandom(&nwidPostfix,sizeof(nwidPostfix)); - uint64_t nwidOriginalPostfix = nwidPostfix; - do { - uint64_t tryNwid = nwidPrefix | (nwidPostfix & 0xffffffULL); - if (!nwidPostfix) - tryNwid |= 1; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)tryNwid); + responseBody.append("]\n}\n"); - sqlite3_reset(_sGetNetworkRevision); - sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC); - if (sqlite3_step(_sGetNetworkRevision) != SQLITE_ROW) { - nwid = tryNwid; - break; - } + responseContentType = "application/json"; + return 200; + } // else 404 - ++nwidPostfix; - } while (nwidPostfix != nwidOriginalPostfix); + } else { + // List members - // 503 means we have no more free IDs for this prefix. You shouldn't host anywhere - // near 16 million networks on the same controller, so shouldn't happen. - if (!nwid) - return 503; - } + sqlite3_reset(_sListNetworkMembers); + sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); + responseBody.append("{"); + bool firstMember = true; + while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { + responseBody.append(firstMember ? "\"" : ",\""); + firstMember = false; + responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); + responseBody.append("\":"); + responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,1)); + } + responseBody.push_back('}'); + responseContentType = "application/json"; + return 200; - sqlite3_reset(_sCreateNetwork); - sqlite3_bind_text(_sCreateNetwork,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sCreateNetwork,2,"",0,SQLITE_STATIC); - sqlite3_bind_int64(_sCreateNetwork,3,(long long)OSUtils::now()); - if (sqlite3_step(_sCreateNetwork) != SQLITE_DONE) - return 500; - path_copy[1].assign(nwids); - } + } - json_value *j = json_parse(body.c_str(),body.length()); - if (j) { - if (j->type == json_object) { - for(unsigned int k=0;ku.object.length;++k) { - sqlite3_stmt *stmt = (sqlite3_stmt *)0; + } // else 404 - if (!strcmp(j->u.object.values[k].name,"name")) { - if ((j->u.object.values[k].value->type == json_string)&&(j->u.object.values[k].value->u.string.ptr[0])) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"name\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); - } - } else if (!strcmp(j->u.object.values[k].name,"private")) { - if (j->u.object.values[k].value->type == json_boolean) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET \"private\" = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); - } - } else if (!strcmp(j->u.object.values[k].name,"enableBroadcast")) { - if (j->u.object.values[k].value->type == json_boolean) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET enableBroadcast = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); - } - } else if (!strcmp(j->u.object.values[k].name,"allowPassiveBridging")) { - if (j->u.object.values[k].value->type == json_boolean) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET allowPassiveBridging = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_int(stmt,1,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1); - } - } else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) { - if (j->u.object.values[k].value->type == json_string) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET v4AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); - } - } else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) { - if (j->u.object.values[k].value->type == json_string) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET v6AssignMode = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_text(stmt,1,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC); - } - } else if (!strcmp(j->u.object.values[k].name,"multicastLimit")) { - if (j->u.object.values[k].value->type == json_integer) { - if (sqlite3_prepare_v2(_db,"UPDATE Network SET multicastLimit = ? WHERE id = ?",-1,&stmt,(const char **)0) == SQLITE_OK) - sqlite3_bind_int(stmt,1,(int)j->u.object.values[k].value->u.integer); - } - } else if (!strcmp(j->u.object.values[k].name,"relays")) { - if (j->u.object.values[k].value->type == json_array) { - std::map nodeIdToPhyAddress; - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *relay = j->u.object.values[k].value->u.array.values[kk]; - const char *address = (const char *)0; - const char *phyAddress = (const char *)0; - if ((relay)&&(relay->type == json_object)) { - for(unsigned int rk=0;rku.object.length;++rk) { - if ((!strcmp(relay->u.object.values[rk].name,"address"))&&(relay->u.object.values[rk].value->type == json_string)) - address = relay->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(relay->u.object.values[rk].name,"phyAddress"))&&(relay->u.object.values[rk].value->type == json_string)) - phyAddress = relay->u.object.values[rk].value->u.string.ptr; - } - } - if ((address)&&(phyAddress)) - nodeIdToPhyAddress[Address(address)] = InetAddress(phyAddress); - } - - sqlite3_reset(_sDeleteRelaysForNetwork); - sqlite3_bind_text(_sDeleteRelaysForNetwork,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sDeleteRelaysForNetwork); - - for(std::map::iterator rl(nodeIdToPhyAddress.begin());rl!=nodeIdToPhyAddress.end();++rl) { - sqlite3_reset(_sCreateRelay); - sqlite3_bind_text(_sCreateRelay,1,nwids,16,SQLITE_STATIC); - std::string a(rl->first.toString()),b(rl->second.toString()); // don't destroy strings until sqlite3_step() - sqlite3_bind_text(_sCreateRelay,2,a.c_str(),-1,SQLITE_STATIC); - sqlite3_bind_text(_sCreateRelay,3,b.c_str(),-1,SQLITE_STATIC); - sqlite3_step(_sCreateRelay); - } - } - } else if (!strcmp(j->u.object.values[k].name,"gateways")) { - sqlite3_reset(_sDeleteGateways); - sqlite3_bind_text(_sDeleteGateways,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sDeleteGateways); - if (j->u.object.values[k].value->type == json_array) { - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *gateway = j->u.object.values[k].value->u.array.values[kk]; - if ((gateway)&&(gateway->type == json_string)) { - InetAddress gwip(gateway->u.string.ptr); - sqlite3_reset(_sCreateGateway); - sqlite3_bind_text(_sCreateGateway,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_int(_sCreateGateway,4,(int)gwip.metric()); - if (gwip.ss_family == AF_INET) { - char ipBlob[16]; - memset(ipBlob,0,12); - memcpy(ipBlob + 12,gwip.rawIpData(),4); - sqlite3_bind_blob(_sCreateGateway,2,(const void *)ipBlob,16,SQLITE_STATIC); - sqlite3_bind_int(_sCreateGateway,3,4); - sqlite3_step(_sCreateGateway); - } else if (gwip.ss_family == AF_INET6) { - sqlite3_bind_blob(_sCreateGateway,2,gwip.rawIpData(),16,SQLITE_STATIC); - sqlite3_bind_int(_sCreateGateway,3,6); - sqlite3_step(_sCreateGateway); - } - } - } - } - } else if (!strcmp(j->u.object.values[k].name,"ipLocalRoutes")) { - sqlite3_reset(_sDeleteLocalRoutes); - sqlite3_bind_text(_sDeleteLocalRoutes,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_int(_sDeleteLocalRoutes,2,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); - sqlite3_step(_sDeleteLocalRoutes); - if (j->u.object.values[k].value->type == json_array) { - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *localRoute = j->u.object.values[k].value->u.array.values[kk]; - if ((localRoute)&&(localRoute->type == json_string)) { - InetAddress lr(localRoute->u.string.ptr); - if (lr.ss_family == AF_INET) { - char ipBlob[16]; - memset(ipBlob,0,12); - memcpy(ipBlob + 12,lr.rawIpData(),4); - sqlite3_reset(_sAllocateIp); - sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_null(_sAllocateIp,2); - sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); - sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); - sqlite3_bind_int(_sAllocateIp,5,lr.netmaskBits()); - sqlite3_bind_int(_sAllocateIp,6,4); - sqlite3_step(_sAllocateIp); - } else if (lr.ss_family == AF_INET6) { - sqlite3_reset(_sAllocateIp); - sqlite3_bind_text(_sAllocateIp,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_null(_sAllocateIp,2); - sqlite3_bind_int(_sAllocateIp,3,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); - sqlite3_bind_blob(_sAllocateIp,4,lr.rawIpData(),16,SQLITE_STATIC); - sqlite3_bind_int(_sAllocateIp,5,lr.netmaskBits()); - sqlite3_bind_int(_sAllocateIp,6,6); - sqlite3_step(_sAllocateIp); - } - } - } - } - } else if (!strcmp(j->u.object.values[k].name,"ipAssignmentPools")) { - if (j->u.object.values[k].value->type == json_array) { - std::vector< std::pair > pools; - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *pool = j->u.object.values[k].value->u.array.values[kk]; - const char *iprs = (const char *)0; - const char *ipre = (const char *)0; - if ((pool)&&(pool->type == json_object)) { - for(unsigned int rk=0;rku.object.length;++rk) { - if ((!strcmp(pool->u.object.values[rk].name,"ipRangeStart"))&&(pool->u.object.values[rk].value->type == json_string)) - iprs = pool->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(pool->u.object.values[rk].name,"ipRangeEnd"))&&(pool->u.object.values[rk].value->type == json_string)) - ipre = pool->u.object.values[rk].value->u.string.ptr; - } - } - if ((iprs)&&(ipre)) { - InetAddress iprs2(iprs); - InetAddress ipre2(ipre); - if (iprs2.ss_family == ipre2.ss_family) { - iprs2.setPort(0); - ipre2.setPort(0); - pools.push_back(std::pair(iprs2,ipre2)); - } - } - } - std::sort(pools.begin(),pools.end()); - pools.erase(std::unique(pools.begin(),pools.end()),pools.end()); + } else { + // get network info + sqlite3_reset(_sGetNetworkById); + sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC); + if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { + Utils::snprintf(json,sizeof(json), + "{\n" + "\t\"nwid\": \"%s\",\n" + "\t\"controllerInstanceId\": \"%s\",\n" + "\t\"clock\": %llu,\n" + "\t\"name\": \"%s\",\n" + "\t\"private\": %s,\n" + "\t\"enableBroadcast\": %s,\n" + "\t\"allowPassiveBridging\": %s,\n" + "\t\"v4AssignMode\": \"%s\",\n" + "\t\"v6AssignMode\": \"%s\",\n" + "\t\"multicastLimit\": %d,\n" + "\t\"creationTime\": %llu,\n" + "\t\"revision\": %llu,\n" + "\t\"memberRevisionCounter\": %llu,\n" + "\t\"authorizedMemberCount\": %llu,\n" + "\t\"relays\": [", + nwids, + _instanceId.c_str(), + (unsigned long long)OSUtils::now(), + _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(), + (sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false", + (sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false", + (sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false", + _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,4)).c_str(), + _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,5)).c_str(), + sqlite3_column_int(_sGetNetworkById,6), + (unsigned long long)sqlite3_column_int64(_sGetNetworkById,7), + (unsigned long long)sqlite3_column_int64(_sGetNetworkById,8), + (unsigned long long)sqlite3_column_int64(_sGetNetworkById,9), + (unsigned long long)sqlite3_column_int64(_sGetNetworkById,10)); + responseBody = json; - sqlite3_reset(_sDeleteIpAssignmentPoolsForNetwork); - sqlite3_bind_text(_sDeleteIpAssignmentPoolsForNetwork,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sDeleteIpAssignmentPoolsForNetwork); + sqlite3_reset(_sGetRelays); + sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC); + bool firstRelay = true; + while (sqlite3_step(_sGetRelays) == SQLITE_ROW) { + responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t"); + firstRelay = false; + responseBody.append("{\"address\":\""); + responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0)); + responseBody.append("\",\"phyAddress\":\""); + responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1))); + responseBody.append("\"}"); + } - for(std::vector< std::pair >::const_iterator p(pools.begin());p!=pools.end();++p) { - char ipBlob1[16],ipBlob2[16]; - sqlite3_reset(_sCreateIpAssignmentPool); - sqlite3_bind_text(_sCreateIpAssignmentPool,1,nwids,16,SQLITE_STATIC); - if (p->first.ss_family == AF_INET) { - memset(ipBlob1,0,12); - memcpy(ipBlob1 + 12,p->first.rawIpData(),4); - memset(ipBlob2,0,12); - memcpy(ipBlob2 + 12,p->second.rawIpData(),4); - sqlite3_bind_blob(_sCreateIpAssignmentPool,2,(const void *)ipBlob1,16,SQLITE_STATIC); - sqlite3_bind_blob(_sCreateIpAssignmentPool,3,(const void *)ipBlob2,16,SQLITE_STATIC); - sqlite3_bind_int(_sCreateIpAssignmentPool,4,4); - } else if (p->first.ss_family == AF_INET6) { - sqlite3_bind_blob(_sCreateIpAssignmentPool,2,p->first.rawIpData(),16,SQLITE_STATIC); - sqlite3_bind_blob(_sCreateIpAssignmentPool,3,p->second.rawIpData(),16,SQLITE_STATIC); - sqlite3_bind_int(_sCreateIpAssignmentPool,4,6); - } else continue; - sqlite3_step(_sCreateIpAssignmentPool); - } - } - } else if (!strcmp(j->u.object.values[k].name,"rules")) { - if (j->u.object.values[k].value->type == json_array) { - sqlite3_reset(_sDeleteRulesForNetwork); - sqlite3_bind_text(_sDeleteRulesForNetwork,1,nwids,16,SQLITE_STATIC); - sqlite3_step(_sDeleteRulesForNetwork); + responseBody.append("],\n\t\"gateways\": ["); - for(unsigned int kk=0;kku.object.values[k].value->u.array.length;++kk) { - json_value *rj = j->u.object.values[k].value->u.array.values[kk]; - if ((rj)&&(rj->type == json_object)) { - struct { // NULL pointers indicate missing or NULL -- wildcards - const json_int_t *ruleNo; - const char *nodeId; - const char *sourcePort; - const char *destPort; - const json_int_t *vlanId; - const json_int_t *vlanPcp; - const json_int_t *etherType; - const char *macSource; - const char *macDest; - const char *ipSource; - const char *ipDest; - const json_int_t *ipTos; - const json_int_t *ipProtocol; - const json_int_t *ipSourcePort; - const json_int_t *ipDestPort; - const json_int_t *flags; - const json_int_t *invFlags; - const char *action; - } rule; - memset(&rule,0,sizeof(rule)); + sqlite3_reset(_sGetGateways); + sqlite3_bind_text(_sGetGateways,1,nwids,16,SQLITE_STATIC); + bool firstGateway = true; + while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { + char tmp[128]; + const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); + switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion + case 4: + Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d\"", + (firstGateway) ? "\"" : ",\"", + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + break; + case 6: + Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"", + (firstGateway) ? "\"" : ",\"", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)ip[4], + (int)ip[5], + (int)ip[6], + (int)ip[7], + (int)ip[8], + (int)ip[9], + (int)ip[10], + (int)ip[11], + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + break; + } + responseBody.append(tmp); + firstGateway = false; + } - for(unsigned int rk=0;rku.object.length;++rk) { - if ((!strcmp(rj->u.object.values[rk].name,"ruleNo"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ruleNo = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"nodeId"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.nodeId = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"sourcePort"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.sourcePort = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"destPort"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.destPort = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"vlanId"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.vlanId = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"vlanPcp"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.vlanPcp = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"etherType"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.etherType = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"macSource"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.macSource = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"macDest"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.macDest = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"ipSource"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.ipSource = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"ipDest"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.ipDest = rj->u.object.values[rk].value->u.string.ptr; - else if ((!strcmp(rj->u.object.values[rk].name,"ipTos"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ipTos = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"ipProtocol"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ipProtocol = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"ipSourcePort"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ipSourcePort = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"ipDestPort"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.ipDestPort = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"flags"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.flags = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"invFlags"))&&(rj->u.object.values[rk].value->type == json_integer)) - rule.invFlags = &(rj->u.object.values[rk].value->u.integer); - else if ((!strcmp(rj->u.object.values[rk].name,"action"))&&(rj->u.object.values[rk].value->type == json_string)) - rule.action = rj->u.object.values[rk].value->u.string.ptr; - } + responseBody.append("],\n\t\"ipLocalRoutes\": ["); - if ((rule.ruleNo)&&(rule.action)&&(rule.action[0])) { - char mactmp1[16],mactmp2[16]; - sqlite3_reset(_sCreateRule); - sqlite3_bind_text(_sCreateRule,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_int64(_sCreateRule,2,*rule.ruleNo); + sqlite3_reset(_sGetLocalRoutes); + sqlite3_bind_text(_sGetLocalRoutes,1,nwids,16,SQLITE_STATIC); + sqlite3_bind_int(_sGetLocalRoutes,2,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); + bool firstLocalRoute = true; + while (sqlite3_step(_sGetLocalRoutes) == SQLITE_ROW) { + char tmp[128]; + const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetLocalRoutes,0); + switch (sqlite3_column_int(_sGetLocalRoutes,2)) { + case 4: + Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d\"", + (firstLocalRoute) ? "\"" : ",\"", + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetLocalRoutes,1)); // netmask bits + break; + case 6: + Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"", + (firstLocalRoute) ? "\"" : ",\"", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)ip[4], + (int)ip[5], + (int)ip[6], + (int)ip[7], + (int)ip[8], + (int)ip[9], + (int)ip[10], + (int)ip[11], + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetLocalRoutes,1)); // netmask bits + break; + } + responseBody.append(tmp); + firstLocalRoute = false; + } - // Optional values: null by default - for(int i=3;i<=18;++i) - sqlite3_bind_null(_sCreateRule,i); - if ((rule.nodeId)&&(strlen(rule.nodeId) == 10)) sqlite3_bind_text(_sCreateRule,3,rule.nodeId,10,SQLITE_STATIC); - if ((rule.sourcePort)&&(strlen(rule.sourcePort) == 10)) sqlite3_bind_text(_sCreateRule,4,rule.sourcePort,10,SQLITE_STATIC); - if ((rule.destPort)&&(strlen(rule.destPort) == 10)) sqlite3_bind_text(_sCreateRule,5,rule.destPort,10,SQLITE_STATIC); - if (rule.vlanId) sqlite3_bind_int(_sCreateRule,6,(int)*rule.vlanId); - if (rule.vlanPcp) sqlite3_bind_int(_sCreateRule,7,(int)*rule.vlanPcp); - if (rule.etherType) sqlite3_bind_int(_sCreateRule,8,(int)*rule.etherType & (int)0xffff); - if (rule.macSource) { - MAC m(rule.macSource); - Utils::snprintf(mactmp1,sizeof(mactmp1),"%.12llx",(unsigned long long)m.toInt()); - sqlite3_bind_text(_sCreateRule,9,mactmp1,-1,SQLITE_STATIC); - } - if (rule.macDest) { - MAC m(rule.macDest); - Utils::snprintf(mactmp2,sizeof(mactmp2),"%.12llx",(unsigned long long)m.toInt()); - sqlite3_bind_text(_sCreateRule,10,mactmp2,-1,SQLITE_STATIC); - } - if (rule.ipSource) sqlite3_bind_text(_sCreateRule,11,rule.ipSource,-1,SQLITE_STATIC); - if (rule.ipDest) sqlite3_bind_text(_sCreateRule,12,rule.ipDest,-1,SQLITE_STATIC); - if (rule.ipTos) sqlite3_bind_int(_sCreateRule,13,(int)*rule.ipTos); - if (rule.ipProtocol) sqlite3_bind_int(_sCreateRule,14,(int)*rule.ipProtocol); - if (rule.ipSourcePort) sqlite3_bind_int(_sCreateRule,15,(int)*rule.ipSourcePort & (int)0xffff); - if (rule.ipDestPort) sqlite3_bind_int(_sCreateRule,16,(int)*rule.ipDestPort & (int)0xffff); - if (rule.flags) sqlite3_bind_int64(_sCreateRule,17,(int64_t)*rule.flags); - if (rule.invFlags) sqlite3_bind_int64(_sCreateRule,18,(int64_t)*rule.invFlags); + responseBody.append("],\n\t\"ipAssignmentPools\": ["); - sqlite3_bind_text(_sCreateRule,19,rule.action,-1,SQLITE_STATIC); - sqlite3_step(_sCreateRule); - } - } - } - } + sqlite3_reset(_sGetIpAssignmentPools2); + sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC); + bool firstIpAssignmentPool = true; + while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) { + const char *ipRangeStartB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools2,0)); + const char *ipRangeEndB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools2,1)); + if ((ipRangeStartB)&&(ipRangeEndB)) { + InetAddress ipps,ippe; + int ipVersion = sqlite3_column_int(_sGetIpAssignmentPools2,2); + if (ipVersion == 4) { + ipps.set((const void *)(ipRangeStartB + 12),4,0); + ippe.set((const void *)(ipRangeEndB + 12),4,0); + } else if (ipVersion == 6) { + ipps.set((const void *)ipRangeStartB,16,0); + ippe.set((const void *)ipRangeEndB,16,0); } - - if (stmt) { - sqlite3_bind_text(stmt,2,nwids,16,SQLITE_STATIC); - sqlite3_step(stmt); - sqlite3_finalize(stmt); + if (ipps) { + responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t"); + firstIpAssignmentPool = false; + Utils::snprintf(json,sizeof(json),"{\"ipRangeStart\":\"%s\",\"ipRangeEnd\":\"%s\"}", + _jsonEscape(ipps.toIpString()).c_str(), + _jsonEscape(ippe.toIpString()).c_str()); + responseBody.append(json); } } } - json_value_free(j); - } - sqlite3_reset(_sSetNetworkRevision); - sqlite3_bind_int64(_sSetNetworkRevision,1,revision += 1); - sqlite3_bind_text(_sSetNetworkRevision,2,nwids,16,SQLITE_STATIC); - sqlite3_step(_sSetNetworkRevision); + responseBody.append("],\n\t\"rules\": ["); - return _doCPGet(path_copy,urlArgs,headers,body,responseBody,responseContentType); - } + sqlite3_reset(_sListRules); + sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC); + bool firstRule = true; + while (sqlite3_step(_sListRules) == SQLITE_ROW) { + responseBody.append(firstRule ? "\n\t{\n" : ",{\n"); + firstRule = false; + Utils::snprintf(json,sizeof(json),"\t\t\"ruleNo\": %lld,\n",sqlite3_column_int64(_sListRules,0)); + responseBody.append(json); + if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"nodeId\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"sourcePort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,2)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"destPort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,3)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"vlanId\": %d,\n",sqlite3_column_int(_sListRules,4)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"vlanPcp\": %d,\n",sqlite3_column_int(_sListRules,5)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"etherType\": %d,\n",sqlite3_column_int(_sListRules,6)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"macSource\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,7)).toString().c_str()); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"macDest\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,8)).toString().c_str()); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipSource\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,9)).c_str()); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipDest\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,10)).c_str()); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipTos\": %d,\n",sqlite3_column_int(_sListRules,11)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipProtocol\": %d,\n",sqlite3_column_int(_sListRules,12)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,13) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipSourcePort\": %d,\n",sqlite3_column_int(_sListRules,13)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,14) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"ipDestPort\": %d,\n",sqlite3_column_int(_sListRules,14)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,15) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"flags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,15)); + responseBody.append(json); + } + if (sqlite3_column_type(_sListRules,16) != SQLITE_NULL) { + Utils::snprintf(json,sizeof(json),"\t\t\"invFlags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,16)); + responseBody.append(json); + } + responseBody.append("\t\t\"action\": \""); + responseBody.append(_jsonEscape( (sqlite3_column_type(_sListRules,17) == SQLITE_NULL) ? "drop" : (const char *)sqlite3_column_text(_sListRules,17) )); + responseBody.append("\"\n\t}"); + } + responseBody.append("]\n}\n"); + responseContentType = "application/json"; + return 200; + } // else 404 + } + } else if (path.size() == 1) { + // list networks + sqlite3_reset(_sListNetworks); + responseContentType = "application/json"; + responseBody = "["; + bool first = true; + while (sqlite3_step(_sListNetworks) == SQLITE_ROW) { + if (first) { + first = false; + responseBody.push_back('"'); + } else responseBody.append(",\""); + responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0)); + responseBody.push_back('"'); + } + responseBody.push_back(']'); + return 200; } // else 404 - } // else 404 + } else { + // GET /controller returns status and API version if controller is supported + Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"instanceId\": \"%s\"\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),_instanceId.c_str()); + responseBody = json; + responseContentType = "application/json"; + return 200; + } return 404; } -unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( - const std::vector &path, - const std::map &urlArgs, - const std::map &headers, - const std::string &body, - std::string &responseBody, - std::string &responseContentType) +NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &signingId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,Dictionary &netconf) { - if (path.empty()) - return 404; - Mutex::Lock _l(_lock); - - if (path[0] == "network") { - - if ((path.size() >= 2)&&(path[1].length() == 16)) { - uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); - char nwids[24]; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - - sqlite3_reset(_sGetNetworkById); - sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC); - if (sqlite3_step(_sGetNetworkById) != SQLITE_ROW) - return 404; - - if (path.size() >= 3) { - - if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) { - uint64_t address = Utils::hexStrToU64(path[3].c_str()); - char addrs[24]; - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); - - sqlite3_reset(_sGetMember); - sqlite3_bind_text(_sGetMember,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetMember,2,addrs,10,SQLITE_STATIC); - if (sqlite3_step(_sGetMember) != SQLITE_ROW) - return 404; - - sqlite3_reset(_sDeleteIpAllocations); - sqlite3_bind_text(_sDeleteIpAllocations,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sDeleteIpAllocations,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sDeleteIpAllocations,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - if (sqlite3_step(_sDeleteIpAllocations) == SQLITE_DONE) { - sqlite3_reset(_sDeleteMember); - sqlite3_bind_text(_sDeleteMember,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sDeleteMember,2,addrs,10,SQLITE_STATIC); - if (sqlite3_step(_sDeleteMember) != SQLITE_DONE) - return 500; - } else return 500; + // Assumes _lock is locked - return 200; - } + // Decode some stuff from metaData + const unsigned int clientMajorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION,0); + const unsigned int clientMinorVersion = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION,0); + const unsigned int clientRevision = (unsigned int)metaData.getHexUInt(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION,0); + const bool clientIs104 = (Utils::compareVersion(clientMajorVersion,clientMinorVersion,clientRevision,1,0,4) >= 0); - } else { + // Note: we can't reuse prepared statements that return const char * pointers without + // making our own copy in e.g. a std::string first. - sqlite3_reset(_sDeleteNetwork); - sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC); - return ((sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) ? 200 : 500); + if ((!signingId)||(!signingId.hasPrivate())) { + netconf["error"] = "signing identity invalid or lacks private key"; + return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } + if (signingId.address().toInt() != (nwid >> 24)) { + netconf["error"] = "signing identity address does not match most significant 40 bits of network ID"; + return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } - } - } // else 404 + // Check rate limit - } // else 404 + { + uint64_t &lrt = _lastRequestTime[std::pair(identity.address(),nwid)]; + uint64_t lrt2 = lrt; + if (((lrt = OSUtils::now()) - lrt2) <= ZT_NETCONF_MIN_REQUEST_PERIOD) + return NetworkController::NETCONF_QUERY_IGNORE; + } - return 404; -} + NetworkRecord network; + memset(&network,0,sizeof(network)); + Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); -unsigned int SqliteNetworkController::_doCPGet( - const std::vector &path, - const std::map &urlArgs, - const std::map &headers, - const std::string &body, - std::string &responseBody, - std::string &responseContentType) -{ - // Assumes _lock is locked - char json[16384]; + MemberRecord member; + memset(&member,0,sizeof(member)); + Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); - if ((path.size() > 0)&&(path[0] == "network")) { + // Create Node record or do full identity check if we already have one - if ((path.size() >= 2)&&(path[1].length() == 16)) { - uint64_t nwid = Utils::hexStrToU64(path[1].c_str()); - char nwids[24]; - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); + 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) { + netconf["error"] = "unable to create new Node record"; + return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } + } - if (path.size() >= 3) { - // /network//... + // Fetch Network record - if (path[2] == "member") { + 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.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4); + network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5); + network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6); + network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); + network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8); + network.memberRevisionCounter = (uint64_t)sqlite3_column_int64(_sGetNetworkById,9); + } else { + return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; + } - if (path.size() >= 4) { - // Get specific member info + // Fetch Member record - uint64_t address = Utils::hexStrToU64(path[3].c_str()); - char addrs[24]; - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address); + 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); + } - sqlite3_reset(_sGetMember2); - sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC); - if (sqlite3_step(_sGetMember2) == SQLITE_ROW) { - const char *memberIdStr = (const char *)sqlite3_column_text(_sGetMember2,3); + // Create Member record for unknown nodes, auto-authorizing if network is public - // If testSingingId is included in the URL or X-ZT1-TestSigningId in the headers - // and if it contains an identity with a secret portion, the resturned JSON - // will contain an extra field called _testConf. This will contain several - // fields that report the result of doNetworkConfigRequest() for this member. - std::string testFields; - { - Identity testOutputSigningId; - std::map::const_iterator sid(urlArgs.find("testSigningId")); - if (sid != urlArgs.end()) { - testOutputSigningId.fromString(sid->second.c_str()); - } else { - sid = headers.find("x-zt1-testsigningid"); - if (sid != headers.end()) - testOutputSigningId.fromString(sid->second.c_str()); - } + 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) { + netconf["error"] = "unable to create new member record"; + return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } + member.rowid = (int64_t)sqlite3_last_insert_rowid(_db); - if ((testOutputSigningId.hasPrivate())&&(memberIdStr)) { - Dictionary testNetconf; - NetworkController::ResultCode rc = this->doNetworkConfigRequest( - InetAddress(), - testOutputSigningId, - Identity(memberIdStr), - nwid, - Dictionary(), // TODO: allow passing of meta-data for testing - testNetconf); - char rcs[16]; - Utils::snprintf(rcs,sizeof(rcs),"%d,\n",(int)rc); - testFields.append("\t\"_test\": {\n"); - testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); - testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\""); - testFields.append("\t}\n"); - } - } + sqlite3_reset(_sIncrementMemberRevisionCounter); + sqlite3_bind_text(_sIncrementMemberRevisionCounter,1,network.id,16,SQLITE_STATIC); + sqlite3_step(_sIncrementMemberRevisionCounter); + } - Utils::snprintf(json,sizeof(json), - "{\n%s" - "\t\"nwid\": \"%s\",\n" - "\t\"address\": \"%s\",\n" - "\t\"controllerInstanceId\": \"%s\",\n" - "\t\"authorized\": %s,\n" - "\t\"activeBridge\": %s,\n" - "\t\"memberRevision\": %llu,\n" - "\t\"clock\": %llu,\n" - "\t\"identity\": \"%s\",\n" - "\t\"ipAssignments\": [", - testFields.c_str(), - nwids, - addrs, - _instanceId.c_str(), - (sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false", - (sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false", - (unsigned long long)sqlite3_column_int64(_sGetMember2,2), - (unsigned long long)OSUtils::now(), - _jsonEscape(memberIdStr).c_str()); - responseBody = json; + // Add log entry + { + char ver[16]; + std::string fa; + if (fromAddr) { + fa = fromAddr.toString(); + if (fa.length() > 64) + fa = fa.substr(0,64); + } + sqlite3_reset(_sPutLog); + sqlite3_bind_text(_sPutLog,1,network.id,16,SQLITE_STATIC); + sqlite3_bind_text(_sPutLog,2,member.nodeId,10,SQLITE_STATIC); + sqlite3_bind_int64(_sPutLog,3,(long long)OSUtils::now()); + sqlite3_bind_int(_sPutLog,4,member.authorized ? 1 : 0); + if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) { + Utils::snprintf(ver,sizeof(ver),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); + sqlite3_bind_text(_sPutLog,5,ver,-1,SQLITE_STATIC); + } else sqlite3_bind_null(_sPutLog,5); + if (fa.length() > 0) + sqlite3_bind_text(_sPutLog,6,fa.c_str(),-1,SQLITE_STATIC); + else sqlite3_bind_null(_sPutLog,6); + sqlite3_step(_sPutLog); + } - sqlite3_reset(_sGetIpAssignmentsForNode2); - sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC); - sqlite3_bind_int(_sGetIpAssignmentsForNode2,3,(int)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); - bool firstIp = true; - while (sqlite3_step(_sGetIpAssignmentsForNode2) == SQLITE_ROW) { - int ipversion = sqlite3_column_int(_sGetIpAssignmentsForNode2,2); - char ipBlob[16]; - memcpy(ipBlob,(const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),16); - InetAddress ip( - (const void *)(ipversion == 6 ? ipBlob : &ipBlob[12]), - (ipversion == 6 ? 16 : 4), - (unsigned int)sqlite3_column_int(_sGetIpAssignmentsForNode2,1) - ); - responseBody.append(firstIp ? "\"" : ",\""); - firstIp = false; - responseBody.append(_jsonEscape(ip.toString())); - responseBody.push_back('"'); - } + // Check member authorization - responseBody.append("],\n\t\"recentLog\": ["); + if (!member.authorized) + return NetworkController::NETCONF_QUERY_ACCESS_DENIED; - sqlite3_reset(_sGetRecentMemberLog); - sqlite3_bind_text(_sGetRecentMemberLog,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetRecentMemberLog,2,addrs,10,SQLITE_STATIC); - bool firstLog = true; - while (sqlite3_step(_sGetRecentMemberLog) == SQLITE_ROW) { - responseBody.append(firstLog ? "{" : ",{"); - firstLog = false; - responseBody.append("\"ts\":"); - responseBody.append(reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,0))); - responseBody.append((sqlite3_column_int(_sGetRecentMemberLog,1) == 0) ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":"); - const char *ver = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,2)); - if ((ver)&&(ver[0])) { - responseBody.push_back('"'); - responseBody.append(_jsonEscape(ver)); - responseBody.append("\",\"fromAddr\":"); - } else responseBody.append("null,\"fromAddr\":"); - const char *fa = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,3)); - if ((fa)&&(fa[0])) { - responseBody.push_back('"'); - responseBody.append(_jsonEscape(fa)); - responseBody.append("\"}"); - } else responseBody.append("null}"); - } + // Create and sign netconf - responseBody.append("]\n}\n"); + netconf.clear(); + { + char tss[24],rs[24]; + Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)OSUtils::now()); + Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision); + netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; + netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs; + netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = network.id; + netconf[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = member.nodeId; + netconf[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = network.isPrivate ? "1" : "0"; + netconf[ZT_NETWORKCONFIG_DICT_KEY_NAME] = (network.name) ? network.name : ""; + netconf[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = network.enableBroadcast ? "1" : "0"; + netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = network.allowPassiveBridging ? "1" : "0"; - responseContentType = "application/json"; - return 200; - } // else 404 + { // TODO: right now only etherTypes are supported in rules + std::vector 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()); + std::string allowedEtherTypesCsv; + for(std::vector::const_iterator i(allowedEtherTypes.begin());i!=allowedEtherTypes.end();++i) { + if (allowedEtherTypesCsv.length()) + allowedEtherTypesCsv.push_back(','); + char tmp[16]; + Utils::snprintf(tmp,sizeof(tmp),"%.4x",(unsigned int)*i); + allowedEtherTypesCsv.append(tmp); + } + netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = allowedEtherTypesCsv; + } - } else { - // List members + if (network.multicastLimit > 0) { + char ml[16]; + Utils::snprintf(ml,sizeof(ml),"%lx",(unsigned long)network.multicastLimit); + netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT] = ml; + } - sqlite3_reset(_sListNetworkMembers); - sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC); - responseBody.append("{"); - bool firstMember = true; - while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) { - responseBody.append(firstMember ? "\"" : ",\""); - firstMember = false; - responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,0)); - responseBody.append("\":"); - responseBody.append((const char *)sqlite3_column_text(_sListNetworkMembers,1)); - } - responseBody.push_back('}'); - responseContentType = "application/json"; - return 200; + { + std::string activeBridges; + 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)) { + if (activeBridges.length()) + activeBridges.push_back(','); + activeBridges.append(ab); + } + if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment + break; + } + if (activeBridges.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; + } + { + std::string relays; + 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)&&(addr)) { + if (relays.length()) + relays.push_back(','); + relays.append(node.toString()); + relays.push_back(';'); + relays.append(addr.toString()); } + } + } + if (relays.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_RELAYS] = relays; + } - } // else 404 + { + char tmp[128]; + std::string gateways; + sqlite3_reset(_sGetGateways); + sqlite3_bind_text(_sGetGateways,1,network.id,16,SQLITE_STATIC); + while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { + const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); + switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion + case 4: + Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d", + (gateways.length() > 0) ? "," : "", + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + gateways.append(tmp); + break; + case 6: + Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d", + (gateways.length() > 0) ? "," : "", + (int)ip[0], + (int)ip[1], + (int)ip[2], + (int)ip[3], + (int)ip[4], + (int)ip[5], + (int)ip[6], + (int)ip[7], + (int)ip[8], + (int)ip[9], + (int)ip[10], + (int)ip[11], + (int)ip[12], + (int)ip[13], + (int)ip[14], + (int)ip[15], + (int)sqlite3_column_int(_sGetGateways,2)); // metric + gateways.append(tmp); + break; + } + } + if (gateways.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS] = gateways; + } - } else { - // get network info - sqlite3_reset(_sGetNetworkById); - sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC); - if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { - Utils::snprintf(json,sizeof(json), - "{\n" - "\t\"nwid\": \"%s\",\n" - "\t\"controllerInstanceId\": \"%s\",\n" - "\t\"clock\": %llu,\n" - "\t\"name\": \"%s\",\n" - "\t\"private\": %s,\n" - "\t\"enableBroadcast\": %s,\n" - "\t\"allowPassiveBridging\": %s,\n" - "\t\"v4AssignMode\": \"%s\",\n" - "\t\"v6AssignMode\": \"%s\",\n" - "\t\"multicastLimit\": %d,\n" - "\t\"creationTime\": %llu,\n" - "\t\"revision\": %llu,\n" - "\t\"memberRevisionCounter\": %llu,\n" - "\t\"authorizedMemberCount\": %llu,\n" - "\t\"relays\": [", - nwids, - _instanceId.c_str(), - (unsigned long long)OSUtils::now(), - _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(), - (sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false", - (sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false", - (sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false", - _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,4)).c_str(), - _jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,5)).c_str(), - sqlite3_column_int(_sGetNetworkById,6), - (unsigned long long)sqlite3_column_int64(_sGetNetworkById,7), - (unsigned long long)sqlite3_column_int64(_sGetNetworkById,8), - (unsigned long long)sqlite3_column_int64(_sGetNetworkById,9), - (unsigned long long)sqlite3_column_int64(_sGetNetworkById,10)); - responseBody = json; + if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { + std::string v4s; - sqlite3_reset(_sGetRelays); - sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC); - bool firstRelay = true; - while (sqlite3_step(_sGetRelays) == SQLITE_ROW) { - responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t"); - firstRelay = false; - responseBody.append("{\"address\":\""); - responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0)); - responseBody.append("\",\"phyAddress\":\""); - responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1))); - responseBody.append("\"}"); - } + // Get existing IPv4 IP assignments and network routes -- keep routes in a + // vector for use in auto-assign if we need them. + std::vector< std::pair > routedNetworks; + 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 *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; - responseBody.append("],\n\t\"gateways\": ["); + const IpAssignmentType ipt = (IpAssignmentType)sqlite3_column_int(_sGetIpAssignmentsForNode,0); + switch(ipt) { + case ZT_IP_ASSIGNMENT_TYPE_ADDRESS: + haveStaticIpAssignment = true; + break; + case ZT_IP_ASSIGNMENT_TYPE_NETWORK: + routedNetworks.push_back(std::pair(Utils::ntoh(*(reinterpret_cast(ip + 12))),ipNetmaskBits)); + break; + default: + continue; + } - sqlite3_reset(_sGetGateways); - sqlite3_bind_text(_sGetGateways,1,nwids,16,SQLITE_STATIC); - bool firstGateway = true; - while (sqlite3_step(_sGetGateways) == SQLITE_ROW) { - char tmp[128]; - const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetGateways,0); - switch(sqlite3_column_int(_sGetGateways,1)) { // ipVersion - case 4: - Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d\"", - (firstGateway) ? "\"" : ",\"", - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetGateways,2)); // metric - break; - case 6: - Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"", - (firstGateway) ? "\"" : ",\"", - (int)ip[0], - (int)ip[1], - (int)ip[2], - (int)ip[3], - (int)ip[4], - (int)ip[5], - (int)ip[6], - (int)ip[7], - (int)ip[8], - (int)ip[9], - (int)ip[10], - (int)ip[11], - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetGateways,2)); // metric - break; - } - responseBody.append(tmp); - firstGateway = false; - } + // 1.0.4 or newer clients support network routes in addition to IPs. + // Older clients only support IP address / netmask entries. + if ((clientIs104)||(ipt == ZT_IP_ASSIGNMENT_TYPE_ADDRESS)) { + char tmp[32]; + Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[12],(int)ip[13],(int)ip[14],(int)ip[15],ipNetmaskBits); + if (v4s.length()) + v4s.push_back(','); + v4s.append(tmp); + } + } - responseBody.append("],\n\t\"ipLocalRoutes\": ["); + if (!haveStaticIpAssignment) { + // Attempt to auto-assign an IPv4 address from an available routed pool + sqlite3_reset(_sGetIpAssignmentPools); + sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC); + sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4 - sqlite3_reset(_sGetLocalRoutes); - sqlite3_bind_text(_sGetLocalRoutes,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_int(_sGetLocalRoutes,2,(int)ZT_IP_ASSIGNMENT_TYPE_NETWORK); - bool firstLocalRoute = true; - while (sqlite3_step(_sGetLocalRoutes) == SQLITE_ROW) { - char tmp[128]; - const unsigned char *ip = (const unsigned char *)sqlite3_column_blob(_sGetLocalRoutes,0); - switch (sqlite3_column_int(_sGetLocalRoutes,2)) { - case 4: - Utils::snprintf(tmp,sizeof(tmp),"%s%d.%d.%d.%d/%d\"", - (firstLocalRoute) ? "\"" : ",\"", - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetLocalRoutes,1)); // netmask bits - break; - case 6: - Utils::snprintf(tmp,sizeof(tmp),"%s%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x:%.2x%.2x/%d\"", - (firstLocalRoute) ? "\"" : ",\"", - (int)ip[0], - (int)ip[1], - (int)ip[2], - (int)ip[3], - (int)ip[4], - (int)ip[5], - (int)ip[6], - (int)ip[7], - (int)ip[8], - (int)ip[9], - (int)ip[10], - (int)ip[11], - (int)ip[12], - (int)ip[13], - (int)ip[14], - (int)ip[15], - (int)sqlite3_column_int(_sGetLocalRoutes,1)); // netmask bits - break; - } - responseBody.append(tmp); - firstLocalRoute = false; - } + while (sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW) { + const unsigned char *ipRangeStartB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools,0)); + const unsigned char *ipRangeEndB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools,1)); + if ((!ipRangeStartB)||(!ipRangeEndB)||(sqlite3_column_bytes(_sGetIpAssignmentPools,0) != 16)||(sqlite3_column_bytes(_sGetIpAssignmentPools,1) != 16)) + continue; - responseBody.append("],\n\t\"ipAssignmentPools\": ["); + uint32_t ipRangeStart = Utils::ntoh(*(reinterpret_cast(ipRangeStartB + 12))); + uint32_t ipRangeEnd = Utils::ntoh(*(reinterpret_cast(ipRangeEndB + 12))); + if (ipRangeEnd < ipRangeStart) + continue; + uint32_t ipRangeLen = ipRangeEnd - ipRangeStart; - sqlite3_reset(_sGetIpAssignmentPools2); - sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC); - bool firstIpAssignmentPool = true; - while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) { - const char *ipRangeStartB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools2,0)); - const char *ipRangeEndB = reinterpret_cast(sqlite3_column_blob(_sGetIpAssignmentPools2,1)); - if ((ipRangeStartB)&&(ipRangeEndB)) { - InetAddress ipps,ippe; - int ipVersion = sqlite3_column_int(_sGetIpAssignmentPools2,2); - if (ipVersion == 4) { - ipps.set((const void *)(ipRangeStartB + 12),4,0); - ippe.set((const void *)(ipRangeEndB + 12),4,0); - } else if (ipVersion == 6) { - ipps.set((const void *)ipRangeStartB,16,0); - ippe.set((const void *)ipRangeEndB,16,0); - } - if (ipps) { - responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t"); - firstIpAssignmentPool = false; - Utils::snprintf(json,sizeof(json),"{\"ipRangeStart\":\"%s\",\"ipRangeEnd\":\"%s\"}", - _jsonEscape(ipps.toIpString()).c_str(), - _jsonEscape(ippe.toIpString()).c_str()); - responseBody.append(json); + // 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; + + for(std::vector< std::pair >::const_iterator r(routedNetworks.begin());r!=routedNetworks.end();++r) { + if ((ip & (0xffffffff << (32 - r->second))) == r->first) { + // IP is included in a routed network, so check if it's allocated + + uint32_t ipBlob[4]; + 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)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)ZT_IP_ASSIGNMENT_TYPE_ADDRESS); + sqlite3_bind_blob(_sAllocateIp,4,(const void *)ipBlob,16,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,5,r->second); // IP netmask bits from matching route + sqlite3_bind_int(_sAllocateIp,6,4); // 4 == IPv4 + if (sqlite3_step(_sAllocateIp) == SQLITE_DONE) { + char tmp[32]; + Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)((ip >> 24) & 0xff),(int)((ip >> 16) & 0xff),(int)((ip >> 8) & 0xff),(int)(ip & 0xff),r->second); + if (v4s.length()) + v4s.push_back(','); + v4s.append(tmp); + haveStaticIpAssignment = true; // break outer loop + } + } + + break; // stop checking routed networks } } + + if (haveStaticIpAssignment) + break; } + } + } - responseBody.append("],\n\t\"rules\": ["); + if (v4s.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; + } - sqlite3_reset(_sListRules); - sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC); - bool firstRule = true; - while (sqlite3_step(_sListRules) == SQLITE_ROW) { - responseBody.append(firstRule ? "\n\t{\n" : ",{\n"); - firstRule = false; - Utils::snprintf(json,sizeof(json),"\t\t\"ruleNo\": %lld,\n",sqlite3_column_int64(_sListRules,0)); - responseBody.append(json); - if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"nodeId\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"sourcePort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,2)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"destPort\": \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,3)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"vlanId\": %d,\n",sqlite3_column_int(_sListRules,4)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"vlanPcp\": %d,\n",sqlite3_column_int(_sListRules,5)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"etherType\": %d,\n",sqlite3_column_int(_sListRules,6)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"macSource\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,7)).toString().c_str()); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"macDest\": \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,8)).toString().c_str()); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipSource\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,9)).c_str()); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipDest\": \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,10)).c_str()); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipTos\": %d,\n",sqlite3_column_int(_sListRules,11)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipProtocol\": %d,\n",sqlite3_column_int(_sListRules,12)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,13) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipSourcePort\": %d,\n",sqlite3_column_int(_sListRules,13)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,14) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"ipDestPort\": %d,\n",sqlite3_column_int(_sListRules,14)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,15) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"flags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,15)); - responseBody.append(json); - } - if (sqlite3_column_type(_sListRules,16) != SQLITE_NULL) { - Utils::snprintf(json,sizeof(json),"\t\t\"invFlags\": %lu,\n",(unsigned long)sqlite3_column_int64(_sListRules,16)); - responseBody.append(json); - } - responseBody.append("\t\t\"action\": \""); - responseBody.append(_jsonEscape( (sqlite3_column_type(_sListRules,17) == SQLITE_NULL) ? "drop" : (const char *)sqlite3_column_text(_sListRules,17) )); - responseBody.append("\"\n\t}"); - } + // TODO: IPv6 auto-assign once it's supported in UI - responseBody.append("]\n}\n"); - responseContentType = "application/json"; - return 200; - } // else 404 - } - } else if (path.size() == 1) { - // list networks - sqlite3_reset(_sListNetworks); - responseContentType = "application/json"; - responseBody = "["; - bool first = true; - while (sqlite3_step(_sListNetworks) == SQLITE_ROW) { - if (first) { - first = false; - responseBody.push_back('"'); - } else responseBody.append(",\""); - responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0)); - responseBody.push_back('"'); + if (network.isPrivate) { + CertificateOfMembership com(OSUtils::now(),ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); + if (com.sign(signingId)) // basically can't fail unless our identity is invalid + netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); + else { + netconf["error"] = "unable to sign COM"; + return NETCONF_QUERY_INTERNAL_SERVER_ERROR; } - responseBody.push_back(']'); - return 200; - } // else 404 + } - } else { - // GET /controller returns status and API version if controller is supported - Utils::snprintf(json,sizeof(json),"{\n\t\"controller\": true,\n\t\"apiVersion\": %d,\n\t\"clock\": %llu,\n\t\"instanceId\": \"%s\"\n}\n",ZT_NETCONF_CONTROLLER_API_VERSION,(unsigned long long)OSUtils::now(),_instanceId.c_str()); - responseBody = json; - responseContentType = "application/json"; - return 200; + if (!netconf.sign(signingId,OSUtils::now())) { + netconf["error"] = "unable to sign netconf dictionary"; + return NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } } - return 404; + return NetworkController::NETCONF_QUERY_OK; } } // namespace ZeroTier diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index ce4a512c..5d995379 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -93,6 +93,13 @@ private: const std::string &body, std::string &responseBody, std::string &responseContentType); + NetworkController::ResultCode _doNetworkConfigRequest( + const InetAddress &fromAddr, + const Identity &signingId, + const Identity &identity, + uint64_t nwid, + const Dictionary &metaData, + Dictionary &netconf); std::string _dbPath; std::string _instanceId; -- cgit v1.2.3 From a35fa7ac932ff93c9a9a70ff3f57eea5e6a9b19a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Thu, 10 Sep 2015 15:14:10 -0700 Subject: Add expansion of netconf in _test field. --- controller/SqliteNetworkController.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 0905c99f..7098a027 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1007,7 +1007,18 @@ unsigned int SqliteNetworkController::_doCPGet( Utils::snprintf(rcs,sizeof(rcs),"%d,\n",(int)rc); testFields.append("\t\"_test\": {\n"); testFields.append("\t\t\"resultCode\": "); testFields.append(rcs); - testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\""); + testFields.append("\t\t\"result\": \""); testFields.append(_jsonEscape(testNetconf.toString().c_str()).c_str()); testFields.append("\",\n"); + testFields.append("\t\t\"resultJson\": {\n"); + for(Dictionary::const_iterator i(testNetconf.begin());i!=testNetconf.end();++i) { + if (i != testNetconf.begin()) + testFields.append(",\n"); + testFields.append("\t\t\t\""); + testFields.append(i->first); + testFields.append("\": \""); + testFields.append(_jsonEscape(i->second.c_str())); + testFields.push_back('"'); + } + testFields.append("\n\t\t}\n"); testFields.append("\t}\n"); } } -- cgit v1.2.3 From cd005341c5b6a81f6b23129041828c31d432c5a9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 11 Sep 2015 15:02:26 -0700 Subject: Extra statement to clean up Members -- cascade did not seem to work, possibly due to dual key. --- controller/SqliteNetworkController.cpp | 9 ++++++++- controller/SqliteNetworkController.hpp | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 7098a027..f7d95666 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -203,6 +203,7 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"UPDATE Member SET authorized = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberAuthorized,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"UPDATE Member SET activeBridge = ?,memberRevision = (SELECT memberRevisionCounter FROM Network WHERE id = ?) WHERE rowid = ?",-1,&_sUpdateMemberActiveBridge,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sDeleteMember,(const char **)0) != SQLITE_OK) + ||(sqlite3_prepare_v2(_db,"DELETE FROM Member WHERE networkId = ?",-1,&_sDeleteAllNetworkMembers,(const char **)0) != SQLITE_OK) /* Gateway */ ||(sqlite3_prepare_v2(_db,"SELECT \"ip\",ipVersion,metric FROM Gateway WHERE networkId = ? ORDER BY metric ASC",-1,&_sGetGateways,(const char **)0) != SQLITE_OK) @@ -287,6 +288,7 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sUpdateMemberAuthorized); sqlite3_finalize(_sUpdateMemberActiveBridge); sqlite3_finalize(_sDeleteMember); + sqlite3_finalize(_sDeleteAllNetworkMembers); sqlite3_finalize(_sDeleteNetwork); sqlite3_finalize(_sGetGateways); sqlite3_finalize(_sDeleteGateways); @@ -932,7 +934,12 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE( sqlite3_reset(_sDeleteNetwork); sqlite3_bind_text(_sDeleteNetwork,1,nwids,16,SQLITE_STATIC); - return ((sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) ? 200 : 500); + if (sqlite3_step(_sDeleteNetwork) == SQLITE_DONE) { + sqlite3_reset(_sDeleteAllNetworkMembers); + sqlite3_bind_text(_sDeleteAllNetworkMembers,1,nwids,16,SQLITE_STATIC); + sqlite3_step(_sDeleteAllNetworkMembers); + return 200; + } else return 500; } } // else 404 diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 5d995379..23e16c4b 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -143,6 +143,7 @@ private: sqlite3_stmt *_sUpdateMemberAuthorized; sqlite3_stmt *_sUpdateMemberActiveBridge; sqlite3_stmt *_sDeleteMember; + sqlite3_stmt *_sDeleteAllNetworkMembers; sqlite3_stmt *_sDeleteNetwork; sqlite3_stmt *_sGetGateways; sqlite3_stmt *_sDeleteGateways; -- cgit v1.2.3 From ef316ced3bf52434eab34490b3f12b8d080c9252 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 14 Sep 2015 11:59:43 -0700 Subject: Fix JSON. --- controller/SqliteNetworkController.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index f7d95666..e1e6c9cd 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1026,7 +1026,7 @@ unsigned int SqliteNetworkController::_doCPGet( testFields.push_back('"'); } testFields.append("\n\t\t}\n"); - testFields.append("\t}\n"); + testFields.append("\t},\n"); } } -- cgit v1.2.3 From 610ab0750ce3f104663577663d4b5883906c9641 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 15 Sep 2015 10:59:23 -0700 Subject: Drop Sqlite-based Log table for now and switch to an in-memory log for recent activity. Log table gets too big on busy nodes. Should probably support push of events to some kind of event system later. --- controller/SqliteNetworkController.cpp | 105 +++++++++++++++------------------ controller/SqliteNetworkController.hpp | 35 +++++++++-- controller/schema.sql | 13 ---- controller/schema.sql.c | 13 ---- 4 files changed, 77 insertions(+), 89 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index e1e6c9cd..89878c27 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -210,11 +210,6 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) : ||(sqlite3_prepare_v2(_db,"DELETE FROM Gateway WHERE networkId = ?",-1,&_sDeleteGateways,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"INSERT INTO Gateway (networkId,\"ip\",ipVersion,metric) VALUES (?,?,?,?)",-1,&_sCreateGateway,(const char **)0) != SQLITE_OK) - /* Log */ - ||(sqlite3_prepare_v2(_db,"INSERT INTO \"Log\" (networkId,nodeId,\"ts\",\"authorized\",\"version\",fromAddr) VALUES (?,?,?,?,?,?)",-1,&_sPutLog,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? AND \"ts\" >= ? ORDER BY \"ts\" ASC",-1,&_sGetMemberLog,(const char **)0) != SQLITE_OK) - ||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? ORDER BY \"ts\" DESC LIMIT 10",-1,&_sGetRecentMemberLog,(const char **)0) != SQLITE_OK) - /* Config */ ||(sqlite3_prepare_v2(_db,"SELECT \"v\" FROM \"Config\" WHERE \"k\" = ?",-1,&_sGetConfig,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK) @@ -296,9 +291,6 @@ SqliteNetworkController::~SqliteNetworkController() sqlite3_finalize(_sIncrementMemberRevisionCounter); sqlite3_finalize(_sGetConfig); sqlite3_finalize(_sSetConfig); - sqlite3_finalize(_sPutLog); - sqlite3_finalize(_sGetMemberLog); - sqlite3_finalize(_sGetRecentMemberLog); sqlite3_close(_db); } } @@ -1074,28 +1066,35 @@ unsigned int SqliteNetworkController::_doCPGet( responseBody.append("],\n\t\"recentLog\": ["); - sqlite3_reset(_sGetRecentMemberLog); - sqlite3_bind_text(_sGetRecentMemberLog,1,nwids,16,SQLITE_STATIC); - sqlite3_bind_text(_sGetRecentMemberLog,2,addrs,10,SQLITE_STATIC); - bool firstLog = true; - while (sqlite3_step(_sGetRecentMemberLog) == SQLITE_ROW) { - responseBody.append(firstLog ? "{" : ",{"); - firstLog = false; - responseBody.append("\"ts\":"); - responseBody.append(reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,0))); - responseBody.append((sqlite3_column_int(_sGetRecentMemberLog,1) == 0) ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":"); - const char *ver = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,2)); - if ((ver)&&(ver[0])) { - responseBody.push_back('"'); - responseBody.append(_jsonEscape(ver)); - responseBody.append("\",\"fromAddr\":"); - } else responseBody.append("null,\"fromAddr\":"); - const char *fa = reinterpret_cast(sqlite3_column_text(_sGetRecentMemberLog,3)); - if ((fa)&&(fa[0])) { - responseBody.push_back('"'); - responseBody.append(_jsonEscape(fa)); - responseBody.append("\"}"); - } else responseBody.append("null}"); + { + std::map< std::pair,_LLEntry >::const_iterator lli(_lastLog.find(std::pair(Address(address),nwid))); + if (lli != _lastLog.end()) { + const _LLEntry &lastLogEntry = lli->second; + uint64_t eptr = lastLogEntry.totalRequests; + for(int k=0;k(identity.address(),nwid)]; - uint64_t lrt2 = lrt; - if (((lrt = OSUtils::now()) - lrt2) <= ZT_NETCONF_MIN_REQUEST_PERIOD) - return NetworkController::NETCONF_QUERY_IGNORE; - } + // Check rate limit circuit breaker to prevent flooding + const uint64_t now = OSUtils::now(); + _LLEntry &lastLogEntry = _lastLog[std::pair(identity.address(),nwid)]; + if ((now - lastLogEntry.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD) + return NetworkController::NETCONF_QUERY_IGNORE; + lastLogEntry.lastRequestTime = now; NetworkRecord network; memset(&network,0,sizeof(network)); @@ -1523,28 +1520,18 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c sqlite3_step(_sIncrementMemberRevisionCounter); } - // Add log entry + // Add log entry to in-memory circular log + { - char ver[16]; - std::string fa; - if (fromAddr) { - fa = fromAddr.toString(); - if (fa.length() > 64) - fa = fa.substr(0,64); - } - sqlite3_reset(_sPutLog); - sqlite3_bind_text(_sPutLog,1,network.id,16,SQLITE_STATIC); - sqlite3_bind_text(_sPutLog,2,member.nodeId,10,SQLITE_STATIC); - sqlite3_bind_int64(_sPutLog,3,(long long)OSUtils::now()); - sqlite3_bind_int(_sPutLog,4,member.authorized ? 1 : 0); - if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) { - Utils::snprintf(ver,sizeof(ver),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); - sqlite3_bind_text(_sPutLog,5,ver,-1,SQLITE_STATIC); - } else sqlite3_bind_null(_sPutLog,5); - if (fa.length() > 0) - sqlite3_bind_text(_sPutLog,6,fa.c_str(),-1,SQLITE_STATIC); - else sqlite3_bind_null(_sPutLog,6); - sqlite3_step(_sPutLog); + const unsigned long ptr = (unsigned long)lastLogEntry.totalRequests % ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE; + lastLogEntry.l[ptr].ts = now; + lastLogEntry.l[ptr].fromAddr = fromAddr; + if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) + Utils::snprintf(lastLogEntry.l[ptr].version,sizeof(lastLogEntry.l[ptr].version),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision); + else lastLogEntry.l[ptr].version[0] = (char)0; + lastLogEntry.l[ptr].authorized = member.authorized; + ++lastLogEntry.totalRequests; + // TODO: push or save these somewhere } // Check member authorization diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp index 23e16c4b..f0b61c40 100644 --- a/controller/SqliteNetworkController.hpp +++ b/controller/SqliteNetworkController.hpp @@ -40,6 +40,9 @@ #include "../node/NetworkController.hpp" #include "../node/Mutex.hpp" +// Number of in-memory last log entries to maintain per user +#define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32 + namespace ZeroTier { class SqliteNetworkController : public NetworkController @@ -104,7 +107,34 @@ private: std::string _dbPath; std::string _instanceId; - std::map< std::pair,uint64_t > _lastRequestTime; + // A circular buffer last log + struct _LLEntry + { + _LLEntry() + { + for(long i=0;il[i].ts = 0; + this->lastRequestTime = 0; + this->totalRequests = 0; + } + + // Circular buffer of last log entries + struct { + uint64_t ts; // timestamp or 0 if circular buffer entry unused + char version[64]; + InetAddress fromAddr; + bool authorized; + } l[ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE]; + + // Time of last request whether successful or not + uint64_t lastRequestTime; + + // Total requests by this address / network ID pair (also serves mod IN_MEMORY_LOG_SIZE as circular buffer ptr) + uint64_t totalRequests; + }; + + // Last log entries by address and network ID pair + std::map< std::pair,_LLEntry > _lastLog; sqlite3 *_db; @@ -151,9 +181,6 @@ private: sqlite3_stmt *_sIncrementMemberRevisionCounter; sqlite3_stmt *_sGetConfig; sqlite3_stmt *_sSetConfig; - sqlite3_stmt *_sPutLog; - sqlite3_stmt *_sGetMemberLog; - sqlite3_stmt *_sGetRecentMemberLog; Mutex _lock; }; diff --git a/controller/schema.sql b/controller/schema.sql index 48d5607c..b6db7fa4 100644 --- a/controller/schema.sql +++ b/controller/schema.sql @@ -77,19 +77,6 @@ CREATE TABLE Member ( CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge); CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision); -CREATE TABLE Log ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - ts integer NOT NULL, - authorized integer NOT NULL, - authTokenId integer, - version varchar(16), - fromAddr varchar(64) -); - -CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId); -CREATE INDEX Log_ts ON Log(ts); - CREATE TABLE Relay ( networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE, address char(10) NOT NULL, diff --git a/controller/schema.sql.c b/controller/schema.sql.c index 49c44548..a5b9130b 100644 --- a/controller/schema.sql.c +++ b/controller/schema.sql.c @@ -78,19 +78,6 @@ "CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\ "CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);\n"\ "\n"\ -"CREATE TABLE Log (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" ts integer NOT NULL,\n"\ -" authorized integer NOT NULL,\n"\ -" authTokenId integer,\n"\ -" version varchar(16),\n"\ -" fromAddr varchar(64)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId);\n"\ -"CREATE INDEX Log_ts ON Log(ts);\n"\ -"\n"\ "CREATE TABLE Relay (\n"\ " networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\ " address char(10) NOT NULL,\n"\ -- cgit v1.2.3 From ddf3d1f94932738d058aa57ce2fa11f3c9bde089 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 18 Sep 2015 13:35:00 -0700 Subject: Controller side support for IPv6 assignment. --- controller/SqliteNetworkController.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'controller/SqliteNetworkController.cpp') diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp index 89878c27..d16c5996 100644 --- a/controller/SqliteNetworkController.cpp +++ b/controller/SqliteNetworkController.cpp @@ -1544,7 +1544,7 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c netconf.clear(); { char tss[24],rs[24]; - Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)OSUtils::now()); + Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)now); Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision); netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs; @@ -1788,10 +1788,13 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; } - // TODO: IPv6 auto-assign once it's supported in UI + if ((network.v6AssignMode)&&(!strcmp(network.v6AssignMode,"rfc4193"))) { + InetAddress rfc4193Addr(InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt())); + netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = rfc4193Addr.toString(); + } if (network.isPrivate) { - CertificateOfMembership com(OSUtils::now(),ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); + CertificateOfMembership com(now,ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address()); if (com.sign(signingId)) // basically can't fail unless our identity is invalid netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString(); else { @@ -1800,7 +1803,7 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c } } - if (!netconf.sign(signingId,OSUtils::now())) { + if (!netconf.sign(signingId,now)) { netconf["error"] = "unable to sign netconf dictionary"; return NETCONF_QUERY_INTERNAL_SERVER_ERROR; } -- cgit v1.2.3