A whole lot of Sqlite netconf master work, and some fixes elsewhere in the code.

This commit is contained in:
Adam Ierymenko 2015-03-18 16:10:48 -07:00
parent cea3f28155
commit a8a92c5b89
9 changed files with 444 additions and 400 deletions

View File

@ -56,8 +56,7 @@ namespace ZeroTier {
SqliteNetworkConfigMaster::SqliteNetworkConfigMaster(const Identity &signingId,const char *dbPath) : SqliteNetworkConfigMaster::SqliteNetworkConfigMaster(const Identity &signingId,const char *dbPath) :
_signingId(signingId), _signingId(signingId),
_dbPath(dbPath), _dbPath(dbPath),
_db((sqlite3 *)0), _db((sqlite3 *)0)
_lock()
{ {
if (!_signingId.hasPrivate()) if (!_signingId.hasPrivate())
throw std::runtime_error("SqliteNetworkConfigMaster signing identity must have a private key"); 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"); throw std::runtime_error("SqliteNetworkConfigMaster cannot open database file");
sqlite3_busy_timeout(_db,10000); sqlite3_busy_timeout(_db,10000);
sqlite3_stmt *s; sqlite3_stmt *s = (sqlite3_stmt *)0;
for(int k=0;k<2;++k) { if ((sqlite3_prepare_v2(_db,"SELECT 'v' FROM Config WHERE 'k' = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) {
s = (sqlite3_stmt *)0; int schemaVersion = -1234;
if ((sqlite3_prepare_v2(_db,"SELECT 'v' FROM Config WHERE 'k' = 'schemaVersion';",-1,&s,(const char **)0) != SQLITE_OK)||(!s)) { if (sqlite3_step(s) == SQLITE_ROW)
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) { schemaVersion = sqlite3_column_int(s,0);
sqlite3_close(_db);
throw std::runtime_error("SqliteNetworkConfigMaster cannot initialize database and/or insert schemaVersion into Config table"); sqlite3_finalize(s);
} else {
// Initialized database and set schema version, so we are done. if (schemaVersion == -1234) {
return; sqlite3_close(_db);
} throw std::runtime_error("SqliteNetworkConfigMaster schemaVersion not found in Config table (init failure?)");
} else break; } 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 (!s) { sqlite3_close(_db);
sqlite3_close(_db); throw std::runtime_error("SqliteNetworkConfigMaster database schema version mismatch");
throw std::runtime_error("SqliteNetworkConfigMaster unable to create prepared statement or initialize database"); }
} 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 if (
// to check schema version. Check and upgrade if needed. (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)
int schemaVersion = -1234; ||(sqlite3_prepare_v2(_db,"INSERT INTO Member ('networkId','nodeId','cachedNetconfRevision','clientReportedRevision','authorized','activeBridge') VALUES (?,?,0,0,?,0)",-1,&_sCreateMember,(const char **)0) != SQLITE_OK)
if (sqlite3_step(s) == SQLITE_ROW) ||(sqlite3_prepare_v2(_db,"SELECT 'identity' FROM Node WHERE 'id' = ?",-1,&_sGetNodeIdentity,(const char **)0) != SQLITE_OK)
schemaVersion = sqlite3_column_int(s,0); ||(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_finalize(s); ||(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)
if (schemaVersion == -1234) { ||(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("SqliteNetworkConfigMaster schemaVersion not found in Config table (init failure?)"); throw std::runtime_error("SqliteNetworkConfigMaster unable to initialize one or more prepared statements");
} 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");
} }
} }
SqliteNetworkConfigMaster::~SqliteNetworkConfigMaster() SqliteNetworkConfigMaster::~SqliteNetworkConfigMaster()
{ {
Mutex::Lock _l(_lock); 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); 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); Mutex::Lock _l(_lock);
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); // Note: we can't reuse prepared statements that return const char * pointers without
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); // making our own copy in e.g. a std::string first.
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); 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);
// Check to make sure network itself exists and is valid struct {
if (!_hget(nwKey,"id",tmps2)) { int64_t rowid;
netconf["error"] = "Sqlite error retrieving network record ID field"; char nodeId[16];
return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; int cachedNetconfBytes;
} const void *cachedNetconf;
if (tmps2 != nwids) uint64_t cachedNetconfRevision;
return NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND; 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());
// Get network revision // Create/update Node record and check identity fully -- identities are first-come-first-claim
if (!_get(revKey,revision)) {
netconf["error"] = "Sqlite error retrieving network revision";
return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
}
if (!revision.length())
revision = "0";
// Get network member record for this peer sqlite3_reset(_sGetNodeIdentity);
if (!_hgetall(memberKey,memberRecord)) { sqlite3_bind_text(_sGetNodeIdentity,1,member.nodeId,10,SQLITE_STATIC);
netconf["error"] = "Sqlite error retrieving member record"; if (sqlite3_step(_sGetNodeIdentity) == SQLITE_ROW) {
return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; try {
} Identity alreadyKnownIdentity((const char *)sqlite3_column_text(_sGetNodeIdentity,0));
if (alreadyKnownIdentity == identity) {
// If there is no member record, init a new one -- for public networks this char lastSeen[64];
// auto-authorizes, and for private nets it makes the peer show up in the UI Utils::snprintf(lastSeen,sizeof(lastSeen),"%llu",(unsigned long long)Utils::now());
// so the admin can authorize or delete/hide it. if (fromAddr) {
if ((memberRecord.size() == 0)||(memberRecord.get("id","") != addrs)||(memberRecord.get("nwid","") != nwids)) { std::string lastAt(fromAddr.toString());
if (!_initNewMember(nwid,member,metaData,memberRecord)) { sqlite3_reset(_sUpdateNode);
netconf["error"] = "_initNewMember() failed"; 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; return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
} }
} }
if (memberRecord.getBoolean("authorized")) { // Fetch Network record
// Get current netconf and netconf timestamp
uint64_t ts = memberRecord.getUInt("netconfTimestamp",0);
std::string netconfStr(memberRecord.get("netconf",""));
netconf.fromString(netconfStr);
// Update statistics for this node bool foundNetwork = false;
Dictionary upd; sqlite3_reset(_sGetNetworkById);
upd.set("netconfClientTimestamp",haveTimestamp); sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC);
if (fromAddr) if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
upd.set("lastAt",fromAddr.toString()); foundNetwork = true;
upd.set("lastSeen",Utils::now()); network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0);
_hmset(memberKey,upd); 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 (!foundNetwork)
return NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND;
// Attempt to generate netconf for this node if there isn't // Fetch Member record
// one or it's not in step with the network's revision.
if (((ts == 0)||(netconfStr.length() == 0))||(memberRecord.get("netconfRevision","") != revision)) { bool foundMember = false;
std::string errorMessage; sqlite3_reset(_sGetMemberByNetworkAndNodeId);
if (!_generateNetconf(nwid,member,metaData,netconf,ts,errorMessage)) { sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,1,network.id,16,SQLITE_STATIC);
netconf["error"] = errorMessage; sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,2,member.nodeId,10,SQLITE_STATIC);
return NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR; 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);
}
// 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 (ts > haveTimestamp) // Check member authorization
return NetworkConfigMaster::NETCONF_QUERY_OK;
else return NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER; if (!member.authorized)
} else {
return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED; return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED;
}
#endif
}
bool SqliteNetworkConfigMaster::_initNewMember(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &memberRecord) // Update client's currently reported haveRevision in Member record
{
#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); if (member.rowid > 0) {
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); sqlite3_reset(_sUpdateMemberClientReportedRevision);
Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs); sqlite3_bind_int64(_sUpdateMemberClientReportedRevision,1,(sqlite3_int64)haveRevision);
Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); sqlite3_bind_int64(_sUpdateMemberClientReportedRevision,2,member.rowid);
Utils::snprintf(membersKey,sizeof(membersKey),"zt1:network:%s:members",nwids); sqlite3_step(_sUpdateMemberClientReportedRevision);
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;
} }
memberRecord.clear(); // If netconf is unchanged from client reported revision, just tell client they're up to date
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 (!_hmset(memberKey,memberRecord)) if ((haveRevision > 0)&&(haveRevision == network.revision))
return false; return NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER;
if (!_sadd(membersKey,addrs))
return false;
return true; // Generate or retrieve cached netconf
#endif
}
bool SqliteNetworkConfigMaster::_generateNetconf(uint64_t nwid,const Identity &member,const Dictionary &metaData,Dictionary &netconf,uint64_t &ts,std::string &errorMessage) netconf.clear();
{ if ((member.cachedNetconfRevision == network.revision)&&(member.cachedNetconfBytes > 0)) {
#if 0 // Use cached copy
char memberKey[256],nwids[24],addrs[16],tss[24],nwKey[256],revKey[128],abKey[128],ipaKey[128]; std::string tmp((const char *)member.cachedNetconf,member.cachedNetconfBytes);
Dictionary networkRecord,memberRecord; netconf.fromString(tmp);
std::string revision; } else {
// Create and sign a new netconf, and save in database to re-use in the future
Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs); char tss[24],rs[24];
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid); Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)Utils::now());
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.address().toInt()); Utils::snprintf(rs,sizeof(rs),"%.16llx",(unsigned long long)network.revision);
Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); netconf[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss;
Utils::snprintf(revKey,sizeof(revKey),"zt1:network:%s:revision",nwids); netconf[ZT_NETWORKCONFIG_DICT_KEY_REVISION] = rs;
Utils::snprintf(abKey,sizeof(revKey),"zt1:network:%s:activeBridges",nwids); netconf[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = network.id;
Utils::snprintf(ipaKey,sizeof(revKey),"zt1:network:%s:ipAssignments",nwids); 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 (!_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;
}
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<std::string> activeBridgeSet;
if (!_smembers(abKey,activeBridgeSet)) {
errorMessage = "Sqlite error retrieving active bridge set";
return false;
}
std::sort(activeBridgeSet.begin(),activeBridgeSet.end());
for(std::vector<std::string>::const_iterator i(activeBridgeSet.begin());i!=activeBridgeSet.end();++i) {
if (i->length() == 10) {
if (activeBridgeList.length() > 0)
activeBridgeList.push_back(',');
activeBridgeList.append(*i);
}
}
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<InetAddress> ip4s,ip6s;
{ {
std::vector<std::string> ips(Utils::split(ipAssignments.c_str(),",","","")); std::vector<int> allowedEtherTypes;
for(std::vector<std::string>::iterator i(ips.begin());i!=ips.end();++i) { sqlite3_reset(_sGetEtherTypesFromRuleTable);
InetAddress a(*i); sqlite3_bind_text(_sGetEtherTypesFromRuleTable,1,network.id,16,SQLITE_STATIC);
if (a.isV4()) while (sqlite3_step(_sGetEtherTypesFromRuleTable) == SQLITE_ROW) {
ip4s.push_back(a); int et = sqlite3_column_int(_sGetEtherTypesFromRuleTable,0);
else if (a.isV6()) if ((et >= 0)&&(et <= 0xffff))
ip6s.push_back(a); allowedEtherTypes.push_back(et);
}
std::sort(allowedEtherTypes.begin(),allowedEtherTypes.end());
std::unique(allowedEtherTypes.begin(),allowedEtherTypes.end());
std::string allowedEtherTypesCsv;
for(std::vector<int>::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;
}
{
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") { std::string activeBridges;
// If we have no IPv4 addresses and we have an assignment pool, auto-assign sqlite3_reset(_sGetActiveBridges);
if (ip4s.empty()) { sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC);
InetAddress v4AssignPool(networkRecord.get("v4AssignPool","")); while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) {
uint32_t pnet = Utils::ntoh(*((const uint32_t *)v4AssignPool.rawIpData())); const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0);
unsigned int pbits = v4AssignPool.netmaskBits(); 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 ((v4AssignPool.isV4())&&(pbits > 0)&&(pbits < 32)&&(pnet != 0)) { if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) {
uint32_t pmask = 0xffffffff << (32 - pbits); // netmask over network part std::string v4s;
uint32_t invmask = ~pmask; // netmask over "random" part
// Begin exploring the IP space by generating an IP from the ZeroTier address sqlite3_reset(_sGetIpAssignmentsForNode);
uint32_t first = (((uint32_t)(member.address().toInt() & 0xffffffffULL)) & invmask) | (pnet & pmask); sqlite3_bind_text(_sGetIpAssignmentsForNode,1,network.id,16,SQLITE_STATIC);
if ((first & 0xff) == 0) sqlite3_bind_text(_sGetIpAssignmentsForNode,2,member.nodeId,10,SQLITE_STATIC);
first |= 1; sqlite3_bind_int(_sGetIpAssignmentsForNode,3,4); // 4 == IPv4
else if ((first & 0xff) == 0xff) while (sqlite3_step(_sGetIpAssignmentsForNode) == SQLITE_ROW) {
first &= 0xfe; 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);
}
}
// Start by trying this first IP if (!v4s.length()) {
uint32_t abcd = first; // 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
InetAddress ip; for(uint32_t k=0;k<=im;++k) { // try up to the number of IPs possible in this network
bool gotone = false; uint32_t ip = ( ((abits + k) & im) | (n & m) ); // build IP using bits from ZT address of member + k
unsigned long sanityCounter = 0; if ((ip & 0x000000ff) == 0x00) continue; // no IPs ending in .0 allowed
do { if ((ip & 0x000000ff) == 0xff) continue; // no IPs ending in .255 allowed
// Convert to IPv4 InetAddress
uint32_t abcdNetworkByteOrder = Utils::hton(abcd);
ip.set(&abcdNetworkByteOrder,4,pbits);
// Is 'ip' already assigned to another node? uint32_t nip = Utils::hton(ip); // IP in big-endian "network" byte order
std::string assignment; sqlite3_reset(_sCheckIfIpIsAllocated);
if (!_hget(ipaKey,ip.toString().c_str(),assignment)) { sqlite3_bind_text(_sCheckIfIpIsAllocated,1,network.id,16,SQLITE_STATIC);
errorMessage = "Sqlite error while checking IP allocation"; sqlite3_bind_blob(_sCheckIfIpIsAllocated,2,(const void *)&nip,4,SQLITE_STATIC);
return false; 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 ((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;
} }
} }
} }
// Create comma-delimited list to send to client
std::string v4s;
for(std::vector<InetAddress>::iterator i(ip4s.begin());i!=ip4s.end();++i) {
if (v4s.length() > 0)
v4s.push_back(',');
v4s.append(i->toString());
}
if (v4s.length()) if (v4s.length())
netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s; netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s;
} }
if (networkRecord.get("v6AssignMode","") == "zt") { // TODO: IPv6 auto-assign once it's supported in UI
// TODO: IPv6 auto-assign ... not quite baked yet. :)
std::string v6s; if (network.isPrivate) {
for(std::vector<InetAddress>::iterator i(ip6s.begin());i!=ip6s.end();++i) { CertificateOfMembership com(network.revision,16,nwid,identity.address());
if (v6s.length() > 0) if (com.sign(_signingId)) // basically can't fail unless our identity is invalid
v6s.push_back(','); netconf[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
v6s.append(i->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 (!netconf.sign(_signingId)) {
netconf["error"] = "unable to sign netconf dictionary";
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
}
// 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);
} }
} }
// If this is a private network, generate a signed certificate of membership return NetworkConfigMaster::NETCONF_QUERY_OK;
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;
}
}
// 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;
}
}
return true;
#endif
} }
} // namespace ZeroTier } // namespace ZeroTier

View File

@ -51,20 +51,34 @@ public:
virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest( virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr, const InetAddress &fromAddr,
uint64_t packetId, uint64_t packetId,
const Identity &member, const Identity &identity,
uint64_t nwid, uint64_t nwid,
const Dictionary &metaData, const Dictionary &metaData,
uint64_t haveTimestamp, uint64_t haveRevision,
Dictionary &netconf); Dictionary &netconf);
private: 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; Identity _signingId;
std::string _dbPath; std::string _dbPath;
sqlite3 *_db; 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; Mutex _lock;
}; };

View File

@ -6,11 +6,12 @@ CREATE TABLE Config (
CREATE TABLE IpAssignment ( CREATE TABLE IpAssignment (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
nodeId char(10) NOT NULL, nodeId char(10) NOT NULL,
ip varchar(64) NOT NULL, ip blob(16) NOT NULL,
ipNetmaskBits integer(4) NOT NULL DEFAULT(0) 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); CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);
@ -18,9 +19,10 @@ CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId);
CREATE TABLE IpAssignmentPool ( CREATE TABLE IpAssignmentPool (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
ipNetwork varchar(64) NOT NULL, ipNetwork blob(16) NOT NULL,
ipNetmaskBits integer(4) NOT NULL, ipNetmaskBits integer NOT NULL,
active integer(1) NOT NULL DEFAULT(1) ipVersion integer NOT NULL DEFAULT(4),
active integer NOT NULL DEFAULT(1)
); );
CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId); CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);
@ -29,10 +31,10 @@ CREATE TABLE Member (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
nodeId char(10) NOT NULL, nodeId char(10) NOT NULL,
cachedNetconf blob(4096), cachedNetconf blob(4096),
cachedNetconfRevision integer(32), cachedNetconfRevision integer NOT NULL DEFAULT(0),
clientReportedRevision integer(32), clientReportedRevision integer NOT NULL DEFAULT(0),
authorized integer(1) NOT NULL DEFAULT(0), authorized integer NOT NULL DEFAULT(0),
activeBridge integer(1) NOT NULL DEFAULT(0) activeBridge integer NOT NULL DEFAULT(0)
); );
CREATE INDEX Member_networkId ON Member (networkId); CREATE INDEX Member_networkId ON Member (networkId);
@ -42,10 +44,10 @@ CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId);
CREATE TABLE MulticastRate ( CREATE TABLE MulticastRate (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
mgMac char(12) NOT NULL, mgMac char(12) NOT NULL,
mgAdi integer(8) NOT NULL DEFAULT(0), mgAdi integer NOT NULL DEFAULT(0),
preload integer(16) NOT NULL, preload integer NOT NULL,
maxBalance integer(16) NOT NULL, maxBalance integer NOT NULL,
accrual integer(16) NOT NULL accrual integer NOT NULL
); );
CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);
@ -53,38 +55,38 @@ CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);
CREATE TABLE Network ( CREATE TABLE Network (
id char(16) PRIMARY KEY NOT NULL, id char(16) PRIMARY KEY NOT NULL,
name varchar(128) NOT NULL, name varchar(128) NOT NULL,
private integer(1) NOT NULL DEFAULT(1), private integer NOT NULL DEFAULT(1),
enableBroadcast integer(1) NOT NULL DEFAULT(1), enableBroadcast integer NOT NULL DEFAULT(1),
allowPassiveBridging integer(1) NOT NULL DEFAULT(0), allowPassiveBridging integer NOT NULL DEFAULT(0),
v4AssignMode varchar(8) NOT NULL DEFAULT('none'), v4AssignMode varchar(8) NOT NULL DEFAULT('none'),
v6AssignMode varchar(8) NOT NULL DEFAULT('none'), v6AssignMode varchar(8) NOT NULL DEFAULT('none'),
multicastLimit integer(8) NOT NULL DEFAULT(32), multicastLimit integer NOT NULL DEFAULT(32),
creationTime integer(32) NOT NULL DEFAULT(0), creationTime integer NOT NULL DEFAULT(0),
revision integer(32) NOT NULL DEFAULT(0) revision integer NOT NULL DEFAULT(1)
); );
CREATE TABLE Node ( CREATE TABLE Node (
id char(10) PRIMARY KEY NOT NULL, id char(10) PRIMARY KEY NOT NULL,
identity varchar(4096) NOT NULL, identity varchar(4096) NOT NULL,
lastAt varchar(64), lastAt varchar(64),
lastSeen integer(32) NOT NULL DEFAULT(0), lastSeen integer NOT NULL DEFAULT(0),
firstSeen integer(32) NOT NULL DEFAULT(0) firstSeen integer NOT NULL DEFAULT(0)
); );
CREATE TABLE Rule ( CREATE TABLE Rule (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
nodeId char(10), nodeId char(10),
vlanId integer(4), vlanId integer,
vlanPcp integer(4), vlanPcp integer,
etherType integer(8), etherType integer,
macSource char(12), macSource char(12),
macDest char(12), macDest char(12),
ipSource varchar(64), ipSource varchar(64),
ipDest varchar(64), ipDest varchar(64),
ipTos integer(4), ipTos integer,
ipProtocol integer(4), ipProtocol integer,
ipSourcePort integer(8), ipSourcePort integer,
ipDestPort integer(8), ipDestPort integer,
"action" varchar(4096) NOT NULL DEFAULT('accept') "action" varchar(4096) NOT NULL DEFAULT('accept')
); );

View File

@ -7,11 +7,12 @@
"CREATE TABLE IpAssignment (\n"\ "CREATE TABLE IpAssignment (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" nodeId char(10) NOT NULL,\n"\ " nodeId char(10) NOT NULL,\n"\
" ip varchar(64) NOT NULL,\n"\ " ip blob(16) NOT NULL,\n"\
" ipNetmaskBits integer(4) NOT NULL DEFAULT(0)\n"\ " ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\
" ipVersion integer NOT NULL DEFAULT(4)\n"\
");\n"\ ");\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"\ "\n"\
"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ "CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\
"\n"\ "\n"\
@ -19,9 +20,10 @@
"\n"\ "\n"\
"CREATE TABLE IpAssignmentPool (\n"\ "CREATE TABLE IpAssignmentPool (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" ipNetwork varchar(64) NOT NULL,\n"\ " ipNetwork blob(16) NOT NULL,\n"\
" ipNetmaskBits integer(4) NOT NULL,\n"\ " ipNetmaskBits integer NOT NULL,\n"\
" active integer(1) NOT NULL DEFAULT(1)\n"\ " ipVersion integer NOT NULL DEFAULT(4),\n"\
" active integer NOT NULL DEFAULT(1)\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\ "CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\
@ -30,10 +32,10 @@
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" nodeId char(10) NOT NULL,\n"\ " nodeId char(10) NOT NULL,\n"\
" cachedNetconf blob(4096),\n"\ " cachedNetconf blob(4096),\n"\
" cachedNetconfRevision integer(32),\n"\ " cachedNetconfRevision integer NOT NULL DEFAULT(0),\n"\
" clientReportedRevision integer(32),\n"\ " clientReportedRevision integer NOT NULL DEFAULT(0),\n"\
" authorized integer(1) NOT NULL DEFAULT(0),\n"\ " authorized integer NOT NULL DEFAULT(0),\n"\
" activeBridge integer(1) NOT NULL DEFAULT(0)\n"\ " activeBridge integer NOT NULL DEFAULT(0)\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE INDEX Member_networkId ON Member (networkId);\n"\ "CREATE INDEX Member_networkId ON Member (networkId);\n"\
@ -43,10 +45,10 @@
"CREATE TABLE MulticastRate (\n"\ "CREATE TABLE MulticastRate (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" mgMac char(12) NOT NULL,\n"\ " mgMac char(12) NOT NULL,\n"\
" mgAdi integer(8) NOT NULL DEFAULT(0),\n"\ " mgAdi integer NOT NULL DEFAULT(0),\n"\
" preload integer(16) NOT NULL,\n"\ " preload integer NOT NULL,\n"\
" maxBalance integer(16) NOT NULL,\n"\ " maxBalance integer NOT NULL,\n"\
" accrual integer(16) NOT NULL\n"\ " accrual integer NOT NULL\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\ "CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId);\n"\
@ -54,38 +56,38 @@
"CREATE TABLE Network (\n"\ "CREATE TABLE Network (\n"\
" id char(16) PRIMARY KEY NOT NULL,\n"\ " id char(16) PRIMARY KEY NOT NULL,\n"\
" name varchar(128) NOT NULL,\n"\ " name varchar(128) NOT NULL,\n"\
" private integer(1) NOT NULL DEFAULT(1),\n"\ " private integer NOT NULL DEFAULT(1),\n"\
" enableBroadcast integer(1) NOT NULL DEFAULT(1),\n"\ " enableBroadcast integer NOT NULL DEFAULT(1),\n"\
" allowPassiveBridging integer(1) NOT NULL DEFAULT(0),\n"\ " allowPassiveBridging integer NOT NULL DEFAULT(0),\n"\
" v4AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\ " v4AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
" v6AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\ " v6AssignMode varchar(8) NOT NULL DEFAULT('none'),\n"\
" multicastLimit integer(8) NOT NULL DEFAULT(32),\n"\ " multicastLimit integer NOT NULL DEFAULT(32),\n"\
" creationTime integer(32) NOT NULL DEFAULT(0),\n"\ " creationTime integer NOT NULL DEFAULT(0),\n"\
" revision integer(32) NOT NULL DEFAULT(0)\n"\ " revision integer NOT NULL DEFAULT(1)\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE TABLE Node (\n"\ "CREATE TABLE Node (\n"\
" id char(10) PRIMARY KEY NOT NULL,\n"\ " id char(10) PRIMARY KEY NOT NULL,\n"\
" identity varchar(4096) NOT NULL,\n"\ " identity varchar(4096) NOT NULL,\n"\
" lastAt varchar(64),\n"\ " lastAt varchar(64),\n"\
" lastSeen integer(32) NOT NULL DEFAULT(0),\n"\ " lastSeen integer NOT NULL DEFAULT(0),\n"\
" firstSeen integer(32) NOT NULL DEFAULT(0)\n"\ " firstSeen integer NOT NULL DEFAULT(0)\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE TABLE Rule (\n"\ "CREATE TABLE Rule (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" nodeId char(10),\n"\ " nodeId char(10),\n"\
" vlanId integer(4),\n"\ " vlanId integer,\n"\
" vlanPcp integer(4),\n"\ " vlanPcp integer,\n"\
" etherType integer(8),\n"\ " etherType integer,\n"\
" macSource char(12),\n"\ " macSource char(12),\n"\
" macDest char(12),\n"\ " macDest char(12),\n"\
" ipSource varchar(64),\n"\ " ipSource varchar(64),\n"\
" ipDest varchar(64),\n"\ " ipDest varchar(64),\n"\
" ipTos integer(4),\n"\ " ipTos integer,\n"\
" ipProtocol integer(4),\n"\ " ipProtocol integer,\n"\
" ipSourcePort integer(8),\n"\ " ipSourcePort integer,\n"\
" ipDestPort integer(8),\n"\ " ipDestPort integer,\n"\
" \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\ " \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\
");\n"\ ");\n"\
"\n"\ "\n"\

View File

@ -155,6 +155,9 @@ std::string Identity::toString(bool includePrivate) const
bool Identity::fromString(const char *str) bool Identity::fromString(const char *str)
{ {
if (!str)
return false;
char *saveptr = (char *)0; char *saveptr = (char *)0;
char tmp[4096]; char tmp[4096];
if (!Utils::scopy(tmp,sizeof(tmp),str)) if (!Utils::scopy(tmp,sizeof(tmp),str))

View File

@ -111,7 +111,7 @@ public:
inline std::string toString() const inline std::string toString() const
{ {
char buf[64]; 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); return std::string(buf);
} }

View File

@ -44,6 +44,7 @@ SharedPtr<NetworkConfig> NetworkConfig::createTestNetworkConfig(const Address &s
nc->_etWhitelist[0] |= 1; // allow all nc->_etWhitelist[0] |= 1; // allow all
nc->_nwid = ZT_TEST_NETWORK_ID; nc->_nwid = ZT_TEST_NETWORK_ID;
nc->_timestamp = Utils::now(); nc->_timestamp = Utils::now();
nc->_revision = 1;
nc->_issuedTo = self; nc->_issuedTo = self;
nc->_multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT; nc->_multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
nc->_allowPassiveBridging = false; nc->_allowPassiveBridging = false;
@ -108,6 +109,7 @@ void NetworkConfig::_fromDictionary(const Dictionary &d)
throw std::invalid_argument("configuration contains zero network ID"); throw std::invalid_argument("configuration contains zero network ID");
_timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP).c_str()); _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)); memset(_etWhitelist,0,sizeof(_etWhitelist));
std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","","")); std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES).c_str(),",","",""));

View File

@ -52,6 +52,7 @@ namespace ZeroTier {
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et" #define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid" #define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
#define ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP "ts" #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_ISSUED_TO "id"
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml" #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr" #define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES "mr"
@ -134,6 +135,7 @@ public:
inline uint64_t networkId() const throw() { return _nwid; } inline uint64_t networkId() const throw() { return _nwid; }
inline uint64_t timestamp() const throw() { return _timestamp; } 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 const Address &issuedTo() const throw() { return _issuedTo; }
inline unsigned int multicastLimit() const throw() { return _multicastLimit; } inline unsigned int multicastLimit() const throw() { return _multicastLimit; }
inline const std::map<MulticastGroup,MulticastRate> &multicastRates() const throw() { return _multicastRates; } inline const std::map<MulticastGroup,MulticastRate> &multicastRates() const throw() { return _multicastRates; }
@ -174,6 +176,7 @@ private:
uint64_t _nwid; uint64_t _nwid;
uint64_t _timestamp; uint64_t _timestamp;
uint64_t _revision;
unsigned char _etWhitelist[65536 / 8]; unsigned char _etWhitelist[65536 / 8];
Address _issuedTo; Address _issuedTo;
unsigned int _multicastLimit; unsigned int _multicastLimit;

View File

@ -75,17 +75,17 @@ public:
* @param member Originating peer ZeroTier identity * @param member Originating peer ZeroTier identity
* @param nwid 64-bit network ID * @param nwid 64-bit network ID
* @param metaData Meta-data bundled with request (empty if none) * @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 * @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 * @return Returns NETCONF_QUERY_OK if result dictionary is valid, or an error code on error
*/ */
virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest( virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr, const InetAddress &fromAddr,
uint64_t packetId, uint64_t packetId,
const Identity &member, const Identity &identity,
uint64_t nwid, uint64_t nwid,
const Dictionary &metaData, const Dictionary &metaData,
uint64_t haveTimestamp, uint64_t haveRevision,
Dictionary &result) = 0; Dictionary &result) = 0;
}; };