浏览代码

Add a timestamp to netconf cache, fix some SQL queries in NC.

Adam Ierymenko 10 年之前
父节点
当前提交
740121504f
共有 5 个文件被更改,包括 66 次插入27 次删除
  1. 34 23
      controller/SqliteNetworkController.cpp
  2. 1 0
      controller/schema.sql
  3. 1 0
      controller/schema.sql.c
  4. 11 0
      include/ZeroTierOne.h
  5. 19 4
      selftest.cpp

+ 34 - 23
controller/SqliteNetworkController.cpp

@@ -37,6 +37,9 @@
 #include <utility>
 #include <utility>
 #include <stdexcept>
 #include <stdexcept>
 
 
+#include "../include/ZeroTierOne.h"
+#include "../node/Constants.hpp"
+
 #include "SqliteNetworkController.hpp"
 #include "SqliteNetworkController.hpp"
 #include "../node/Utils.hpp"
 #include "../node/Utils.hpp"
 #include "../node/CertificateOfMembership.hpp"
 #include "../node/CertificateOfMembership.hpp"
@@ -52,6 +55,9 @@
 #define ZT_NETCONF_SQLITE_SCHEMA_VERSION 1
 #define ZT_NETCONF_SQLITE_SCHEMA_VERSION 1
 #define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "1"
 #define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "1"
 
 
+// Maximum age in ms for a cached netconf before we regenerate anyway (one hour)
+#define ZT_CACHED_NETCONF_MAX_AGE (60 * 60 * 1000)
+
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
 SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
@@ -63,10 +69,11 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
 	sqlite3_busy_timeout(_db,10000);
 	sqlite3_busy_timeout(_db,10000);
 
 
 	sqlite3_stmt *s = (sqlite3_stmt *)0;
 	sqlite3_stmt *s = (sqlite3_stmt *)0;
-	if ((sqlite3_prepare_v2(_db,"SELECT 'v' FROM Config WHERE 'k' = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) {
+	if ((sqlite3_prepare_v2(_db,"SELECT v FROM Config WHERE k = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) {
 		int schemaVersion = -1234;
 		int schemaVersion = -1234;
-		if (sqlite3_step(s) == SQLITE_ROW)
+		if (sqlite3_step(s) == SQLITE_ROW) {
 			schemaVersion = sqlite3_column_int(s,0);
 			schemaVersion = sqlite3_column_int(s,0);
+		}
 
 
 		sqlite3_finalize(s);
 		sqlite3_finalize(s);
 
 
@@ -88,22 +95,22 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
 	}
 	}
 
 
 	if (
 	if (
-			  (sqlite3_prepare_v2(_db,"SELECT 'name','private','enableBroadcast','allowPassiveBridging','v4AssignMode','v6AssignMode','multicastLimit','revision' FROM Network WHERE 'id' = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT rowid,'cachedNetconf','cachedNetconfRevision','clientReportedRevision','authorized','activeBridge' FROM Member WHERE 'networkId' = ? AND 'nodeId' = ?",-1,&_sGetMemberByNetworkAndNodeId,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"INSERT INTO Member ('networkId','nodeId','cachedNetconfRevision','clientReportedRevision','authorized','activeBridge') VALUES (?,?,0,0,?,0)",-1,&_sCreateMember,(const char **)0) != SQLITE_OK)
-			||(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','lastAt','lastSeen','firstSeen') VALUES (?,?,?,?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"UPDATE Node SET 'lastAt' = ?,'lastSeen' = ? WHERE 'id' = ?",-1,&_sUpdateNode,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"UPDATE Node SET 'lastSeen' = ? WHERE 'id' = ?",-1,&_sUpdateNode2,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"UPDATE Member SET 'clientReportedRevision' = ? WHERE rowid = ?",-1,&_sUpdateMemberClientReportedRevision,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT 'etherType' FROM Rule WHERE 'networkId' = ? AND 'action' = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT 'mgMac','mgAdi','preload','maxBalance','accrual' FROM MulticastRate WHERE 'networkId' = ?",-1,&_sGetMulticastRates,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT 'nodeId' FROM Member WHERE 'networkId' = ? AND 'authorized' > 0 AND 'activeBridge' > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT DISTINCT 'ip','ipNetmaskBits' FROM IpAssignment WHERE 'networkId' = ? AND 'nodeId' = ? AND 'ipVersion' = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT DISTINCT 'ipNetwork','ipNetmaskBits' FROM IpAssignmentPool WHERE 'networkId' = ? AND 'ipVersion' = ? AND 'active' > 0",-1,&_sGetIpAssignmentPools,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"SELECT 1 FROM IpAssignment WHERE 'networkId' = ? AND 'ip' = ? AND 'ipVersion' = ?",-1,&_sCheckIfIpIsAllocated,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignment ('networkId','nodeId','ip','ipNetmaskBits','ipVersion') VALUES (?,?,?,?,?)",-1,&_sAllocateIp,(const char **)0) != SQLITE_OK)
-			||(sqlite3_prepare_v2(_db,"UPDATE Member SET 'cachedNetconf' = ?,'cachedNetconfRevision' = ? WHERE rowid = ?",-1,&_sCacheNetconf,(const char **)0) != SQLITE_OK)
+			  (sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,v4AssignMode,v6AssignMode,multicastLimit,revision FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT rowid,cachedNetconf,cachedNetconfRevision,cachedNetconfTimestamp,clientReportedRevision,authorized,activeBridge FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sGetMemberByNetworkAndNodeId,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"INSERT INTO Member (networkId,nodeId,cachedNetconfRevision,clientReportedRevision,authorized,activeBridge) VALUES (?,?,0,0,?,0)",-1,&_sCreateMember,(const char **)0) != SQLITE_OK)
+			||(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,lastAt,lastSeen,firstSeen) VALUES (?,?,?,?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"UPDATE Node SET lastAt = ?,lastSeen = ? WHERE id = ?",-1,&_sUpdateNode,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"UPDATE Node SET lastSeen = ? WHERE id = ?",-1,&_sUpdateNode2,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"UPDATE Member SET clientReportedRevision = ? WHERE rowid = ?",-1,&_sUpdateMemberClientReportedRevision,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT etherType FROM Rule WHERE networkId = ? AND \"action\" = 'accept'",-1,&_sGetEtherTypesFromRuleTable,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT mgMac,mgAdi,preload,maxBalance,accrual FROM MulticastRate WHERE networkId = ?",-1,&_sGetMulticastRates,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT nodeId FROM Member WHERE networkId = ? AND authorized > 0 AND activeBridge > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT DISTINCT ip,ipNetmaskBits FROM IpAssignment WHERE networkId = ? AND nodeId = ? AND ipVersion = ?",-1,&_sGetIpAssignmentsForNode,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT DISTINCT ipNetwork,ipNetmaskBits FROM IpAssignmentPool WHERE networkId = ? AND ipVersion = ? AND active > 0",-1,&_sGetIpAssignmentPools,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"SELECT 1 FROM IpAssignment WHERE networkId = ? AND ip = ? AND ipVersion = ?",-1,&_sCheckIfIpIsAllocated,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignment (networkId,nodeId,ip,ipNetmaskBits,ipVersion) VALUES (?,?,?,?,?)",-1,&_sAllocateIp,(const char **)0) != SQLITE_OK)
+			||(sqlite3_prepare_v2(_db,"UPDATE Member SET cachedNetconf = ?,cachedNetconfRevision = ? WHERE rowid = ?",-1,&_sCacheNetconf,(const char **)0) != SQLITE_OK)
 		 ) {
 		 ) {
 		sqlite3_close(_db);
 		sqlite3_close(_db);
 		throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements");
 		throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements");
@@ -166,6 +173,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
 		int cachedNetconfBytes;
 		int cachedNetconfBytes;
 		const void *cachedNetconf;
 		const void *cachedNetconf;
 		uint64_t cachedNetconfRevision;
 		uint64_t cachedNetconfRevision;
+		uint64_t cachedNetconfTimestamp;
 		uint64_t clientReportedRevision;
 		uint64_t clientReportedRevision;
 		bool authorized;
 		bool authorized;
 		bool activeBridge;
 		bool activeBridge;
@@ -252,9 +260,10 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
 		member.cachedNetconfBytes = sqlite3_column_bytes(_sGetMemberByNetworkAndNodeId,1);
 		member.cachedNetconfBytes = sqlite3_column_bytes(_sGetMemberByNetworkAndNodeId,1);
 		member.cachedNetconf = sqlite3_column_blob(_sGetMemberByNetworkAndNodeId,1);
 		member.cachedNetconf = sqlite3_column_blob(_sGetMemberByNetworkAndNodeId,1);
 		member.cachedNetconfRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,2);
 		member.cachedNetconfRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,2);
-		member.clientReportedRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,3);
-		member.authorized = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,4) > 0);
-		member.activeBridge = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,5) > 0);
+		member.cachedNetconfTimestamp = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,3);
+		member.clientReportedRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,4);
+		member.authorized = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,5) > 0);
+		member.activeBridge = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,6) > 0);
 	}
 	}
 
 
 	// Create Member record for unknown nodes, auto-authorizing if network is public
 	// Create Member record for unknown nodes, auto-authorizing if network is public
@@ -297,7 +306,9 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
 	// Generate or retrieve cached netconf
 	// Generate or retrieve cached netconf
 
 
 	netconf.clear();
 	netconf.clear();
-	if ((member.cachedNetconfRevision == network.revision)&&(member.cachedNetconfBytes > 0)) {
+	if ( (member.cachedNetconfBytes > 0)&&
+		   (member.cachedNetconfRevision == network.revision)&&
+		   ((OSUtils::now() - member.cachedNetconfTimestamp) < ZT_CACHED_NETCONF_MAX_AGE) ) {
 		// Use cached copy
 		// Use cached copy
 		std::string tmp((const char *)member.cachedNetconf,member.cachedNetconfBytes);
 		std::string tmp((const char *)member.cachedNetconf,member.cachedNetconfBytes);
 		netconf.fromString(tmp);
 		netconf.fromString(tmp);
@@ -450,7 +461,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
 		// TODO: IPv6 auto-assign once it's supported in UI
 		// TODO: IPv6 auto-assign once it's supported in UI
 
 
 		if (network.isPrivate) {
 		if (network.isPrivate) {
-			CertificateOfMembership com(network.revision,16,nwid,identity.address());
+			CertificateOfMembership com(network.revision,ZT1_CERTIFICATE_OF_MEMBERSHIP_REVISION_MAX_DELTA,nwid,identity.address());
 			if (com.sign(signingId)) // basically can't fail unless our identity is invalid
 			if (com.sign(signingId)) // basically can't fail unless our identity is invalid
 				netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
 				netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
 			else {
 			else {

+ 1 - 0
controller/schema.sql

@@ -32,6 +32,7 @@ CREATE TABLE Member (
   nodeId char(10) NOT NULL,
   nodeId char(10) NOT NULL,
   cachedNetconf blob(4096),
   cachedNetconf blob(4096),
   cachedNetconfRevision integer NOT NULL DEFAULT(0),
   cachedNetconfRevision integer NOT NULL DEFAULT(0),
+  cachedNetconfTimestamp integer NOT NULL DEFAULT(0),
   clientReportedRevision integer NOT NULL DEFAULT(0),
   clientReportedRevision integer NOT NULL DEFAULT(0),
   authorized integer NOT NULL DEFAULT(0),
   authorized integer NOT NULL DEFAULT(0),
   activeBridge integer NOT NULL DEFAULT(0)
   activeBridge integer NOT NULL DEFAULT(0)

+ 1 - 0
controller/schema.sql.c

@@ -33,6 +33,7 @@
 "  nodeId char(10) NOT NULL,\n"\
 "  nodeId char(10) NOT NULL,\n"\
 "  cachedNetconf blob(4096),\n"\
 "  cachedNetconf blob(4096),\n"\
 "  cachedNetconfRevision integer NOT NULL DEFAULT(0),\n"\
 "  cachedNetconfRevision integer NOT NULL DEFAULT(0),\n"\
+"  cachedNetconfTimestamp integer NOT NULL DEFAULT(0),\n"\
 "  clientReportedRevision integer NOT NULL DEFAULT(0),\n"\
 "  clientReportedRevision integer NOT NULL DEFAULT(0),\n"\
 "  authorized integer NOT NULL DEFAULT(0),\n"\
 "  authorized integer NOT NULL DEFAULT(0),\n"\
 "  activeBridge integer NOT NULL DEFAULT(0)\n"\
 "  activeBridge integer NOT NULL DEFAULT(0)\n"\

+ 11 - 0
include/ZeroTierOne.h

@@ -103,6 +103,17 @@ extern "C" {
  */
  */
 #define ZT1_MAX_PEER_NETWORK_PATHS 4
 #define ZT1_MAX_PEER_NETWORK_PATHS 4
 
 
+/**
+ * Maximum number of revisions over which a network COM can differ and still be in-horizon (agree)
+ *
+ * This is the default max delta for the revision field in COMs issued
+ * by network controllers, and is defined here for documentation purposes.
+ * When a network is changed so as to de-authorize a member, its revision
+ * should be incremented by this number. Otherwise all other changes that
+ * materially affect the network should result in increment by one.
+ */
+#define ZT1_CERTIFICATE_OF_MEMBERSHIP_REVISION_MAX_DELTA 16
+
 /**
 /**
  * Feature flag: ZeroTier One was built to be thread-safe -- concurrent processXXX() calls are okay
  * Feature flag: ZeroTier One was built to be thread-safe -- concurrent processXXX() calls are okay
  */
  */

+ 19 - 4
selftest.cpp

@@ -720,13 +720,25 @@ static int testPhy()
 static int testSqliteNetworkController()
 static int testSqliteNetworkController()
 {
 {
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
+
+	OSUtils::rm("./selftest_network_controller.db");
+
 	try {
 	try {
 		std::cout << "[network-controller] Generating signing identity..." << std::endl;
 		std::cout << "[network-controller] Generating signing identity..." << std::endl;
 		Identity signingId;
 		Identity signingId;
 		signingId.generate();
 		signingId.generate();
 
 
-		std::cout << "[network-controller] Creating database..." << std::endl;
-		SqliteNetworkController controller(signingId,"network-controller-test.db");
+		{
+			std::cout << "[network-controller] Creating database..." << std::endl;
+			SqliteNetworkController controller("./selftest_network_controller.db");
+			std::cout << "[network-controller] Closing database..." << std::endl;
+		}
+
+		{
+			std::cout << "[network-controller] Re-opening database..." << std::endl;
+			SqliteNetworkController controller("./selftest_network_controller.db");
+			std::cout << "[network-controller] Closing database..." << std::endl;
+		}
 	} catch (std::runtime_error &exc) {
 	} catch (std::runtime_error &exc) {
 		std::cout << "FAIL! (unexpected exception: " << exc.what() << ")" << std::endl;
 		std::cout << "FAIL! (unexpected exception: " << exc.what() << ")" << std::endl;
 		return -1;
 		return -1;
@@ -734,6 +746,9 @@ static int testSqliteNetworkController()
 		std::cout << "FAIL! (unexpected exception: ...)" << std::endl;
 		std::cout << "FAIL! (unexpected exception: ...)" << std::endl;
 		return -1;
 		return -1;
 	}
 	}
+
+	OSUtils::rm("./selftest_network_controller.db");
+
 #endif // ZT_ENABLE_NETWORK_CONTROLLER
 #endif // ZT_ENABLE_NETWORK_CONTROLLER
 	return 0;
 	return 0;
 }
 }
@@ -818,8 +833,8 @@ int main(int argc,char **argv)
 
 
 	srand((unsigned int)time(0));
 	srand((unsigned int)time(0));
 
 
-	r |= testPhy();
-	r |= testHttp();
+	//r |= testPhy();
+	//r |= testHttp();
 	r |= testSqliteNetworkController();
 	r |= testSqliteNetworkController();
 	r |= testCrypto();
 	r |= testCrypto();
 	r |= testPacket();
 	r |= testPacket();