From a8a92c5b89b51f6786d6e5cb41a588197b6afabb Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 18 Mar 2015 16:10:48 -0700 Subject: A whole lot of Sqlite netconf master work, and some fixes elsewhere in the code. --- netconf/SqliteNetworkConfigMaster.cpp | 684 +++++++++++++++++----------------- netconf/SqliteNetworkConfigMaster.hpp | 24 +- netconf/netconf-schema.sql | 62 +-- netconf/netconf-schema.sql.c | 60 +-- node/Identity.cpp | 3 + node/MulticastGroup.hpp | 2 +- node/NetworkConfig.cpp | 2 + node/NetworkConfig.hpp | 3 + node/NetworkConfigMaster.hpp | 6 +- 9 files changed, 445 insertions(+), 401 deletions(-) diff --git a/netconf/SqliteNetworkConfigMaster.cpp b/netconf/SqliteNetworkConfigMaster.cpp index 88d96c2a..7d8cf3be 100644 --- a/netconf/SqliteNetworkConfigMaster.cpp +++ b/netconf/SqliteNetworkConfigMaster.cpp @@ -56,8 +56,7 @@ namespace ZeroTier { SqliteNetworkConfigMaster::SqliteNetworkConfigMaster(const Identity &signingId,const char *dbPath) : _signingId(signingId), _dbPath(dbPath), - _db((sqlite3 *)0), - _lock() + _db((sqlite3 *)0) { if (!_signingId.hasPrivate()) throw std::runtime_error("SqliteNetworkConfigMaster signing identity must have a private key"); @@ -66,396 +65,415 @@ SqliteNetworkConfigMaster::SqliteNetworkConfigMaster(const Identity &signingId,c throw std::runtime_error("SqliteNetworkConfigMaster cannot open database file"); sqlite3_busy_timeout(_db,10000); - sqlite3_stmt *s; - for(int k=0;k<2;++k) { - 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_exec(_db,ZT_NETCONF_SCHEMA_SQL"INSERT INTO Config (k,v) VALUES ('schemaVersion',"ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR");",0,0,0) != SQLITE_OK) { - sqlite3_close(_db); - throw std::runtime_error("SqliteNetworkConfigMaster cannot initialize database and/or insert schemaVersion into Config table"); - } else { - // Initialized database and set schema version, so we are done. - return; - } - } else break; - } - if (!s) { - sqlite3_close(_db); - throw std::runtime_error("SqliteNetworkConfigMaster unable to create prepared statement or initialize database"); + 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)) { + int schemaVersion = -1234; + if (sqlite3_step(s) == SQLITE_ROW) + schemaVersion = sqlite3_column_int(s,0); + + sqlite3_finalize(s); + + if (schemaVersion == -1234) { + sqlite3_close(_db); + throw std::runtime_error("SqliteNetworkConfigMaster schemaVersion not found in Config table (init failure?)"); + } else if (schemaVersion != ZT_NETCONF_SQLITE_SCHEMA_VERSION) { + // Note -- this will eventually run auto-upgrades so this isn't how it'll work going forward + sqlite3_close(_db); + throw std::runtime_error("SqliteNetworkConfigMaster database schema version mismatch"); + } + } else { + // Prepare statement will fail if Config table doesn't exist, which means our DB + // needs to be initialized. + if (sqlite3_exec(_db,ZT_NETCONF_SCHEMA_SQL"INSERT INTO Config (k,v) VALUES ('schemaVersion',"ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR");",0,0,0) != SQLITE_OK) { + sqlite3_close(_db); + throw std::runtime_error("SqliteNetworkConfigMaster cannot initialize database and/or insert schemaVersion into Config table"); + } } - // If we made it here, database was opened and prepared statement was created - // to check schema version. Check and upgrade if needed. - - int schemaVersion = -1234; - if (sqlite3_step(s) == SQLITE_ROW) - schemaVersion = sqlite3_column_int(s,0); - - sqlite3_finalize(s); - - if (schemaVersion == -1234) { - sqlite3_close(_db); - throw std::runtime_error("SqliteNetworkConfigMaster schemaVersion not found in Config table (init failure?)"); - } else if (schemaVersion != ZT_NETCONF_SQLITE_SCHEMA_VERSION) { - // Note -- this will eventually run auto-upgrades so this isn't how it'll work going forward + 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_close(_db); - throw std::runtime_error("SqliteNetworkConfigMaster database schema version mismatch"); + throw std::runtime_error("SqliteNetworkConfigMaster unable to initialize one or more prepared statements"); } } SqliteNetworkConfigMaster::~SqliteNetworkConfigMaster() { Mutex::Lock _l(_lock); - if (_db) + if (_db) { + sqlite3_finalize(_sGetNetworkById); + sqlite3_finalize(_sGetMemberByNetworkAndNodeId); + sqlite3_finalize(_sCreateMember); + sqlite3_finalize(_sGetNodeIdentity); + sqlite3_finalize(_sCreateNode); + sqlite3_finalize(_sUpdateNode); + sqlite3_finalize(_sUpdateNode2); + sqlite3_finalize(_sUpdateMemberClientReportedRevision); + sqlite3_finalize(_sGetEtherTypesFromRuleTable); + sqlite3_finalize(_sGetMulticastRates); + sqlite3_finalize(_sGetActiveBridges); + sqlite3_finalize(_sGetIpAssignmentsForNode); + sqlite3_finalize(_sGetIpAssignmentPools); + sqlite3_finalize(_sCheckIfIpIsAllocated); + sqlite3_finalize(_sAllocateIp); + sqlite3_finalize(_sCacheNetconf); sqlite3_close(_db); + } } -NetworkConfigMaster::ResultCode SqliteNetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uint64_t packetId,const Identity &member,uint64_t nwid,const Dictionary &metaData,uint64_t haveTimestamp,Dictionary &netconf) +NetworkConfigMaster::ResultCode SqliteNetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uint64_t packetId,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) { -#if 0 - char memberKey[128],nwids[24],addrs[16],nwKey[128],revKey[128]; - Dictionary memberRecord; - std::string revision,tmps2; - Mutex::Lock _l(_lock); - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); - Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs); - Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); - Utils::snprintf(revKey,sizeof(revKey),"zt1:network:%s:revision",nwids); - - //TRACE("netconf: %s : %s if > %llu",nwids,addrs,(unsigned long long)haveTimestamp); - - // Check to make sure network itself exists and is valid - if (!_hget(nwKey,"id",tmps2)) { - netconf["error"] = "Sqlite error retrieving network record ID field"; - return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + // Note: we can't reuse prepared statements that return const char * pointers without + // making our own copy in e.g. a std::string first. + + struct { + char id[24]; + const char *name; + const char *v4AssignMode; + const char *v6AssignMode; + bool isPrivate; + bool enableBroadcast; + bool allowPassiveBridging; + int multicastLimit; + uint64_t revision; + } network; + memset(&network,0,sizeof(network)); + Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); + + struct { + int64_t rowid; + char nodeId[16]; + int cachedNetconfBytes; + const void *cachedNetconf; + uint64_t cachedNetconfRevision; + uint64_t clientReportedRevision; + bool authorized; + bool activeBridge; + } member; + memset(&member,0,sizeof(member)); + Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); + + // Create/update Node record and check identity fully -- identities are first-come-first-claim + + 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) { + char lastSeen[64]; + Utils::snprintf(lastSeen,sizeof(lastSeen),"%llu",(unsigned long long)Utils::now()); + if (fromAddr) { + std::string lastAt(fromAddr.toString()); + sqlite3_reset(_sUpdateNode); + sqlite3_bind_text(_sUpdateNode,1,lastAt.c_str(),-1,SQLITE_STATIC); + sqlite3_bind_text(_sUpdateNode,2,lastSeen,-1,SQLITE_STATIC); + sqlite3_bind_text(_sUpdateNode,3,member.nodeId,10,SQLITE_STATIC); + sqlite3_step(_sUpdateNode); + } else { // fromAddr is empty, which means this was a relayed packet -- so don't update lastAt + sqlite3_reset(_sUpdateNode2); + sqlite3_bind_text(_sUpdateNode2,1,lastSeen,-1,SQLITE_STATIC); + sqlite3_bind_text(_sUpdateNode2,2,member.nodeId,10,SQLITE_STATIC); + sqlite3_step(_sUpdateNode2); + } + } else { + return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED; + } + } catch ( ... ) { // identity stored in database is not valid or is NULL + return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED; + } + } else { + std::string idstr(identity.toString(false)); + std::string lastAt; + if (fromAddr) + lastAt = fromAddr.toString(); + char lastSeen[64]; + Utils::snprintf(lastSeen,sizeof(lastSeen),"%llu",(unsigned long long)Utils::now()); + sqlite3_reset(_sCreateNode); + sqlite3_bind_text(_sCreateNode,1,member.nodeId,10,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNode,2,idstr.c_str(),-1,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNode,3,lastAt.c_str(),-1,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNode,4,lastSeen,-1,SQLITE_STATIC); + sqlite3_bind_text(_sCreateNode,5,lastSeen,-1,SQLITE_STATIC); + if (sqlite3_step(_sCreateNode) != SQLITE_DONE) { + netconf["error"] = "unable to create new node record"; + return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } } - if (tmps2 != nwids) - return NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND; - // Get network revision - if (!_get(revKey,revision)) { - netconf["error"] = "Sqlite error retrieving network revision"; - return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + // Fetch Network record + + bool foundNetwork = false; + sqlite3_reset(_sGetNetworkById); + sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC); + if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { + foundNetwork = true; + 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.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); } - if (!revision.length()) - revision = "0"; + if (!foundNetwork) + return NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND; - // Get network member record for this peer - if (!_hgetall(memberKey,memberRecord)) { - netconf["error"] = "Sqlite error retrieving member record"; - return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + // Fetch Member record + + bool foundMember = false; + sqlite3_reset(_sGetMemberByNetworkAndNodeId); + sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,1,network.id,16,SQLITE_STATIC); + sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,2,member.nodeId,10,SQLITE_STATIC); + if (sqlite3_step(_sGetMemberByNetworkAndNodeId) == SQLITE_ROW) { + foundMember = true; + member.rowid = (int64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,0); + 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); } - // If there is no member record, init a new one -- for public networks this - // auto-authorizes, and for private nets it makes the peer show up in the UI - // so the admin can authorize or delete/hide it. - if ((memberRecord.size() == 0)||(memberRecord.get("id","") != addrs)||(memberRecord.get("nwid","") != nwids)) { - if (!_initNewMember(nwid,member,metaData,memberRecord)) { - netconf["error"] = "_initNewMember() failed"; + // Create Member record for unknown nodes, auto-authorizing if network is public + + if (!foundMember) { + member.cachedNetconfBytes = 0; + member.cachedNetconfRevision = 0; + member.clientReportedRevision = 0; + 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 ? 0 : 1)); + if ( (sqlite3_step(_sCreateMember) != SQLITE_DONE) && ((member.rowid = (int64_t)sqlite3_last_insert_rowid(_db)) > 0) ) { + netconf["error"] = "unable to create new member record"; return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; } } - if (memberRecord.getBoolean("authorized")) { - // Get current netconf and netconf timestamp - uint64_t ts = memberRecord.getUInt("netconfTimestamp",0); - std::string netconfStr(memberRecord.get("netconf","")); - netconf.fromString(netconfStr); + // Check member authorization - // Update statistics for this node - Dictionary upd; - upd.set("netconfClientTimestamp",haveTimestamp); - if (fromAddr) - upd.set("lastAt",fromAddr.toString()); - upd.set("lastSeen",Utils::now()); - _hmset(memberKey,upd); - - // Attempt to generate netconf for this node if there isn't - // one or it's not in step with the network's revision. - if (((ts == 0)||(netconfStr.length() == 0))||(memberRecord.get("netconfRevision","") != revision)) { - std::string errorMessage; - if (!_generateNetconf(nwid,member,metaData,netconf,ts,errorMessage)) { - netconf["error"] = errorMessage; - return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; - } - } - - if (ts > haveTimestamp) - return NetworkConfigMaster::NETCONF_QUERY_OK; - else return NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER; - } else { + if (!member.authorized) return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED; - } -#endif -} -bool SqliteNetworkConfigMaster::_initNewMember(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &memberRecord) -{ -#if 0 - char memberKey[128],nwids[24],addrs[16],nwKey[128],membersKey[128]; - Dictionary networkRecord; - - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); - Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs); - Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); - Utils::snprintf(membersKey,sizeof(membersKey),"zt1:network:%s:members",nwids); - - if (!_hgetall(nwKey,networkRecord)) { - //LOG("netconf: Sqlite error retrieving %s",nwKey); - return false; - } - if (networkRecord.get("id","") != nwids) { - //TRACE("netconf: network %s not found (initNewMember)",nwids); - return false; + // Update client's currently reported haveRevision in Member record + + if (member.rowid > 0) { + sqlite3_reset(_sUpdateMemberClientReportedRevision); + sqlite3_bind_int64(_sUpdateMemberClientReportedRevision,1,(sqlite3_int64)haveRevision); + sqlite3_bind_int64(_sUpdateMemberClientReportedRevision,2,member.rowid); + sqlite3_step(_sUpdateMemberClientReportedRevision); } - memberRecord.clear(); - memberRecord["id"] = addrs; - memberRecord["nwid"] = nwids; - memberRecord["authorized"] = ((networkRecord.get("private","1") == "0") ? "1" : "0"); // auto-authorize on public networks - memberRecord.set("firstSeen",Utils::now()); - memberRecord["identity"] = member.toString(false); + // If netconf is unchanged from client reported revision, just tell client they're up to date - if (!_hmset(memberKey,memberRecord)) - return false; - if (!_sadd(membersKey,addrs)) - return false; + if ((haveRevision > 0)&&(haveRevision == network.revision)) + return NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER; - return true; -#endif -} + // Generate or retrieve cached netconf -bool SqliteNetworkConfigMaster::_generateNetconf(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &netconf,uint64_t &ts,std::string &errorMessage) -{ -#if 0 - char memberKey[256],nwids[24],addrs[16],tss[24],nwKey[256],revKey[128],abKey[128],ipaKey[128]; - Dictionary networkRecord,memberRecord; - std::string revision; - - Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs); - Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); - Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); - Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); - Utils::snprintf(revKey,sizeof(revKey),"zt1:network:%s:revision",nwids); - Utils::snprintf(abKey,sizeof(revKey),"zt1:network:%s:activeBridges",nwids); - Utils::snprintf(ipaKey,sizeof(revKey),"zt1:network:%s:ipAssignments",nwids); - - if (!_hgetall(nwKey,networkRecord)) { - errorMessage = "Sqlite error retrieving network record"; - return false; - } - if (networkRecord.get("id","") != nwids) { - errorMessage = "network IDs do not match in database"; - return false; - } - - if (!_hgetall(memberKey,memberRecord)) { - errorMessage = "Sqlite error retrieving member record"; - return false; - } + netconf.clear(); + if ((member.cachedNetconfRevision == network.revision)&&(member.cachedNetconfBytes > 0)) { + // Use cached copy + std::string tmp((const char *)member.cachedNetconf,member.cachedNetconfBytes); + netconf.fromString(tmp); + } else { + // Create and sign a new netconf, and save in database to re-use in the future + + char tss[24],rs[24]; + Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)Utils::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 (!_get(revKey,revision)) { - errorMessage = "Sqlite error retrieving network revision"; - return false; - } - if (!revision.length()) - revision = "0"; - - bool isPrivate = networkRecord.getBoolean("private",true); - ts = Utils::now(); - Utils::snprintf(tss,sizeof(tss),"%llx",ts); - - // Core configuration - netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss; - netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwids; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = addrs; - netconf[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = isPrivate ? "1" : "0"; - netconf[ZT_NETWORKCONFIG_DICT_KEY_NAME] = networkRecord.get("name",nwids); - netconf[ZT_NETWORKCONFIG_DICT_KEY_DESC] = networkRecord.get("desc",""); - netconf[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = networkRecord.getBoolean("enableBroadcast",true) ? "1" : "0"; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = networkRecord.getBoolean("allowPassiveBridging",false) ? "1" : "0"; - netconf[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = networkRecord.get("etherTypes",""); // these are stored as hex comma-delimited list - - // Multicast options - netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = networkRecord.get("multicastRates",""); - uint64_t ml = networkRecord.getHexUInt("multicastLimit",0); - if (ml > 0) - netconf.setHex(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,ml); - - // Active bridge configuration - { - std::string activeBridgeList; - std::vector activeBridgeSet; - if (!_smembers(abKey,activeBridgeSet)) { - errorMessage = "Sqlite error retrieving active bridge set"; - return false; - } - std::sort(activeBridgeSet.begin(),activeBridgeSet.end()); - for(std::vector::const_iterator i(activeBridgeSet.begin());i!=activeBridgeSet.end();++i) { - if (i->length() == 10) { - if (activeBridgeList.length() > 0) - activeBridgeList.push_back(','); - activeBridgeList.append(*i); + { + std::vector allowedEtherTypes; + sqlite3_reset(_sGetEtherTypesFromRuleTable); + sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC); + while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) { + int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0); + if ((et >= 0)&&(et <= 0xffff)) + allowedEtherTypes.push_back(et); } + std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end()); + std::unique(allowedEtherTypes.begin(),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; } - if (activeBridgeList.length() > 0) - netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridgeList; - } - // IP address assignment and auto-assign using the ZeroTier-internal mechanism (not DHCP, etc.) - { - std::string ipAssignments(memberRecord.get("ipAssignments","")); - - // Get sorted, separated lists of IPv4 and IPv6 IP address assignments already present - std::vector ip4s,ip6s; { - std::vector ips(Utils::split(ipAssignments.c_str(),",","","")); - for(std::vector::iterator i(ips.begin());i!=ips.end();++i) { - InetAddress a(*i); - if (a.isV4()) - ip4s.push_back(a); - else if (a.isV6()) - ip6s.push_back(a); + std::string multicastRates; + sqlite3_reset(_sGetMulticastRates); + sqlite3_bind_text(_sGetMulticastRates,1,network.id,16,SQLITE_STATIC); + while (sqlite3_step(_sGetMulticastRates) == SQLITE_ROW) { + const char *mac = (const char *)sqlite3_column_text(_sGetMulticastRates,0); + if ((mac)&&(strlen(mac) == 12)) { + unsigned long adi = ((unsigned long)sqlite3_column_int64(_sGetMulticastRates,1)) & 0xffffffff; + char tmp[256]; + Utils::snprintf(tmp,sizeof(tmp),"%s/%.4lx=%x,%x,%x\n",mac,adi,sqlite3_column_int(_sGetMulticastRates,2),sqlite3_column_int(_sGetMulticastRates,3),sqlite3_column_int(_sGetMulticastRates,4)); + multicastRates.append(tmp); + } + } + if (multicastRates.length() > 0) + netconf[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = multicastRates; + 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; } } - std::sort(ip4s.begin(),ip4s.end()); - std::unique(ip4s.begin(),ip4s.end()); - std::sort(ip6s.begin(),ip6s.end()); - std::unique(ip6s.begin(),ip6s.end()); - - // If IPv4 assignment mode is 'zt', send them to the client - if (networkRecord.get("v4AssignMode","") == "zt") { - // If we have no IPv4 addresses and we have an assignment pool, auto-assign - if (ip4s.empty()) { - InetAddress v4AssignPool(networkRecord.get("v4AssignPool","")); - uint32_t pnet = Utils::ntoh(*((const uint32_t *)v4AssignPool.rawIpData())); - unsigned int pbits = v4AssignPool.netmaskBits(); - - if ((v4AssignPool.isV4())&&(pbits > 0)&&(pbits < 32)&&(pnet != 0)) { - uint32_t pmask = 0xffffffff << (32 - pbits); // netmask over network part - uint32_t invmask = ~pmask; // netmask over "random" part - - // Begin exploring the IP space by generating an IP from the ZeroTier address - uint32_t first = (((uint32_t)(member.address().toInt() & 0xffffffffULL)) & invmask) | (pnet & pmask); - if ((first & 0xff) == 0) - first |= 1; - else if ((first & 0xff) == 0xff) - first &= 0xfe; - - // Start by trying this first IP - uint32_t abcd = first; - - InetAddress ip; - bool gotone = false; - unsigned long sanityCounter = 0; - do { - // Convert to IPv4 InetAddress - uint32_t abcdNetworkByteOrder = Utils::hton(abcd); - ip.set(&abcdNetworkByteOrder,4,pbits); - - // Is 'ip' already assigned to another node? - std::string assignment; - if (!_hget(ipaKey,ip.toString().c_str(),assignment)) { - errorMessage = "Sqlite error while checking IP allocation"; - return false; - } - if ((assignment.length() != 10)||(assignment == member.address().toString())) { - gotone = true; - break; // not taken! - } - // If we made it here, the IP was taken so increment and mask and try again - ++abcd; - abcd &= invmask; - abcd |= (pnet & pmask); - if ((abcd & 0xff) == 0) - abcd |= 1; - else if ((abcd & 0xff) == 0xff) - abcd &= 0xfe; - - // Don't spend insane amounts of time here -- if we have to try this hard, the user - // needs to allocate a larger IP block. - if (++sanityCounter >= 65535) - break; - } while (abcd != first); // keep going until we loop back around to 'first' - - // If we got one, add to IP list and claim in database - if (gotone) { - ip4s.push_back(ip); - _hset(ipaKey,ip.toString().c_str(),member.address().toString().c_str()); - if (ipAssignments.length() > 0) - ipAssignments.push_back(','); - ipAssignments.append(ip.toString()); - _hset(memberKey,"ipAssignments",ipAssignments.c_str()); - } else { - char tmp[1024]; - Utils::snprintf(tmp,sizeof(tmp),"failed to allocate IP in %s for %s in network %s, need a larger pool!",v4AssignPool.toString().c_str(),addrs,nwids); - errorMessage = tmp; - return false; - } + { + 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; + } - // Create comma-delimited list to send to client + if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { std::string v4s; - for(std::vector::iterator i(ip4s.begin());i!=ip4s.end();++i) { - if (v4s.length() > 0) - v4s.push_back(','); - v4s.append(i->toString()); + + 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,0); + int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentsForNode,1); + if ((ip)&&(sqlite3_column_bytes(_sGetIpAssignmentsForNode,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits <= 32)) { + char tmp[32]; + Utils::snprintf(tmp,sizeof(tmp),"%d.%d.%d.%d/%d",(int)ip[0],(int)ip[1],(int)ip[2],(int)ip[3],ipNetmaskBits); + if (v4s.length()) + v4s.push_back(','); + v4s.append(tmp); + } + } + + if (!v4s.length()) { + // Attempt to auto-assign an IPv4 address from an available pool if one isn't assigned already + sqlite3_reset(_sGetIpAssignmentPools); + sqlite3_bind_text(_sGetIpAssignmentPools,1,network.id,16,SQLITE_STATIC); + sqlite3_bind_int(_sGetIpAssignmentPools,2,4); // 4 == IPv4 + while ((!v4s.length())&&(sqlite3_step(_sGetIpAssignmentPools) == SQLITE_ROW)) { + const void *ipNetwork = sqlite3_column_blob(_sGetIpAssignmentPools,0); + int ipNetmaskBits = sqlite3_column_int(_sGetIpAssignmentPools,1); + if ((ipNetwork)&&(sqlite3_column_bytes(_sGetIpAssignmentPools,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits < 32)) { + uint32_t n = Utils::ntoh(*((const uint32_t *)ipNetwork)); // network in host byte order e.g. 192.168.0.0 + uint32_t m = 0xffffffff << (32 - ipNetmaskBits); // netmask e.g. 0xffffff00 for '24' since 32 - 24 == 8 + uint32_t im = ~m; // inverse mask, e.g. 0x000000ff for a netmask of 0xffffff00 + uint32_t abits = (uint32_t)(identity.address().toInt() & 0xffffffff); // least significant bits of member ZT address + + for(uint32_t k=0;k<=im;++k) { // try up to the number of IPs possible in this network + uint32_t ip = ( ((abits + k) & im) | (n & m) ); // build IP using bits from ZT address of member + k + if ((ip & 0x000000ff) == 0x00) continue; // no IPs ending in .0 allowed + if ((ip & 0x000000ff) == 0xff) continue; // no IPs ending in .255 allowed + + uint32_t nip = Utils::hton(ip); // IP in big-endian "network" byte order + sqlite3_reset(_sCheckIfIpIsAllocated); + sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC); + sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)&nip,4,SQLITE_STATIC); + sqlite3_bind_int(_sCheckIfIpIsAllocated,3,4); // 4 == IPv4 + 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_blob(_sAllocateIp,3,(const void *)&nip,4,SQLITE_STATIC); + sqlite3_bind_int(_sAllocateIp,4,ipNetmaskBits); + sqlite3_bind_int(_sAllocateIp,5,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),ipNetmaskBits); + if (v4s.length()) + v4s.push_back(','); + v4s.append(tmp); + break; // IP found and reserved! v4s containing something will cause outer while() to break. + } + } + } + } + } } + if (v4s.length()) netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; } - if (networkRecord.get("v6AssignMode","") == "zt") { - // TODO: IPv6 auto-assign ... not quite baked yet. :) + // TODO: IPv6 auto-assign once it's supported in UI - std::string v6s; - for(std::vector::iterator i(ip6s.begin());i!=ip6s.end();++i) { - if (v6s.length() > 0) - v6s.push_back(','); - v6s.append(i->toString()); + if (network.isPrivate) { + CertificateOfMembership com(network.revision,16,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; } - if (v6s.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = v6s; } - } - // If this is a private network, generate a signed certificate of membership - if (isPrivate) { - CertificateOfMembership com(Utils::strToU64(revision.c_str()),1,nwid,member.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 { - errorMessage = "unable to sign COM"; - return false; + if (!netconf.sign(_signingId)) { + netconf["error"] = "unable to sign netconf dictionary"; + return NETCONF_QUERY_INTERNAL_SERVER_ERROR; } - } - - // Sign netconf dictionary itself - if (!netconf.sign(_signingId)) { - errorMessage = "unable to sign netconf dictionary"; - return false; - } - // Record new netconf in database for re-use on subsequent repeat queries - { - Dictionary upd; - upd["netconf"] = netconf.toString(); - upd.set("netconfTimestamp",ts); - upd["netconfRevision"] = revision; - if (!_hmset(memberKey,upd)) { - errorMessage = "Sqlite error updating network record with new netconf dictionary"; - return false; + // Save serialized netconf for future re-use + std::string netconfSerialized(netconf.toString()); + if (netconfSerialized.length() < 4096) { // sanity check + sqlite3_reset(_sCacheNetconf); + sqlite3_bind_blob(_sCacheNetconf,1,(const void *)netconfSerialized.data(),netconfSerialized.length(),SQLITE_STATIC); + sqlite3_bind_int64(_sCacheNetconf,2,(sqlite3_int64)network.revision); + sqlite3_bind_int64(_sCacheNetconf,3,member.rowid); + sqlite3_step(_sCacheNetconf); } } - return true; -#endif + return NetworkConfigMaster::NETCONF_QUERY_OK; } } // namespace ZeroTier diff --git a/netconf/SqliteNetworkConfigMaster.hpp b/netconf/SqliteNetworkConfigMaster.hpp index 829eb4f8..77398434 100644 --- a/netconf/SqliteNetworkConfigMaster.hpp +++ b/netconf/SqliteNetworkConfigMaster.hpp @@ -51,20 +51,34 @@ public: virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest( const InetAddress &fromAddr, uint64_t packetId, - const Identity &member, + const Identity &identity, uint64_t nwid, const Dictionary &metaData, - uint64_t haveTimestamp, + uint64_t haveRevision, Dictionary &netconf); private: - bool _initNewMember(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &memberRecord); - bool _generateNetconf(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &netconf,uint64_t &ts,std::string &errorMessage); - Identity _signingId; std::string _dbPath; sqlite3 *_db; + sqlite3_stmt *_sGetNetworkById; + sqlite3_stmt *_sGetMemberByNetworkAndNodeId; + sqlite3_stmt *_sCreateMember; + sqlite3_stmt *_sGetNodeIdentity; + sqlite3_stmt *_sCreateNode; + sqlite3_stmt *_sUpdateNode; + sqlite3_stmt *_sUpdateNode2; + sqlite3_stmt *_sUpdateMemberClientReportedRevision; + sqlite3_stmt *_sGetEtherTypesFromRuleTable; + sqlite3_stmt *_sGetMulticastRates; + sqlite3_stmt *_sGetActiveBridges; + sqlite3_stmt *_sGetIpAssignmentsForNode; + sqlite3_stmt *_sGetIpAssignmentPools; + sqlite3_stmt *_sCheckIfIpIsAllocated; + sqlite3_stmt *_sAllocateIp; + sqlite3_stmt *_sCacheNetconf; + Mutex _lock; }; diff --git a/netconf/netconf-schema.sql b/netconf/netconf-schema.sql index ccd6de65..f5981f1a 100644 --- a/netconf/netconf-schema.sql +++ b/netconf/netconf-schema.sql @@ -6,11 +6,12 @@ CREATE TABLE Config ( CREATE TABLE IpAssignment ( networkId char(16) NOT NULL, nodeId char(10) NOT NULL, - ip varchar(64) NOT NULL, - ipNetmaskBits integer(4) NOT NULL DEFAULT(0) + ip blob(16) NOT NULL, + ipNetmaskBits integer NOT NULL DEFAULT(0), + ipVersion integer NOT NULL DEFAULT(4) ); -CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); +CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId); @@ -18,9 +19,10 @@ CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId); CREATE TABLE IpAssignmentPool ( networkId char(16) NOT NULL, - ipNetwork varchar(64) NOT NULL, - ipNetmaskBits integer(4) NOT NULL, - active integer(1) NOT NULL DEFAULT(1) + ipNetwork blob(16) NOT NULL, + ipNetmaskBits integer NOT NULL, + ipVersion integer NOT NULL DEFAULT(4), + active integer NOT NULL DEFAULT(1) ); CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId); @@ -29,10 +31,10 @@ CREATE TABLE Member ( networkId char(16) NOT NULL, nodeId char(10) NOT NULL, cachedNetconf blob(4096), - cachedNetconfRevision integer(32), - clientReportedRevision integer(32), - authorized integer(1) NOT NULL DEFAULT(0), - activeBridge integer(1) NOT NULL DEFAULT(0) + cachedNetconfRevision integer NOT NULL DEFAULT(0), + clientReportedRevision integer NOT NULL DEFAULT(0), + authorized integer NOT NULL DEFAULT(0), + activeBridge integer NOT NULL DEFAULT(0) ); CREATE INDEX Member_networkId ON Member (networkId); @@ -42,10 +44,10 @@ CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId); CREATE TABLE MulticastRate ( networkId char(16) NOT NULL, mgMac char(12) NOT NULL, - mgAdi integer(8) NOT NULL DEFAULT(0), - preload integer(16) NOT NULL, - maxBalance integer(16) NOT NULL, - accrual integer(16) NOT NULL + mgAdi integer NOT NULL DEFAULT(0), + preload integer NOT NULL, + maxBalance integer NOT NULL, + accrual integer NOT NULL ); CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); @@ -53,39 +55,39 @@ CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); CREATE TABLE Network ( id char(16) PRIMARY KEY NOT NULL, name varchar(128) NOT NULL, - private integer(1) NOT NULL DEFAULT(1), - enableBroadcast integer(1) NOT NULL DEFAULT(1), - allowPassiveBridging integer(1) NOT NULL DEFAULT(0), + private integer NOT NULL DEFAULT(1), + enableBroadcast integer NOT NULL DEFAULT(1), + allowPassiveBridging integer NOT NULL DEFAULT(0), v4AssignMode varchar(8) NOT NULL DEFAULT('none'), v6AssignMode varchar(8) NOT NULL DEFAULT('none'), - multicastLimit integer(8) NOT NULL DEFAULT(32), - creationTime integer(32) NOT NULL DEFAULT(0), - revision integer(32) NOT NULL DEFAULT(0) + multicastLimit integer NOT NULL DEFAULT(32), + creationTime integer NOT NULL DEFAULT(0), + revision integer NOT NULL DEFAULT(1) ); CREATE TABLE Node ( id char(10) PRIMARY KEY NOT NULL, identity varchar(4096) NOT NULL, lastAt varchar(64), - lastSeen integer(32) NOT NULL DEFAULT(0), - firstSeen integer(32) NOT NULL DEFAULT(0) + lastSeen integer NOT NULL DEFAULT(0), + firstSeen integer NOT NULL DEFAULT(0) ); CREATE TABLE Rule ( networkId char(16) NOT NULL, nodeId char(10), - vlanId integer(4), - vlanPcp integer(4), - etherType integer(8), + vlanId integer, + vlanPcp integer, + etherType integer, macSource char(12), macDest char(12), ipSource varchar(64), ipDest varchar(64), - ipTos integer(4), - ipProtocol integer(4), - ipSourcePort integer(8), - ipDestPort integer(8), + ipTos integer, + ipProtocol integer, + ipSourcePort integer, + ipDestPort integer, "action" varchar(4096) NOT NULL DEFAULT('accept') ); -CREATE INDEX Rule_networkId ON Rule (networkId); +CREATE INDEX Rule_networkId ON Rule (networkId); \ No newline at end of file diff --git a/netconf/netconf-schema.sql.c b/netconf/netconf-schema.sql.c index 42e71073..fdd51360 100644 --- a/netconf/netconf-schema.sql.c +++ b/netconf/netconf-schema.sql.c @@ -7,11 +7,12 @@ "CREATE TABLE IpAssignment (\n"\ " networkId char(16) NOT NULL,\n"\ " nodeId char(10) NOT NULL,\n"\ -" ip varchar(64) NOT NULL,\n"\ -" ipNetmaskBits integer(4) NOT NULL DEFAULT(0)\n"\ +" ip blob(16) NOT NULL,\n"\ +" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\ +" ipVersion integer NOT NULL DEFAULT(4)\n"\ ");\n"\ "\n"\ -"CREATE UNIQUE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ +"CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ "\n"\ "CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ "\n"\ @@ -19,9 +20,10 @@ "\n"\ "CREATE TABLE IpAssignmentPool (\n"\ " networkId char(16) NOT NULL,\n"\ -" ipNetwork varchar(64) NOT NULL,\n"\ -" ipNetmaskBits integer(4) NOT NULL,\n"\ -" active integer(1) NOT NULL DEFAULT(1)\n"\ +" ipNetwork blob(16) NOT NULL,\n"\ +" ipNetmaskBits integer NOT NULL,\n"\ +" ipVersion integer NOT NULL DEFAULT(4),\n"\ +" active integer NOT NULL DEFAULT(1)\n"\ ");\n"\ "\n"\ "CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\ @@ -30,10 +32,10 @@ " networkId char(16) NOT NULL,\n"\ " nodeId char(10) NOT NULL,\n"\ " cachedNetconf blob(4096),\n"\ -" cachedNetconfRevision integer(32),\n"\ -" clientReportedRevision integer(32),\n"\ -" authorized integer(1) NOT NULL DEFAULT(0),\n"\ -" activeBridge integer(1) NOT NULL DEFAULT(0)\n"\ +" cachedNetconfRevision 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"\ ");\n"\ "\n"\ "CREATE INDEX Member_networkId ON Member (networkId);\n"\ @@ -43,10 +45,10 @@ "CREATE TABLE MulticastRate (\n"\ " networkId char(16) NOT NULL,\n"\ " mgMac char(12) NOT NULL,\n"\ -" mgAdi integer(8) NOT NULL DEFAULT(0),\n"\ -" preload integer(16) NOT NULL,\n"\ -" maxBalance integer(16) NOT NULL,\n"\ -" accrual integer(16) NOT NULL\n"\ +" mgAdi integer NOT NULL DEFAULT(0),\n"\ +" preload integer NOT NULL,\n"\ +" maxBalance integer NOT NULL,\n"\ +" accrual integer NOT NULL\n"\ ");\n"\ "\n"\ "CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\ @@ -54,38 +56,38 @@ "CREATE TABLE Network (\n"\ " id char(16) PRIMARY KEY NOT NULL,\n"\ " name varchar(128) NOT NULL,\n"\ -" private integer(1) NOT NULL DEFAULT(1),\n"\ -" enableBroadcast integer(1) NOT NULL DEFAULT(1),\n"\ -" allowPassiveBridging integer(1) NOT NULL DEFAULT(0),\n"\ +" private integer NOT NULL DEFAULT(1),\n"\ +" enableBroadcast integer NOT NULL DEFAULT(1),\n"\ +" allowPassiveBridging integer NOT NULL DEFAULT(0),\n"\ " v4AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\ " v6AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\ -" multicastLimit integer(8) NOT NULL DEFAULT(32),\n"\ -" creationTime integer(32) NOT NULL DEFAULT(0),\n"\ -" revision integer(32) NOT NULL DEFAULT(0)\n"\ +" multicastLimit integer NOT NULL DEFAULT(32),\n"\ +" creationTime integer NOT NULL DEFAULT(0),\n"\ +" revision integer NOT NULL DEFAULT(1)\n"\ ");\n"\ "\n"\ "CREATE TABLE Node (\n"\ " id char(10) PRIMARY KEY NOT NULL,\n"\ " identity varchar(4096) NOT NULL,\n"\ " lastAt varchar(64),\n"\ -" lastSeen integer(32) NOT NULL DEFAULT(0),\n"\ -" firstSeen integer(32) NOT NULL DEFAULT(0)\n"\ +" lastSeen integer NOT NULL DEFAULT(0),\n"\ +" firstSeen integer NOT NULL DEFAULT(0)\n"\ ");\n"\ "\n"\ "CREATE TABLE Rule (\n"\ " networkId char(16) NOT NULL,\n"\ " nodeId char(10),\n"\ -" vlanId integer(4),\n"\ -" vlanPcp integer(4),\n"\ -" etherType integer(8),\n"\ +" vlanId integer,\n"\ +" vlanPcp integer,\n"\ +" etherType integer,\n"\ " macSource char(12),\n"\ " macDest char(12),\n"\ " ipSource varchar(64),\n"\ " ipDest varchar(64),\n"\ -" ipTos integer(4),\n"\ -" ipProtocol integer(4),\n"\ -" ipSourcePort integer(8),\n"\ -" ipDestPort integer(8),\n"\ +" ipTos integer,\n"\ +" ipProtocol integer,\n"\ +" ipSourcePort integer,\n"\ +" ipDestPort integer,\n"\ " \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\ ");\n"\ "\n"\ diff --git a/node/Identity.cpp b/node/Identity.cpp index 6c843cc2..8765da51 100644 --- a/node/Identity.cpp +++ b/node/Identity.cpp @@ -155,6 +155,9 @@ std::string Identity::toString(bool includePrivate) const bool Identity::fromString(const char *str) { + if (!str) + return false; + char *saveptr = (char *)0; char tmp[4096]; if (!Utils::scopy(tmp,sizeof(tmp),str)) diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index bef17a44..85855438 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -111,7 +111,7 @@ public: inline std::string toString() const { char buf[64]; - Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%lx",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned long)_adi); + Utils::snprintf(buf,sizeof(buf),"%.2x%.2x%.2x%.2x%.2x%.2x/%.4x",(unsigned int)_mac[0],(unsigned int)_mac[1],(unsigned int)_mac[2],(unsigned int)_mac[3],(unsigned int)_mac[4],(unsigned int)_mac[5],(unsigned int)_adi); return std::string(buf); } diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index 98e1b0d8..e42b0299 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -44,6 +44,7 @@ SharedPtr NetworkConfig::createTestNetworkConfig(const Address &s nc->_etWhitelist[0] |= 1; // allow all nc->_nwid = ZT_TEST_NETWORK_ID; nc->_timestamp = Utils::now(); + nc->_revision = 1; nc->_issuedTo = self; nc->_multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; nc->_allowPassiveBridging = false; @@ -108,6 +109,7 @@ void NetworkConfig::_fromDictionary(const Dictionary &d) throw std::invalid_argument("configuration contains zero network ID"); _timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP).c_str()); + _revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older netconf masters don't send this, so default to 1 memset(_etWhitelist,0,sizeof(_etWhitelist)); std::vector ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","","")); diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 4b998ea1..0aa7a70d 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -52,6 +52,7 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et" #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid" #define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts" +#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r" #define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id" #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr" @@ -134,6 +135,7 @@ public: inline uint64_t networkId() const throw() { return _nwid; } inline uint64_t timestamp() const throw() { return _timestamp; } + inline uint64_t revision() const throw() { return _revision; } inline const Address &issuedTo() const throw() { return _issuedTo; } inline unsigned int multicastLimit() const throw() { return _multicastLimit; } inline const std::map &multicastRates() const throw() { return _multicastRates; } @@ -174,6 +176,7 @@ private: uint64_t _nwid; uint64_t _timestamp; + uint64_t _revision; unsigned char _etWhitelist[65536 / 8]; Address _issuedTo; unsigned int _multicastLimit; diff --git a/node/NetworkConfigMaster.hpp b/node/NetworkConfigMaster.hpp index 12a926fa..06a4e56c 100644 --- a/node/NetworkConfigMaster.hpp +++ b/node/NetworkConfigMaster.hpp @@ -75,17 +75,17 @@ public: * @param member Originating peer ZeroTier identity * @param nwid 64-bit network ID * @param metaData Meta-data bundled with request (empty if none) - * @param haveTimestamp Timestamp sent by requesting peer or 0 if none + * @param haveRevision Network revision ID sent by requesting peer or 0 if none * @param result Dictionary to receive resulting signed netconf on success * @return Returns NETCONF_QUERY_OK if result dictionary is valid, or an error code on error */ virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest( const InetAddress &fromAddr, uint64_t packetId, - const Identity &member, + const Identity &identity, uint64_t nwid, const Dictionary &metaData, - uint64_t haveTimestamp, + uint64_t haveRevision, Dictionary &result) = 0; }; -- cgit v1.2.3