From 6369c264e2b54f7eb65a9f0f071ef7599ec7b20a Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Wed, 15 Apr 2015 15:12:09 -0700 Subject: Rename netconf to controller and NetworkConfigMaster to NetworkController for consistency. --- README.md | 2 - controller/README.md | 38 +++ controller/SqliteNetworkController.cpp | 479 +++++++++++++++++++++++++++++++++ controller/SqliteNetworkController.hpp | 108 ++++++++ controller/schema.sql | 93 +++++++ controller/schema.sql.c | 95 +++++++ controller/schema2c.sh | 8 + make-freebsd.mk | 18 +- make-linux.mk | 18 +- make-mac.mk | 11 +- netconf/README.md | 38 --- netconf/SqliteNetworkConfigMaster.cpp | 479 --------------------------------- netconf/SqliteNetworkConfigMaster.hpp | 108 -------- netconf/netconf-schema.sql | 93 ------- netconf/netconf-schema.sql.c | 95 ------- netconf/schema2c.sh | 8 - node/IncomingPacket.cpp | 28 +- node/Network.cpp | 18 +- node/Network.hpp | 6 +- node/NetworkConfig.cpp | 2 +- node/NetworkConfig.hpp | 8 +- node/NetworkConfigMaster.hpp | 92 ------- node/NetworkController.hpp | 92 +++++++ node/Node.cpp | 10 +- node/Node.hpp | 2 +- node/RuntimeEnvironment.hpp | 8 +- selftest.cpp | 16 +- 27 files changed, 973 insertions(+), 1000 deletions(-) create mode 100644 controller/README.md create mode 100644 controller/SqliteNetworkController.cpp create mode 100644 controller/SqliteNetworkController.hpp create mode 100644 controller/schema.sql create mode 100644 controller/schema.sql.c create mode 100755 controller/schema2c.sh delete mode 100644 netconf/README.md delete mode 100644 netconf/SqliteNetworkConfigMaster.cpp delete mode 100644 netconf/SqliteNetworkConfigMaster.hpp delete mode 100644 netconf/netconf-schema.sql delete mode 100644 netconf/netconf-schema.sql.c delete mode 100755 netconf/schema2c.sh delete mode 100644 node/NetworkConfigMaster.hpp create mode 100644 node/NetworkController.hpp diff --git a/README.md b/README.md index d83f777b..3b9a2956 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,6 @@ You *only* need an account on our site if you want to use the control panel foun Public networks, as the name implies, can be joined without getting authorization from anyone. All you need is their 16-digit network ID. A public network called [Earth](https://www.zerotier.com/earth.html) (8056c2e21c000001) exists for everyone, but be sure your device is adequately secured and up to date before joining. -Alternatively, you can run your own network configuration controller. This lets you run any network for free. To do this, start with the netconf-service/ subfolder of this project. You'll need to do a bit of system administration work and manually populate a Redis database, but it's not terribly hard if you're into that kind of thing. - More products and services will be forthcoming. ### Basic Troubleshooting diff --git a/controller/README.md b/controller/README.md new file mode 100644 index 00000000..6037424e --- /dev/null +++ b/controller/README.md @@ -0,0 +1,38 @@ +Network Controller Implementation +====== + +This folder contains code implementing the node/NetworkController.hpp interface to allow ZeroTier nodes to create and manage virtual networks. + +The standard implementation uses SQLite3 with the attached schema. A separate service (not included here yet) is used to administrate that database and configure networks. + +### Building + +By default this code is not built or included in the client. To build on Linux, BSD, or Mac add ZT_ENABLE_NETCONF_MASTER=1 to the make command line. It could be built on Windows as well, but you're on your own there. You'd have to build SQLite3 first, or get a pre-built copy somewhere. + +### Running + +To enable netconf functionality, place a properly initialized SQLite3 database called **netconf.db** into the ZeroTier working directory of the node you wish to serve network configurations and restart it. If that file is present it will be opened and the network configuration master function will be enabled. You will see this in the log file. + +To initialize a database run: + + sqlite3 -init netconf-schema.sql netconf.db + +Then type '.quit' to exit the SQLite3 command shell. + +Since SQLite3 supports multiple concurrent processes attached to the same database, it's easy to have another process administrate network details while the ZeroTier One service serves them. The schema is simple. Folks with some sysadmin expertise should be able to figure out how to populate a database and get something running. We'll probably publish some code for this at some point in the future, but for now it's all tied up with our zerotier.com web backend. + +One important detail you'll need to know: + +Whenever a network (including associated tables) is changed in any way, its revision number must be incremented. For private networks this is part of the certificate. Certificates are permitted to differ by up to 16 revisions. Therefore, to explicitly and rapidly de-authorize someone you should do a *two-step increment*. This is done with a time delay. First de-authorize the user and increment the revision by one. Then wait 30-60 seconds and increment it by 15. This gives all running clients a chance to get updated certificates before the now-excluded node falls off the revision number horizon. All other changes need only increment once, since a few nodes briefly having a slightly out of date config won't cause any harm. + +### Reliability + +Network configuration masters can go offline without affecting already-configured members of running networks. You just won't be able to add new members, de-authorize members, or otherwise change any network configuration while the master is offline. + +High-availability can be implemented through fail-over. A simple method involves making a frequent backup of the SQLite database (use the SQLite command line client to do this safely) and the network configuration master's working directory. Then, if the master goes down, another instance of it can rapidly be provisioned elsewhere. Since ZeroTier addresses are mobile, the new instance will quickly take over for the old one and service requests. + +### Limits + +A single network configuration master can administrate up to 2^24 networks as per the ZeroTier protocol limit. The number of clients is theoretically unlimited, but in practice is limited by network bandwidth. + +You should keep an eye on CPU utilization and stop adding networks/users to a network configuration master if it gets too high. The bottleneck here is not the SQLite database but the CPU overhead of signing certificates of membership. You'll hit limits there long before hitting any limit associated with SQLite. diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp new file mode 100644 index 00000000..c2e1a168 --- /dev/null +++ b/controller/SqliteNetworkController.cpp @@ -0,0 +1,479 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "SqliteNetworkController.hpp" +#include "../node/Utils.hpp" +#include "../node/CertificateOfMembership.hpp" +#include "../node/NetworkConfig.hpp" + +// Include ZT_NETCONF_SCHEMA_SQL constant to init database +#include "netconf-schema.sql.c" + +// Stored in database as schemaVersion key in Config. +// If not present, database is assumed to be empty and at the current schema version +// and this key/value is added automatically. +#define ZT_NETCONF_SQLITE_SCHEMA_VERSION 1 +#define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "1" + +namespace ZeroTier { + +SqliteNetworkController::SqliteNetworkController(const Identity &signingId,const char *dbPath) : + _signingId(signingId), + _dbPath(dbPath), + _db((sqlite3 *)0) +{ + if (!_signingId.hasPrivate()) + throw std::runtime_error("SqliteNetworkController signing identity must have a private key"); + + if (sqlite3_open_v2(dbPath,&_db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) + throw std::runtime_error("SqliteNetworkController cannot open database file"); + sqlite3_busy_timeout(_db,10000); + + sqlite3_stmt *s = (sqlite3_stmt *)0; + if ((sqlite3_prepare_v2(_db,"SELECT 'v' FROM Config WHERE 'k' = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) { + 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("SqliteNetworkController 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("SqliteNetworkController 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("SqliteNetworkController cannot initialize database and/or insert schemaVersion into Config table"); + } + } + + 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("SqliteNetworkController unable to initialize one or more prepared statements"); + } +} + +SqliteNetworkController::~SqliteNetworkController() +{ + Mutex::Lock _l(_lock); + 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); + } +} + +NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(const InetAddress &fromAddr,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) +{ + Mutex::Lock _l(_lock); + + // 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 NetworkController::NETCONF_QUERY_ACCESS_DENIED; + } + } catch ( ... ) { // identity stored in database is not valid or is NULL + return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + } + } else { + std::string idstr(identity.toString(false)); + 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 NetworkController::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 (!foundNetwork) + return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND; + + // 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); + } + + // 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 NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; + } + } + + // Check member authorization + + if (!member.authorized) + return NetworkController::NETCONF_QUERY_ACCESS_DENIED; + + // 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); + } + + // If netconf is unchanged from client reported revision, just tell client they're up to date + + if ((haveRevision > 0)&&(haveRevision == network.revision)) + return NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER; + + // Generate or retrieve cached netconf + + 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"; + + { + 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; + } + + { + 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::string activeBridges; + sqlite3_reset(_sGetActiveBridges); + sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC); + while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) { + const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0); + if ((ab)&&(strlen(ab) == 10)) { + if (activeBridges.length()) + activeBridges.push_back(','); + activeBridges.append(ab); + } + if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment + break; + } + if (activeBridges.length()) + netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; + } + + if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { + std::string v4s; + + 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; + } + + // TODO: IPv6 auto-assign once it's supported in UI + + 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 (!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); + } + } + + return NetworkController::NETCONF_QUERY_OK; +} + +} // namespace ZeroTier diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp new file mode 100644 index 00000000..566e97d1 --- /dev/null +++ b/controller/SqliteNetworkController.hpp @@ -0,0 +1,108 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_SQLITENETWORKCONTROLLER_HPP +#define ZT_SQLITENETWORKCONTROLLER_HPP + +#include + +#include + +#include +#include +#include + +#include "../node/Constants.hpp" +#include "../node/NetworkController.hpp" +#include "../node/Mutex.hpp" +#include "../node/NonCopyable.hpp" + +namespace ZeroTier { + +class SqliteNetworkController : public NetworkController +{ +public: + class DBC; + friend class SqliteNetworkController::DBC; + + SqliteNetworkController(const Identity &signingId,const char *dbPath); + virtual ~SqliteNetworkController(); + + virtual NetworkController::ResultCode doNetworkConfigRequest( + const InetAddress &fromAddr, + const Identity &identity, + uint64_t nwid, + const Dictionary &metaData, + uint64_t haveRevision, + Dictionary &netconf); + +private: + 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; + +public: + /** + * Provides a safe interface for direct access to this master's database + * + * This acts as both a contextual lock of the master's Mutex and a pointer + * to the Sqlite3 database instance. Dereferencing this with * yields the + * sqlite3* pointer. Create on parent with DBC(SqliteNetworkController &). + */ + class DBC : NonCopyable + { + public: + DBC(SqliteNetworkController &nc) : _p(&nc) { nc._lock.lock(); } + ~DBC() { _p->_lock.unlock(); } + inline sqlite3 *operator*() const throw() { return _p->_db; } + private: + SqliteNetworkController *const _p; + }; +}; + +} // namespace ZeroTier + +#endif diff --git a/controller/schema.sql b/controller/schema.sql new file mode 100644 index 00000000..f5981f1a --- /dev/null +++ b/controller/schema.sql @@ -0,0 +1,93 @@ +CREATE TABLE Config ( + k varchar(16) PRIMARY KEY NOT NULL, + v varchar(1024) NOT NULL +); + +CREATE TABLE IpAssignment ( + networkId char(16) NOT NULL, + nodeId char(10) NOT NULL, + ip blob(16) NOT NULL, + ipNetmaskBits integer NOT NULL DEFAULT(0), + ipVersion integer NOT NULL DEFAULT(4) +); + +CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); + +CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId); + +CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId); + +CREATE TABLE IpAssignmentPool ( + networkId char(16) NOT NULL, + 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); + +CREATE TABLE Member ( + networkId char(16) NOT NULL, + nodeId char(10) NOT NULL, + cachedNetconf blob(4096), + 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); + +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 NOT NULL DEFAULT(0), + preload integer NOT NULL, + maxBalance integer NOT NULL, + accrual integer NOT NULL +); + +CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); + +CREATE TABLE Network ( + id char(16) PRIMARY KEY NOT NULL, + name varchar(128) NOT NULL, + 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 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 NOT NULL DEFAULT(0), + firstSeen integer NOT NULL DEFAULT(0) +); + +CREATE TABLE Rule ( + networkId char(16) NOT NULL, + nodeId char(10), + vlanId integer, + vlanPcp integer, + etherType integer, + macSource char(12), + macDest char(12), + ipSource varchar(64), + ipDest varchar(64), + ipTos integer, + ipProtocol integer, + ipSourcePort integer, + ipDestPort integer, + "action" varchar(4096) NOT NULL DEFAULT('accept') +); + +CREATE INDEX Rule_networkId ON Rule (networkId); \ No newline at end of file diff --git a/controller/schema.sql.c b/controller/schema.sql.c new file mode 100644 index 00000000..fdd51360 --- /dev/null +++ b/controller/schema.sql.c @@ -0,0 +1,95 @@ +#define ZT_NETCONF_SCHEMA_SQL \ +"CREATE TABLE Config (\n"\ +" k varchar(16) PRIMARY KEY NOT NULL,\n"\ +" v varchar(1024) NOT NULL\n"\ +");\n"\ +"\n"\ +"CREATE TABLE IpAssignment (\n"\ +" networkId char(16) NOT NULL,\n"\ +" nodeId char(10) NOT NULL,\n"\ +" ip blob(16) NOT NULL,\n"\ +" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\ +" ipVersion integer NOT NULL DEFAULT(4)\n"\ +");\n"\ +"\n"\ +"CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ +"\n"\ +"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ +"\n"\ +"CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId);\n"\ +"\n"\ +"CREATE TABLE IpAssignmentPool (\n"\ +" networkId char(16) NOT NULL,\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"\ +"\n"\ +"CREATE TABLE Member (\n"\ +" networkId char(16) NOT NULL,\n"\ +" nodeId char(10) NOT NULL,\n"\ +" cachedNetconf blob(4096),\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"\ +"\n"\ +"CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId);\n"\ +"\n"\ +"CREATE TABLE MulticastRate (\n"\ +" networkId char(16) NOT NULL,\n"\ +" mgMac char(12) 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"\ +"\n"\ +"CREATE TABLE Network (\n"\ +" id char(16) PRIMARY KEY NOT NULL,\n"\ +" name varchar(128) NOT NULL,\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 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 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,\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,\n"\ +" ipProtocol integer,\n"\ +" ipSourcePort integer,\n"\ +" ipDestPort integer,\n"\ +" \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\ +");\n"\ +"\n"\ +"CREATE INDEX Rule_networkId ON Rule (networkId);\n"\ +"" diff --git a/controller/schema2c.sh b/controller/schema2c.sh new file mode 100755 index 00000000..4f4f1647 --- /dev/null +++ b/controller/schema2c.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Run this file to package the .sql file into a .c file whenever the SQL changes. + +rm -f schema.sql.c +echo '#define ZT_NETCONF_SCHEMA_SQL \' >schema.sql.c +cat schema.sql | sed 's/"/\\"/g' | sed 's/^/"/' | sed 's/$/\\n"\\/' >>schema.sql.c +echo '""' >>schema.sql.c diff --git a/make-freebsd.mk b/make-freebsd.mk index 4d73a4eb..37968bdd 100644 --- a/make-freebsd.mk +++ b/make-freebsd.mk @@ -7,7 +7,6 @@ LIBS= include objects.mk OBJS+=osdep/BSDEthernetTap.o -TESTNET_OBJS=testnet/SimNet.o testnet/SimNetSocketManager.o testnet/TestEthernetTap.o # Enable SSE-optimized Salsa20 on x86 and x86_64 machines MACHINE=$(shell uname -m) @@ -30,13 +29,6 @@ ifeq ($(MACHINE),x86) DEFS+=-DZT_SALSA20_SSE endif -# Build with ZT_ENABLE_NETCONF_MASTER=1 to build with NetworkConfigMaster enabled -ifeq ($(ZT_ENABLE_NETCONF_MASTER),1) - DEFS+=-DZT_ENABLE_NETCONF_MASTER - LIBS+=-lsqlite3 - OBJS+=netconf/SqliteNetworkConfigMaster.o -endif - # "make official" is a shortcut for this ifeq ($(ZT_OFFICIAL_RELEASE),1) ZT_AUTO_UPDATE=1 @@ -65,8 +57,8 @@ CXXFLAGS=$(CFLAGS) -fno-rtti all: one -one: $(OBJS) main.o - $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one main.o $(OBJS) $(LIBS) +one: $(OBJS) one.o + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS) $(STRIP) zerotier-one ln -sf zerotier-one zerotier-cli ln -sf zerotier-one zerotier-idtool @@ -75,16 +67,12 @@ selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) $(STRIP) zerotier-selftest -testnet: $(TESTNET_OBJS) $(OBJS) testnet.o - $(CXX) $(CXXFLAGS) -o zerotier-testnet testnet.o $(OBJS) $(TESTNET_OBJS) $(LIBS) - $(STRIP) zerotier-testnet - # No installer on FreeBSD yet #installer: one FORCE # ./buildinstaller.sh clean: - rm -rf *.o netconf/*.o node/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o build-* zerotier-* ZeroTierOneInstaller-* + rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o build-* zerotier-* ZeroTierOneInstaller-* debug: FORCE make -j 4 ZT_DEBUG=1 diff --git a/make-linux.mk b/make-linux.mk index d06f6cf7..c0fe973e 100644 --- a/make-linux.mk +++ b/make-linux.mk @@ -8,7 +8,6 @@ LIBS= include objects.mk OBJS+=osdep/LinuxEthernetTap.o -TESTNET_OBJS=testnet/SimNet.o testnet/SimNetSocketManager.o testnet/TestEthernetTap.o # Enable SSE-optimized Salsa20 on x86 and x86_64 machines MACHINE=$(shell uname -m) @@ -31,13 +30,6 @@ ifeq ($(MACHINE),x86) DEFS+=-DZT_SALSA20_SSE endif -# Build with ZT_ENABLE_NETCONF_MASTER=1 to build with NetworkConfigMaster enabled -ifeq ($(ZT_ENABLE_NETCONF_MASTER),1) - DEFS+=-DZT_ENABLE_NETCONF_MASTER - LIBS+=-lsqlite3 - OBJS+=netconf/SqliteNetworkConfigMaster.o -endif - # "make official" is a shortcut for this ifeq ($(ZT_OFFICIAL_RELEASE),1) ZT_AUTO_UPDATE=1 @@ -71,8 +63,8 @@ CXXFLAGS=$(CFLAGS) -fno-rtti all: one -one: $(OBJS) main.o - $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one main.o $(OBJS) $(LIBS) +one: $(OBJS) one.o + $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS) $(STRIP) zerotier-one ln -sf zerotier-one zerotier-cli ln -sf zerotier-one zerotier-idtool @@ -81,15 +73,11 @@ selftest: $(OBJS) selftest.o $(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS) $(STRIP) zerotier-selftest -testnet: $(TESTNET_OBJS) $(OBJS) testnet.o - $(CXX) $(CXXFLAGS) -o zerotier-testnet testnet.o $(OBJS) $(TESTNET_OBJS) $(LIBS) - $(STRIP) zerotier-testnet - installer: one FORCE ./buildinstaller.sh clean: - rm -rf *.o netconf/*.o node/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-* build-* ZeroTierOneInstaller-* *.deb *.rpm + rm -rf *.o node/*.o controller/*.o osdep/*.o service/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-* build-* ZeroTierOneInstaller-* *.deb *.rpm debug: FORCE make -j 4 ZT_DEBUG=1 diff --git a/make-mac.mk b/make-mac.mk index 66e5b6d8..649ea214 100644 --- a/make-mac.mk +++ b/make-mac.mk @@ -26,12 +26,11 @@ ifeq ($(ZT_AUTO_UPDATE),1) DEFS+=-DZT_AUTO_UPDATE endif -# Build with ZT_ENABLE_NETCONF_MASTER=1 to build with NetworkConfigMaster enabled -ifeq ($(ZT_ENABLE_NETCONF_MASTER),1) - DEFS+=-DZT_ENABLE_NETCONF_MASTER +# Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller +ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1) + DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER LIBS+=-L/usr/local/lib -lsqlite3 - ARCH_FLAGS=-arch x86_64 - OBJS+=netconf/SqliteNetworkConfigMaster.o + OBJS+=controller/SqliteNetworkController.o endif # Enable SSE-optimized Salsa20 -- all Intel macs support SSE2 @@ -77,7 +76,7 @@ selftest: $(OBJS) selftest.o # $(CODESIGN) -vvv "build-ZeroTierUI-release/ZeroTier One.app" clean: - rm -rf *.dSYM build-* *.pkg *.dmg *.o netconf/*.o service/*.o node/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-* ZeroTierOneInstaller-* + rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o zerotier-* ZeroTierOneInstaller-* # For our use -- builds official signed binary, packages in installer and download DMG official: FORCE diff --git a/netconf/README.md b/netconf/README.md deleted file mode 100644 index 41ff27de..00000000 --- a/netconf/README.md +++ /dev/null @@ -1,38 +0,0 @@ -Network Configuration Master -====== - -This folder contains code implementing the node/NetworkConfigMaster.hpp interface to allow ZeroTier nodes to create and manage virtual networks. - -The standard implementation uses SQLite3 with the attached schema. A separate service (not included here yet) is used to administrate that database and configure networks. - -### Building - -By default this code is not built or included in the client. To build on Linux, BSD, or Mac add ZT_ENABLE_NETCONF_MASTER=1 to the make command line. It could be built on Windows as well, but you're on your own there. You'd have to build SQLite3 first, or get a pre-built copy somewhere. - -### Running - -To enable netconf functionality, place a properly initialized SQLite3 database called **netconf.db** into the ZeroTier working directory of the node you wish to serve network configurations and restart it. If that file is present it will be opened and the network configuration master function will be enabled. You will see this in the log file. - -To initialize a database run: - - sqlite3 -init netconf-schema.sql netconf.db - -Then type '.quit' to exit the SQLite3 command shell. - -Since SQLite3 supports multiple concurrent processes attached to the same database, it's easy to have another process administrate network details while the ZeroTier One service serves them. The schema is simple. Folks with some sysadmin expertise should be able to figure out how to populate a database and get something running. We'll probably publish some code for this at some point in the future, but for now it's all tied up with our zerotier.com web backend. - -One important detail you'll need to know: - -Whenever a network (including associated tables) is changed in any way, its revision number must be incremented. For private networks this is part of the certificate. Certificates are permitted to differ by up to 16 revisions. Therefore, to explicitly and rapidly de-authorize someone you should do a *two-step increment*. This is done with a time delay. First de-authorize the user and increment the revision by one. Then wait 30-60 seconds and increment it by 15. This gives all running clients a chance to get updated certificates before the now-excluded node falls off the revision number horizon. All other changes need only increment once, since a few nodes briefly having a slightly out of date config won't cause any harm. - -### Reliability - -Network configuration masters can go offline without affecting already-configured members of running networks. You just won't be able to add new members, de-authorize members, or otherwise change any network configuration while the master is offline. - -High-availability can be implemented through fail-over. A simple method involves making a frequent backup of the SQLite database (use the SQLite command line client to do this safely) and the network configuration master's working directory. Then, if the master goes down, another instance of it can rapidly be provisioned elsewhere. Since ZeroTier addresses are mobile, the new instance will quickly take over for the old one and service requests. - -### Limits - -A single network configuration master can administrate up to 2^24 networks as per the ZeroTier protocol limit. The number of clients is theoretically unlimited, but in practice is limited by network bandwidth. - -You should keep an eye on CPU utilization and stop adding networks/users to a network configuration master if it gets too high. The bottleneck here is not the SQLite database but the CPU overhead of signing certificates of membership. You'll hit limits there long before hitting any limit associated with SQLite. diff --git a/netconf/SqliteNetworkConfigMaster.cpp b/netconf/SqliteNetworkConfigMaster.cpp deleted file mode 100644 index d74b0e0f..00000000 --- a/netconf/SqliteNetworkConfigMaster.cpp +++ /dev/null @@ -1,479 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "SqliteNetworkConfigMaster.hpp" -#include "../node/Utils.hpp" -#include "../node/CertificateOfMembership.hpp" -#include "../node/NetworkConfig.hpp" - -// Include ZT_NETCONF_SCHEMA_SQL constant to init database -#include "netconf-schema.sql.c" - -// Stored in database as schemaVersion key in Config. -// If not present, database is assumed to be empty and at the current schema version -// and this key/value is added automatically. -#define ZT_NETCONF_SQLITE_SCHEMA_VERSION 1 -#define ZT_NETCONF_SQLITE_SCHEMA_VERSION_STR "1" - -namespace ZeroTier { - -SqliteNetworkConfigMaster::SqliteNetworkConfigMaster(const Identity &signingId,const char *dbPath) : - _signingId(signingId), - _dbPath(dbPath), - _db((sqlite3 *)0) -{ - if (!_signingId.hasPrivate()) - throw std::runtime_error("SqliteNetworkConfigMaster signing identity must have a private key"); - - if (sqlite3_open_v2(dbPath,&_db,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) - throw std::runtime_error("SqliteNetworkConfigMaster cannot open database file"); - sqlite3_busy_timeout(_db,10000); - - sqlite3_stmt *s = (sqlite3_stmt *)0; - if ((sqlite3_prepare_v2(_db,"SELECT 'v' FROM Config WHERE 'k' = 'schemaVersion';",-1,&s,(const char **)0) == SQLITE_OK)&&(s)) { - 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 ( - (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 unable to initialize one or more prepared statements"); - } -} - -SqliteNetworkConfigMaster::~SqliteNetworkConfigMaster() -{ - Mutex::Lock _l(_lock); - 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,const Identity &identity,uint64_t nwid,const Dictionary &metaData,uint64_t haveRevision,Dictionary &netconf) -{ - Mutex::Lock _l(_lock); - - // 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; - } - } - - // 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 (!foundNetwork) - return NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND; - - // 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); - } - - // 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; - } - } - - // Check member authorization - - if (!member.authorized) - return NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED; - - // 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); - } - - // If netconf is unchanged from client reported revision, just tell client they're up to date - - if ((haveRevision > 0)&&(haveRevision == network.revision)) - return NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER; - - // Generate or retrieve cached netconf - - 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"; - - { - 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; - } - - { - 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::string activeBridges; - sqlite3_reset(_sGetActiveBridges); - sqlite3_bind_text(_sGetActiveBridges,1,network.id,16,SQLITE_STATIC); - while (sqlite3_step(_sGetActiveBridges) == SQLITE_ROW) { - const char *ab = (const char *)sqlite3_column_text(_sGetActiveBridges,0); - if ((ab)&&(strlen(ab) == 10)) { - if (activeBridges.length()) - activeBridges.push_back(','); - activeBridges.append(ab); - } - if (activeBridges.length() > 1024) // sanity check -- you can't have too many active bridges at the moment - break; - } - if (activeBridges.length()) - netconf[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridges; - } - - if ((network.v4AssignMode)&&(!strcmp(network.v4AssignMode,"zt"))) { - std::string v4s; - - 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; - } - - // TODO: IPv6 auto-assign once it's supported in UI - - 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 (!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); - } - } - - return NetworkConfigMaster::NETCONF_QUERY_OK; -} - -} // namespace ZeroTier diff --git a/netconf/SqliteNetworkConfigMaster.hpp b/netconf/SqliteNetworkConfigMaster.hpp deleted file mode 100644 index 72dea365..00000000 --- a/netconf/SqliteNetworkConfigMaster.hpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_SQLITENETWORKCONFIGMASTER_HPP -#define ZT_SQLITENETWORKCONFIGMASTER_HPP - -#include - -#include - -#include -#include -#include - -#include "../node/Constants.hpp" -#include "../node/NetworkConfigMaster.hpp" -#include "../node/Mutex.hpp" -#include "../node/NonCopyable.hpp" - -namespace ZeroTier { - -class SqliteNetworkConfigMaster : public NetworkConfigMaster -{ -public: - class DBC; - friend class SqliteNetworkConfigMaster::DBC; - - SqliteNetworkConfigMaster(const Identity &signingId,const char *dbPath); - virtual ~SqliteNetworkConfigMaster(); - - virtual NetworkConfigMaster::ResultCode doNetworkConfigRequest( - const InetAddress &fromAddr, - const Identity &identity, - uint64_t nwid, - const Dictionary &metaData, - uint64_t haveRevision, - Dictionary &netconf); - -private: - 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; - -public: - /** - * Provides a safe interface for direct access to this master's database - * - * This acts as both a contextual lock of the master's Mutex and a pointer - * to the Sqlite3 database instance. Dereferencing this with * yields the - * sqlite3* pointer. Create on parent with DBC(SqliteNetworkConfigMaster &). - */ - class DBC : NonCopyable - { - public: - DBC(SqliteNetworkConfigMaster &nc) : _p(&nc) { nc._lock.lock(); } - ~DBC() { _p->_lock.unlock(); } - inline sqlite3 *operator*() const throw() { return _p->_db; } - private: - SqliteNetworkConfigMaster *const _p; - }; -}; - -} // namespace ZeroTier - -#endif diff --git a/netconf/netconf-schema.sql b/netconf/netconf-schema.sql deleted file mode 100644 index f5981f1a..00000000 --- a/netconf/netconf-schema.sql +++ /dev/null @@ -1,93 +0,0 @@ -CREATE TABLE Config ( - k varchar(16) PRIMARY KEY NOT NULL, - v varchar(1024) NOT NULL -); - -CREATE TABLE IpAssignment ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - ip blob(16) NOT NULL, - ipNetmaskBits integer NOT NULL DEFAULT(0), - ipVersion integer NOT NULL DEFAULT(4) -); - -CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip); - -CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId); - -CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId); - -CREATE TABLE IpAssignmentPool ( - networkId char(16) NOT NULL, - 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); - -CREATE TABLE Member ( - networkId char(16) NOT NULL, - nodeId char(10) NOT NULL, - cachedNetconf blob(4096), - 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); - -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 NOT NULL DEFAULT(0), - preload integer NOT NULL, - maxBalance integer NOT NULL, - accrual integer NOT NULL -); - -CREATE INDEX MulticastRate_networkId ON MulticastRate (networkId); - -CREATE TABLE Network ( - id char(16) PRIMARY KEY NOT NULL, - name varchar(128) NOT NULL, - 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 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 NOT NULL DEFAULT(0), - firstSeen integer NOT NULL DEFAULT(0) -); - -CREATE TABLE Rule ( - networkId char(16) NOT NULL, - nodeId char(10), - vlanId integer, - vlanPcp integer, - etherType integer, - macSource char(12), - macDest char(12), - ipSource varchar(64), - ipDest varchar(64), - ipTos integer, - ipProtocol integer, - ipSourcePort integer, - ipDestPort integer, - "action" varchar(4096) NOT NULL DEFAULT('accept') -); - -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 deleted file mode 100644 index fdd51360..00000000 --- a/netconf/netconf-schema.sql.c +++ /dev/null @@ -1,95 +0,0 @@ -#define ZT_NETCONF_SCHEMA_SQL \ -"CREATE TABLE Config (\n"\ -" k varchar(16) PRIMARY KEY NOT NULL,\n"\ -" v varchar(1024) NOT NULL\n"\ -");\n"\ -"\n"\ -"CREATE TABLE IpAssignment (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" ip blob(16) NOT NULL,\n"\ -" ipNetmaskBits integer NOT NULL DEFAULT(0),\n"\ -" ipVersion integer NOT NULL DEFAULT(4)\n"\ -");\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId_ip ON IpAssignment (networkId, ip);\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId_nodeId ON IpAssignment (networkId, nodeId);\n"\ -"\n"\ -"CREATE INDEX IpAssignment_networkId ON IpAssignment (networkId);\n"\ -"\n"\ -"CREATE TABLE IpAssignmentPool (\n"\ -" networkId char(16) NOT NULL,\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"\ -"\n"\ -"CREATE TABLE Member (\n"\ -" networkId char(16) NOT NULL,\n"\ -" nodeId char(10) NOT NULL,\n"\ -" cachedNetconf blob(4096),\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"\ -"\n"\ -"CREATE UNIQUE INDEX Member_networkId_nodeId ON Member (networkId, nodeId);\n"\ -"\n"\ -"CREATE TABLE MulticastRate (\n"\ -" networkId char(16) NOT NULL,\n"\ -" mgMac char(12) 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"\ -"\n"\ -"CREATE TABLE Network (\n"\ -" id char(16) PRIMARY KEY NOT NULL,\n"\ -" name varchar(128) NOT NULL,\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 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 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,\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,\n"\ -" ipProtocol integer,\n"\ -" ipSourcePort integer,\n"\ -" ipDestPort integer,\n"\ -" \"action\" varchar(4096) NOT NULL DEFAULT('accept')\n"\ -");\n"\ -"\n"\ -"CREATE INDEX Rule_networkId ON Rule (networkId);\n"\ -"" diff --git a/netconf/schema2c.sh b/netconf/schema2c.sh deleted file mode 100755 index 2ef1393a..00000000 --- a/netconf/schema2c.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# Run this file to package the .sql file into a .c file whenever the SQL changes. - -rm -f netconf-schema.sql.c -echo '#define ZT_NETCONF_SCHEMA_SQL \' >netconf-schema.sql.c -cat netconf-schema.sql | sed 's/"/\\"/g' | sed 's/^/"/' | sed 's/$/\\n"\\/' >>netconf-schema.sql.c -echo '""' >>netconf-schema.sql.c diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 67e2ae2a..967f50f2 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -38,7 +38,7 @@ #include "Topology.hpp" #include "Switch.hpp" #include "Peer.hpp" -#include "NetworkConfigMaster.hpp" +#include "NetworkController.hpp" #include "SelfAwareness.hpp" namespace ZeroTier { @@ -361,14 +361,14 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p if (dict.length()) { if (nw->setConfiguration(Dictionary(dict)) == 2) { // 2 == accepted and actually new /* If this configuration was indeed new, we do another - * netconf request with its revision. We do this in - * order to (a) tell the netconf server we got it (it + * controller request with its revision. We do this in + * order to (a) tell the network controller we got it (it * won't send a duplicate if ts == current), and (b) - * get another one if the netconf is changing rapidly + * get another one if the controller is changing rapidly * until we finally have the final version. * - * Note that we don't do this for netconf masters with - * versions <= 1.0.3, since those regenerate a new netconf + * Note that we don't do this for network controllers with + * versions <= 1.0.3, since those regenerate a new controller * with a new revision every time. In that case this double * confirmation would create a race condition. */ const SharedPtr nc(nw->config2()); @@ -672,10 +672,10 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons const uint64_t pid = packetId(); peer->received(RR,_remoteAddress,_linkDesperation,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP); - if (RR->netconfMaster) { + if (RR->localNetworkController) { Dictionary netconf; - switch(RR->netconfMaster->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,peer->identity(),nwid,metaData,haveRevision,netconf)) { - case NetworkConfigMaster::NETCONF_QUERY_OK: { + switch(RR->localNetworkController->doNetworkConfigRequest((h > 0) ? InetAddress() : _remoteAddress,peer->identity(),nwid,metaData,haveRevision,netconf)) { + case NetworkController::NETCONF_QUERY_OK: { const std::string netconfStr(netconf.toString()); if (netconfStr.length() > 0xffff) { // sanity check since field ix 16-bit TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length()); @@ -694,9 +694,9 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons } } } break; - case NetworkConfigMaster::NETCONF_QUERY_OK_BUT_NOT_NEWER: // nothing to do -- netconf has not changed + case NetworkController::NETCONF_QUERY_OK_BUT_NOT_NEWER: // nothing to do -- netconf has not changed break; - case NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND: { + case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append(pid); @@ -705,7 +705,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.armor(peer->key(),true); RR->node->putPacket(_remoteAddress,outp.data(),outp.size(),_linkDesperation); } break; - case NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED: { + case NetworkController::NETCONF_QUERY_ACCESS_DENIED: { Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR); outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append(pid); @@ -714,11 +714,11 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons outp.armor(peer->key(),true); RR->node->putPacket(_remoteAddress,outp.data(),outp.size(),_linkDesperation); } break; - case NetworkConfigMaster::NETCONF_QUERY_INTERNAL_SERVER_ERROR: + case NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR: TRACE("NETWORK_CONFIG_REQUEST failed: internal error: %s",netconf.get("error","(unknown)").c_str()); break; default: - TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkConfigMaster::doNetworkConfigRequest()"); + TRACE("NETWORK_CONFIG_REQUEST failed: invalid return value from NetworkController::doNetworkConfigRequest()"); break; } } else { diff --git a/node/Network.cpp b/node/Network.cpp index 7fa17ef1..1c786d24 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -36,7 +36,7 @@ #include "Switch.hpp" #include "Packet.hpp" #include "Buffer.hpp" -#include "NetworkConfigMaster.hpp" +#include "NetworkController.hpp" namespace ZeroTier { @@ -243,21 +243,21 @@ int Network::setConfiguration(const Dictionary &conf,bool saveToDisk) void Network::requestConfiguration() { - if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, no netconf master + if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config return; if (controller() == RR->identity.address()) { - if (RR->netconfMaster) { + if (RR->localNetworkController) { SharedPtr nconf(config2()); Dictionary newconf; - switch(RR->netconfMaster->doNetworkConfigRequest(InetAddress(),RR->identity,_id,Dictionary(),(nconf) ? nconf->revision() : (uint64_t)0,newconf)) { - case NetworkConfigMaster::NETCONF_QUERY_OK: + switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,_id,Dictionary(),(nconf) ? nconf->revision() : (uint64_t)0,newconf)) { + case NetworkController::NETCONF_QUERY_OK: this->setConfiguration(newconf,true); return; - case NetworkConfigMaster::NETCONF_QUERY_OBJECT_NOT_FOUND: + case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND: this->setNotFound(); return; - case NetworkConfigMaster::NETCONF_QUERY_ACCESS_DENIED: + case NetworkController::NETCONF_QUERY_ACCESS_DENIED: this->setAccessDenied(); return; default: @@ -269,7 +269,7 @@ void Network::requestConfiguration() } } - TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str()); + TRACE("requesting netconf for network %.16llx from controller %s",(unsigned long long)_id,controller().toString().c_str()); Packet outp(controller(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append((uint64_t)_id); outp.append((uint16_t)0); // no meta-data @@ -304,7 +304,7 @@ void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool SharedPtr signer(RR->topology->getPeer(cert.signedBy())); if (!signer) { - // This would be rather odd, since this is our netconf master... could happen + // This would be rather odd, since this is our controller... could happen // if we get packets before we've gotten config. RR->sw->requestWhois(cert.signedBy()); return; diff --git a/node/Network.hpp b/node/Network.hpp index 213b44c5..79ae3a90 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -88,7 +88,7 @@ public: inline uint64_t id() const throw() { return _id; } /** - * @return Address of network's netconf master (most significant 40 bits of ID) + * @return Address of network's controller (most significant 40 bits of ID) */ inline Address controller() throw() { return Address(_id >> 24); } @@ -148,7 +148,7 @@ public: int setConfiguration(const Dictionary &conf,bool saveToDisk = true); /** - * Set netconf failure to 'access denied' -- called in IncomingPacket when netconf master reports this + * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this */ inline void setAccessDenied() { @@ -157,7 +157,7 @@ public: } /** - * Set netconf failure to 'not found' -- called by PacketDecider when netconf master reports this + * Set netconf failure to 'not found' -- called by PacketDecider when controller reports this */ inline void setNotFound() { diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp index a034b15f..6cab4b41 100644 --- a/node/NetworkConfig.cpp +++ b/node/NetworkConfig.cpp @@ -109,7 +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 + _revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers 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 31e47f41..02c5ba3f 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -68,9 +68,9 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_KEY_RELAYS "rl" /** - * Network configuration received from netconf master nodes + * Network configuration received from network controller nodes * - * This is an immutable value object created from a dictionary received from netconf master. + * This is an immutable value object created from a dictionary received from controller. */ class NetworkConfig { @@ -102,10 +102,10 @@ public: * Create an instance of a NetworkConfig for the test network ID * * The test network ID is defined as ZT_TEST_NETWORK_ID. This is a - * "fake" network with no real netconf master and default options. + * "fake" network with no real controller and default options. * * @param self This node's ZT address - * @return Configured instance of netconf for test network ID + * @return Configuration for test network ID */ static SharedPtr createTestNetworkConfig(const Address &self); diff --git a/node/NetworkConfigMaster.hpp b/node/NetworkConfigMaster.hpp deleted file mode 100644 index f3b013e4..00000000 --- a/node/NetworkConfigMaster.hpp +++ /dev/null @@ -1,92 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_NETWORKCONFIGMASTER_HPP -#define ZT_NETWORKCONFIGMASTER_HPP - -#include - -#include "Constants.hpp" -#include "InetAddress.hpp" -#include "Dictionary.hpp" -#include "Address.hpp" -#include "Identity.hpp" - -namespace ZeroTier { - -class RuntimeEnvironment; - -/** - * Interface for network configuration (netconf) master implementations - */ -class NetworkConfigMaster -{ -public: - /** - * Return value of doNetworkConfigRequest - */ - enum ResultCode - { - NETCONF_QUERY_OK = 0, - NETCONF_QUERY_OK_BUT_NOT_NEWER = 1, - NETCONF_QUERY_OBJECT_NOT_FOUND = 2, - NETCONF_QUERY_ACCESS_DENIED = 3, - NETCONF_QUERY_INTERNAL_SERVER_ERROR = 4 - }; - - NetworkConfigMaster() {} - virtual ~NetworkConfigMaster() {} - - /** - * Handle a network config request, sending replies if necessary - * - * This call is permitted to block, and may be called concurrently from more - * than one thread. Implementations must use locks if needed. - * - * On internal server errors, the 'error' field in result can be filled in - * to indicate the error. - * - * @param fromAddr Originating wire address or null address if packet is not direct (or from self) - * @param identity Originating peer ZeroTier identity - * @param nwid 64-bit network ID - * @param metaData Meta-data bundled with request (empty 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, - const Identity &identity, - uint64_t nwid, - const Dictionary &metaData, - uint64_t haveRevision, - Dictionary &result) = 0; -}; - -} // namespace ZeroTier - -#endif diff --git a/node/NetworkController.hpp b/node/NetworkController.hpp new file mode 100644 index 00000000..32b8f053 --- /dev/null +++ b/node/NetworkController.hpp @@ -0,0 +1,92 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2015 ZeroTier, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * -- + * + * ZeroTier may be used and distributed under the terms of the GPLv3, which + * are available at: http://www.gnu.org/licenses/gpl-3.0.html + * + * If you would like to embed ZeroTier into a commercial application or + * redistribute it in a modified binary form, please contact ZeroTier Networks + * LLC. Start here: http://www.zerotier.com/ + */ + +#ifndef ZT_NETWORKCONFIGMASTER_HPP +#define ZT_NETWORKCONFIGMASTER_HPP + +#include + +#include "Constants.hpp" +#include "InetAddress.hpp" +#include "Dictionary.hpp" +#include "Address.hpp" +#include "Identity.hpp" + +namespace ZeroTier { + +class RuntimeEnvironment; + +/** + * Interface for network configuration (netconf) master implementations + */ +class NetworkController +{ +public: + /** + * Return value of doNetworkConfigRequest + */ + enum ResultCode + { + NETCONF_QUERY_OK = 0, + NETCONF_QUERY_OK_BUT_NOT_NEWER = 1, + NETCONF_QUERY_OBJECT_NOT_FOUND = 2, + NETCONF_QUERY_ACCESS_DENIED = 3, + NETCONF_QUERY_INTERNAL_SERVER_ERROR = 4 + }; + + NetworkController() {} + virtual ~NetworkController() {} + + /** + * Handle a network config request, sending replies if necessary + * + * This call is permitted to block, and may be called concurrently from more + * than one thread. Implementations must use locks if needed. + * + * On internal server errors, the 'error' field in result can be filled in + * to indicate the error. + * + * @param fromAddr Originating wire address or null address if packet is not direct (or from self) + * @param identity Originating peer ZeroTier identity + * @param nwid 64-bit network ID + * @param metaData Meta-data bundled with request (empty 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 NetworkController::ResultCode doNetworkConfigRequest( + const InetAddress &fromAddr, + const Identity &identity, + uint64_t nwid, + const Dictionary &metaData, + uint64_t haveRevision, + Dictionary &result) = 0; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Node.cpp b/node/Node.cpp index c6ae54bd..801d4078 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -30,7 +30,7 @@ #include "Constants.hpp" #include "Node.hpp" #include "RuntimeEnvironment.hpp" -#include "NetworkConfigMaster.hpp" +#include "NetworkController.hpp" #include "CMWC4096.hpp" #include "Switch.hpp" #include "Multicaster.hpp" @@ -407,9 +407,9 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -void Node::setNetconfMaster(void *networkConfigMasterInstance) +void Node::setNetconfMaster(void *networkControllerInstance) { - RR->netconfMaster = reinterpret_cast(networkConfigMasterInstance); + RR->localNetworkController = reinterpret_cast(networkControllerInstance); } /****************************************************************************/ @@ -654,10 +654,10 @@ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr) } catch ( ... ) {} } -void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkConfigMasterInstance) +void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) { try { - reinterpret_cast(node)->setNetconfMaster(networkConfigMasterInstance); + reinterpret_cast(node)->setNetconfMaster(networkControllerInstance); } catch ( ... ) {} } diff --git a/node/Node.hpp b/node/Node.hpp index 429e5171..f07776da 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -105,7 +105,7 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - void setNetconfMaster(void *networkConfigMasterInstance); + void setNetconfMaster(void *networkControllerInstance); // Internal functions ------------------------------------------------------ diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 6bb8c3cf..228040e7 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -42,7 +42,7 @@ class CMWC4096; class Node; class Multicaster; class AntiRecursion; -class NetworkConfigMaster; +class NetworkController; class SelfAwareness; /** @@ -54,7 +54,7 @@ public: RuntimeEnvironment(Node *n) : node(n), identity(), - netconfMaster((NetworkConfigMaster *)0), + localNetworkController((NetworkController *)0), prng((CMWC4096 *)0), sw((Switch *)0), mc((Multicaster *)0), @@ -72,8 +72,8 @@ public: std::string publicIdentityStr; std::string secretIdentityStr; - // This is set externally to an instance of this base class if netconf functionality is enabled - NetworkConfigMaster *netconfMaster; + // This is set externally to an instance of this base class + NetworkController *localNetworkController; /* * Order matters a bit here. These are constructed in this order diff --git a/selftest.cpp b/selftest.cpp index 465e0088..8c8c6fa1 100644 --- a/selftest.cpp +++ b/selftest.cpp @@ -59,9 +59,9 @@ #include "osdep/Phy.hpp" #endif -#ifdef ZT_ENABLE_NETCONF_MASTER -#include "netconf/SqliteNetworkConfigMaster.hpp" -#endif // ZT_ENABLE_NETCONF_MASTER +#ifdef ZT_ENABLE_NETWORK_CONTROLLER +#include "controller/SqliteNetworkController.hpp" +#endif // ZT_ENABLE_NETWORK_CONTROLLER #ifdef __WINDOWS__ #include @@ -726,14 +726,14 @@ static int testPhy() static int testSqliteNetconfMaster() { -#ifdef ZT_ENABLE_NETCONF_MASTER +#ifdef ZT_ENABLE_NETWORK_CONTROLLER try { - std::cout << "[netconf] Generating signing identity..." << std::endl; + std::cout << "[network-controller] Generating signing identity..." << std::endl; Identity signingId; signingId.generate(); - std::cout << "[netconf] Creating database..." << std::endl; - SqliteNetworkConfigMaster netconf(signingId,"netconf-test.db"); + std::cout << "[network-controller] Creating database..." << std::endl; + SqliteNetworkController controller(signingId,"network-controller-test.db"); } catch (std::runtime_error &exc) { std::cout << "FAIL! (unexpected exception: " << exc.what() << ")" << std::endl; return -1; @@ -741,7 +741,7 @@ static int testSqliteNetconfMaster() std::cout << "FAIL! (unexpected exception: ...)" << std::endl; return -1; } -#endif // ZT_ENABLE_NETCONF_MASTER +#endif // ZT_ENABLE_NETWORK_CONTROLLER return 0; } -- cgit v1.2.3