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

This commit is contained in:
Adam Ierymenko 2015-04-17 15:21:53 -07:00
parent 417f56de2f
commit 740121504f
5 changed files with 66 additions and 27 deletions

View File

@ -37,6 +37,9 @@
#include <utility>
#include <stdexcept>
#include "../include/ZeroTierOne.h"
#include "../node/Constants.hpp"
#include "SqliteNetworkController.hpp"
#include "../node/Utils.hpp"
#include "../node/CertificateOfMembership.hpp"
@ -52,6 +55,9 @@
#define ZT_NETCONF_SQLITE_SCHEMA_VERSION 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 {
SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
@ -63,10 +69,11 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
sqlite3_busy_timeout(_db,10000);
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;
if (sqlite3_step(s) == SQLITE_ROW)
if (sqlite3_step(s) == SQLITE_ROW) {
schemaVersion = sqlite3_column_int(s,0);
}
sqlite3_finalize(s);
@ -88,22 +95,22 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
}
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);
throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements");
@ -166,6 +173,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
int cachedNetconfBytes;
const void *cachedNetconf;
uint64_t cachedNetconfRevision;
uint64_t cachedNetconfTimestamp;
uint64_t clientReportedRevision;
bool authorized;
bool activeBridge;
@ -252,9 +260,10 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
member.cachedNetconfBytes = sqlite3_column_bytes(_sGetMemberByNetworkAndNodeId,1);
member.cachedNetconf = sqlite3_column_blob(_sGetMemberByNetworkAndNodeId,1);
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
@ -297,7 +306,9 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
// Generate or retrieve cached netconf
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
std::string tmp((const char *)member.cachedNetconf,member.cachedNetconfBytes);
netconf.fromString(tmp);
@ -450,7 +461,7 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
// TODO: IPv6 auto-assign once it's supported in UI
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
netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
else {

View File

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

View File

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

View File

@ -103,6 +103,17 @@ extern "C" {
*/
#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
*/

View File

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