Merge branch 'adamierymenko-dev' into android-jni

This commit is contained in:
Grant Limberg 2015-04-22 18:14:14 -07:00
commit b33e4af49f
9 changed files with 822 additions and 88 deletions

View File

@ -36,14 +36,20 @@
#include <algorithm> #include <algorithm>
#include <utility> #include <utility>
#include <stdexcept> #include <stdexcept>
#include <set>
#include "../include/ZeroTierOne.h" #include "../include/ZeroTierOne.h"
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../ext/json-parser/json.h"
#include "SqliteNetworkController.hpp" #include "SqliteNetworkController.hpp"
#include "../node/Utils.hpp" #include "../node/Utils.hpp"
#include "../node/CertificateOfMembership.hpp" #include "../node/CertificateOfMembership.hpp"
#include "../node/NetworkConfig.hpp" #include "../node/NetworkConfig.hpp"
#include "../node/InetAddress.hpp"
#include "../node/MAC.hpp"
#include "../node/Address.hpp"
#include "../osdep/OSUtils.hpp" #include "../osdep/OSUtils.hpp"
// Include ZT_NETCONF_SCHEMA_SQL constant to init database // Include ZT_NETCONF_SCHEMA_SQL constant to init database
@ -60,6 +66,57 @@
namespace ZeroTier { namespace ZeroTier {
namespace {
static std::string _jsonEscape(const char *s)
{
if (!s)
return std::string();
std::string buf;
for(const char *p=s;(*p);++p) {
switch(*p) {
case '\t': buf.append("\\t"); break;
case '\b': buf.append("\\b"); break;
case '\r': buf.append("\\r"); break;
case '\n': buf.append("\\n"); break;
case '\f': buf.append("\\f"); break;
case '"': buf.append("\\\""); break;
case '\\': buf.append("\\\\"); break;
case '/': buf.append("\\/"); break;
default: buf.push_back(*p); break;
}
}
return buf;
}
static std::string _jsonEscape(const std::string &s) { return _jsonEscape(s.c_str()); }
struct MemberRecord {
int64_t rowid;
char nodeId[16];
int cachedNetconfBytes;
const void *cachedNetconf;
uint64_t cachedNetconfRevision;
uint64_t cachedNetconfTimestamp;
uint64_t clientReportedRevision;
bool authorized;
bool activeBridge;
};
struct NetworkRecord {
char id[24];
const char *name;
const char *v4AssignMode;
const char *v6AssignMode;
bool isPrivate;
bool enableBroadcast;
bool allowPassiveBridging;
int multicastLimit;
uint64_t creationTime;
uint64_t revision;
};
} // anonymous namespace
SqliteNetworkController::SqliteNetworkController(const char *dbPath) : SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
_dbPath(dbPath), _dbPath(dbPath),
_db((sqlite3 *)0) _db((sqlite3 *)0)
@ -95,8 +152,8 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
} }
if ( if (
(sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,v4AssignMode,v6AssignMode,multicastLimit,revision FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK) (sqlite3_prepare_v2(_db,"SELECT name,private,enableBroadcast,allowPassiveBridging,v4AssignMode,v6AssignMode,multicastLimit,creationTime,revision FROM Network WHERE id = ?",-1,&_sGetNetworkById,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT rowid,cachedNetconf,cachedNetconfRevision,cachedNetconfTimestamp,clientReportedRevision,authorized,activeBridge FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sGetMemberByNetworkAndNodeId,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT rowid,cachedNetconf,cachedNetconfRevision,cachedNetconfTimestamp,clientReportedRevision,authorized,activeBridge FROM Member WHERE networkId = ? AND nodeId = ?",-1,&_sGetMember,(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,"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,"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,"INSERT INTO Node (id,identity,lastAt,lastSeen,firstSeen) VALUES (?,?,?,?,?)",-1,&_sCreateNode,(const char **)0) != SQLITE_OK)
@ -106,12 +163,27 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
||(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 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 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 activeBridge > 0 AND authorized > 0",-1,&_sGetActiveBridges,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT nodeId FROM Member WHERE networkId = ? AND activeBridge > 0 AND authorized > 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 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 ipNetwork,ipNetmaskBits FROM IpAssignmentPool WHERE networkId = ? AND ipVersion = ?",-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,"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,"INSERT INTO IpAssignment (networkId,nodeId,ip,ipNetmaskBits,ipVersion) VALUES (?,?,?,?,?)",-1,&_sAllocateIp,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Member SET cachedNetconf = ?,cachedNetconfRevision = ? WHERE rowid = ?",-1,&_sCacheNetconf,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"UPDATE Member SET cachedNetconf = ?,cachedNetconfRevision = ? WHERE rowid = ?",-1,&_sCacheNetconf,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT DISTINCT nodeId,address FROM Relay WHERE networkId = ?",-1,&_sGetRelays,(const char **)0) != SQLITE_OK) ||(sqlite3_prepare_v2(_db,"SELECT nodeId,phyAddress FROM Relay WHERE networkId = ? ORDER BY nodeId ASC",-1,&_sGetRelays,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT id FROM Network ORDER BY id ASC",-1,&_sListNetworks,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT m.authorized,m.activeBridge,n.id,n.lastAt,n.lastSeen,n.firstSeen FROM Member AS m,Node AS n WHERE m.networkId = ? AND n.id = m.nodeId ORDER BY n.id ASC",-1,&_sListNetworkMembers,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT m.authorized,m.activeBridge,n.identity,n.lastAt,n.lastSeen,n.firstSeen FROM Member AS m,Node AS n WHERE m.networkId = ? AND m.nodeId = ?",-1,&_sGetMember2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ipNetwork,ipNetmaskBits,ipVersion FROM IpAssignmentPool WHERE networkId = ? ORDER BY ipNetwork ASC",-1,&_sGetIpAssignmentPools2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ruleId,nodeId,vlanId,vlanPcp,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\" FROM Rule WHERE networkId = ? ORDER BY ruleId ASC",-1,&_sListRules,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Rule (networkId,ruleId,nodeId,vlanId,vlanPcP,etherType,macSource,macDest,ipSource,ipDest,ipTos,ipProtocol,ipSourcePort,ipDestPort,\"action\") VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",-1,&_sCreateRule,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Network (networkId,name,creationTime,revision) VALUES (?,?,?,1)",-1,&_sCreateNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"UPDATE Network SET ? = ? WHERE networkId = ?",-1,&_sUpdateNetworkField,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT revision FROM Network WHERE id = ?",-1,&_sGetNetworkRevision,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"SELECT ip,ipNetmaskBits,ipVersion FROM IpAssignment WHERE networkId = ? AND nodeId = ?",-1,&_sGetIpAssignmentsForNode2,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Relay WHERE networkId = ?",-1,&_sDeleteRelaysForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO Relay (networkId,nodeId,phyAddress) VALUES (?,?,?)",-1,&_sCreateRelay,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM IpAssignmentPool WHERE networkId = ?",-1,&_sDeleteIpAssignmentPoolsForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"DELETE FROM Rule WHERE networkId = ?",-1,&_sDeleteRulesForNetwork,(const char **)0) != SQLITE_OK)
||(sqlite3_prepare_v2(_db,"INSERT INTO IpAssignmentPool (networkId,ipNetwork,ipNetmaskBits,ipVersion) VALUES (?,?,?,?)",-1,&_sCreateIpAssignmentPool,(const char **)0) != SQLITE_OK)
) { ) {
sqlite3_close(_db); sqlite3_close(_db);
throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements"); throw std::runtime_error("SqliteNetworkController unable to initialize one or more prepared statements");
@ -123,7 +195,7 @@ SqliteNetworkController::~SqliteNetworkController()
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
if (_db) { if (_db) {
sqlite3_finalize(_sGetNetworkById); sqlite3_finalize(_sGetNetworkById);
sqlite3_finalize(_sGetMemberByNetworkAndNodeId); sqlite3_finalize(_sGetMember);
sqlite3_finalize(_sCreateMember); sqlite3_finalize(_sCreateMember);
sqlite3_finalize(_sGetNodeIdentity); sqlite3_finalize(_sGetNodeIdentity);
sqlite3_finalize(_sCreateNode); sqlite3_finalize(_sCreateNode);
@ -139,6 +211,21 @@ SqliteNetworkController::~SqliteNetworkController()
sqlite3_finalize(_sAllocateIp); sqlite3_finalize(_sAllocateIp);
sqlite3_finalize(_sCacheNetconf); sqlite3_finalize(_sCacheNetconf);
sqlite3_finalize(_sGetRelays); sqlite3_finalize(_sGetRelays);
sqlite3_finalize(_sListNetworks);
sqlite3_finalize(_sListNetworkMembers);
sqlite3_finalize(_sGetMember2);
sqlite3_finalize(_sGetIpAssignmentPools2);
sqlite3_finalize(_sListRules);
sqlite3_finalize(_sCreateRule);
sqlite3_finalize(_sCreateNetwork);
sqlite3_finalize(_sUpdateNetworkField);
sqlite3_finalize(_sGetNetworkRevision);
sqlite3_finalize(_sGetIpAssignmentsForNode2);
sqlite3_finalize(_sDeleteRelaysForNetwork);
sqlite3_finalize(_sCreateRelay);
sqlite3_finalize(_sDeleteIpAssignmentPoolsForNetwork);
sqlite3_finalize(_sDeleteRulesForNetwork);
sqlite3_finalize(_sCreateIpAssignmentPool);
sqlite3_close(_db); sqlite3_close(_db);
} }
} }
@ -155,31 +242,11 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR; return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
} }
struct { NetworkRecord network;
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)); memset(&network,0,sizeof(network));
Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid); Utils::snprintf(network.id,sizeof(network.id),"%.16llx",(unsigned long long)nwid);
struct { MemberRecord member;
int64_t rowid;
char nodeId[16];
int cachedNetconfBytes;
const void *cachedNetconf;
uint64_t cachedNetconfRevision;
uint64_t cachedNetconfTimestamp;
uint64_t clientReportedRevision;
bool authorized;
bool activeBridge;
} member;
memset(&member,0,sizeof(member)); memset(&member,0,sizeof(member));
Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt()); Utils::snprintf(member.nodeId,sizeof(member.nodeId),"%.10llx",(unsigned long long)identity.address().toInt());
@ -233,11 +300,9 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
// Fetch Network record // Fetch Network record
bool foundNetwork = false;
sqlite3_reset(_sGetNetworkById); sqlite3_reset(_sGetNetworkById);
sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC); sqlite3_bind_text(_sGetNetworkById,1,network.id,16,SQLITE_STATIC);
if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) { if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
foundNetwork = true;
network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0); network.name = (const char *)sqlite3_column_text(_sGetNetworkById,0);
network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0); network.isPrivate = (sqlite3_column_int(_sGetNetworkById,1) > 0);
network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0); network.enableBroadcast = (sqlite3_column_int(_sGetNetworkById,2) > 0);
@ -245,27 +310,26 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
network.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4); network.v4AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,4);
network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5); network.v6AssignMode = (const char *)sqlite3_column_text(_sGetNetworkById,5);
network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6); network.multicastLimit = sqlite3_column_int(_sGetNetworkById,6);
network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7); network.creationTime = (uint64_t)sqlite3_column_int64(_sGetNetworkById,7);
} network.revision = (uint64_t)sqlite3_column_int64(_sGetNetworkById,8);
if (!foundNetwork) } else return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND;
return NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND;
// Fetch Member record // Fetch Member record
bool foundMember = false; bool foundMember = false;
sqlite3_reset(_sGetMemberByNetworkAndNodeId); sqlite3_reset(_sGetMember);
sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,1,network.id,16,SQLITE_STATIC); sqlite3_bind_text(_sGetMember,1,network.id,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetMemberByNetworkAndNodeId,2,member.nodeId,10,SQLITE_STATIC); sqlite3_bind_text(_sGetMember,2,member.nodeId,10,SQLITE_STATIC);
if (sqlite3_step(_sGetMemberByNetworkAndNodeId) == SQLITE_ROW) { if (sqlite3_step(_sGetMember) == SQLITE_ROW) {
foundMember = true; foundMember = true;
member.rowid = (int64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,0); member.rowid = (int64_t)sqlite3_column_int64(_sGetMember,0);
member.cachedNetconfBytes = sqlite3_column_bytes(_sGetMemberByNetworkAndNodeId,1); member.cachedNetconfBytes = sqlite3_column_bytes(_sGetMember,1);
member.cachedNetconf = sqlite3_column_blob(_sGetMemberByNetworkAndNodeId,1); member.cachedNetconf = sqlite3_column_blob(_sGetMember,1);
member.cachedNetconfRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,2); member.cachedNetconfRevision = (uint64_t)sqlite3_column_int64(_sGetMember,2);
member.cachedNetconfTimestamp = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,3); member.cachedNetconfTimestamp = (uint64_t)sqlite3_column_int64(_sGetMember,3);
member.clientReportedRevision = (uint64_t)sqlite3_column_int64(_sGetMemberByNetworkAndNodeId,4); member.clientReportedRevision = (uint64_t)sqlite3_column_int64(_sGetMember,4);
member.authorized = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,5) > 0); member.authorized = (sqlite3_column_int(_sGetMember,5) > 0);
member.activeBridge = (sqlite3_column_int(_sGetMemberByNetworkAndNodeId,6) > 0); member.activeBridge = (sqlite3_column_int(_sGetMember,6) > 0);
} }
// Create Member record for unknown nodes, auto-authorizing if network is public // Create Member record for unknown nodes, auto-authorizing if network is public
@ -444,13 +508,15 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
if ((ipNetwork)&&(sqlite3_column_bytes(_sGetIpAssignmentPools,0) >= 4)&&(ipNetmaskBits > 0)&&(ipNetmaskBits < 32)) { 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 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 m = 0xffffffff << (32 - ipNetmaskBits); // netmask e.g. 0xffffff00 for '24' since 32 - 24 == 8
n &= m; // sanity check -- ipNetwork bits right of netmask bit count should be zero
uint32_t im = ~m; // inverse mask, e.g. 0x000000ff for a netmask of 0xffffff00 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 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 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 uint32_t ip = ( ((abits + k) & im) | n ); // build IP using bits from ZT address of member + k
if ((ip & 0x000000ff) == 0x00) continue; // no IPs ending in .0 allowed if ((ip & 0xffffff00) == 0) continue; // no IPs ending in .0
if ((ip & 0x000000ff) == 0xff) continue; // no IPs ending in .255 allowed if (ip == n) continue; // no IPs equal to the network e.g. 10.0.0.0 for 10.0.0.0/255.255.255.0
if (ip == (n | im)) continue; // broadcast address e.g. 10.0.0.255 for 10.0.0.0/255.255.255.0
uint32_t nip = Utils::hton(ip); // IP in big-endian "network" byte order uint32_t nip = Utils::hton(ip); // IP in big-endian "network" byte order
sqlite3_reset(_sCheckIfIpIsAllocated); sqlite3_reset(_sCheckIfIpIsAllocated);
@ -514,4 +580,530 @@ NetworkController::ResultCode SqliteNetworkController::doNetworkConfigRequest(co
return NetworkController::NETCONF_QUERY_OK; return NetworkController::NETCONF_QUERY_OK;
} }
unsigned int SqliteNetworkController::handleControlPlaneHttpGET(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType)
{
char json[16384];
if (path.empty())
return 404;
Mutex::Lock _l(_lock);
if (path[0] == "network") {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
sqlite3_reset(_sGetMember2);
sqlite3_bind_text(_sGetMember2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetMember2,2,addrs,10,SQLITE_STATIC);
if (sqlite3_step(_sGetMember2) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\taddress: \"%s\""
"\tauthorized: %s,"
"\tactiveBridge: %s,\n"
"\tlastAt: \"%s\",\n"
"\tlastSeen: %llu,\n"
"\tfirstSeen: %llu,\n"
"\tidentity: \"%s\",\n"
"\tipAssignments: [",
addrs,
(sqlite3_column_int(_sGetMember2,0) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetMember2,1) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,3)).c_str(),
(unsigned long long)sqlite3_column_int64(_sGetMember2,4),
(unsigned long long)sqlite3_column_int64(_sGetMember2,5),
_jsonEscape((const char *)sqlite3_column_text(_sGetMember2,2)).c_str());
responseBody = json;
sqlite3_reset(_sGetIpAssignmentsForNode2);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sGetIpAssignmentsForNode2,2,addrs,10,SQLITE_STATIC);
bool firstIp = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
InetAddress ip((const void *)sqlite3_column_blob(_sGetIpAssignmentsForNode2,0),(sqlite3_column_int(_sGetIpAssignmentsForNode2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
responseBody.append(firstIp ? "\"" : ",\"");
firstIp = false;
responseBody.append(_jsonEscape(ip.toString()));
responseBody.push_back('"');
}
responseBody.append("]\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
} // else 404
} else {
// get network info
sqlite3_reset(_sGetNetworkById);
sqlite3_bind_text(_sGetNetworkById,1,nwids,16,SQLITE_STATIC);
if (sqlite3_step(_sGetNetworkById) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"{\n"
"\tnwid: \"%s\",\n"
"\tname: \"%s\",\n"
"\tprivate: %s,\n"
"\tenableBroadcast: %s,\n"
"\tallowPassiveBridging: %s,\n"
"\tv4AssignMode: \"%s\",\n"
"\tv6AssignMode: \"%s\",\n"
"\tmulticastLimit: %d,\n"
"\tcreationTime: %llu,\n",
"\trevision: %llu,\n"
"\tmembers: [",
nwids,
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,0)).c_str(),
(sqlite3_column_int(_sGetNetworkById,1) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,2) > 0) ? "true" : "false",
(sqlite3_column_int(_sGetNetworkById,3) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,4)).c_str(),
_jsonEscape((const char *)sqlite3_column_text(_sGetNetworkById,5)).c_str(),
sqlite3_column_int(_sGetNetworkById,6),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,7),
(unsigned long long)sqlite3_column_int64(_sGetNetworkById,8));
responseBody = json;
sqlite3_reset(_sListNetworkMembers);
sqlite3_bind_text(_sListNetworkMembers,1,nwids,16,SQLITE_STATIC);
bool firstMember = true;
while (sqlite3_step(_sListNetworkMembers) == SQLITE_ROW) {
Utils::snprintf(json,sizeof(json),
"%s{\n"
"\t\taddress: \"%s\",\n"
"\t\tauthorized: %s,\n"
"\t\tactiveBridge: %s,\n"
"\t\tlastAt: \"%s\",\n"
"\t\tlastSeen: %llu,\n"
"\t\tfirstSeen: %llu\n"
"\t}",
firstMember ? "\n\t" : ",",
(const char *)sqlite3_column_text(_sListNetworkMembers,2),
(sqlite3_column_int(_sListNetworkMembers,0) > 0) ? "true" : "false",
(sqlite3_column_int(_sListNetworkMembers,1) > 0) ? "true" : "false",
_jsonEscape((const char *)sqlite3_column_text(_sListNetworkMembers,3)).c_str(),
(unsigned long long)sqlite3_column_int64(_sListNetworkMembers,4),
(unsigned long long)sqlite3_column_int64(_sListNetworkMembers,5));
responseBody.append(json);
firstMember = false;
}
responseBody.append("],\n\trelays: [");
sqlite3_reset(_sGetRelays);
sqlite3_bind_text(_sGetRelays,1,nwids,16,SQLITE_STATIC);
bool firstRelay = true;
while (sqlite3_step(_sGetRelays) == SQLITE_ROW) {
responseBody.append(firstRelay ? "\n\t\t" : ",\n\t\t");
firstRelay = false;
responseBody.append("{address:\"");
responseBody.append((const char *)sqlite3_column_text(_sGetRelays,0));
responseBody.append("\",phyAddress:\"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sGetRelays,1)));
responseBody.append("\"}");
}
responseBody.append("],\n\tipAssignmentPools: [");
sqlite3_reset(_sGetIpAssignmentPools2);
sqlite3_bind_text(_sGetIpAssignmentPools2,1,nwids,16,SQLITE_STATIC);
bool firstIpAssignmentPool = true;
while (sqlite3_step(_sGetIpAssignmentPools2) == SQLITE_ROW) {
responseBody.append(firstIpAssignmentPool ? "\n\t\t" : ",\n\t\t");
firstIpAssignmentPool = false;
InetAddress ipp((const void *)sqlite3_column_blob(_sGetIpAssignmentPools2,0),(sqlite3_column_int(_sGetIpAssignmentPools2,2) == 6) ? 16 : 4,(unsigned int)sqlite3_column_int(_sGetIpAssignmentPools2,1));
Utils::snprintf(json,sizeof(json),"{network:\"%s\",netmaskBits:%u}",
_jsonEscape(ipp.toIpString()).c_str(),
ipp.netmaskBits());
responseBody.append(json);
}
responseBody.append("],\n\trules: [");
sqlite3_reset(_sListRules);
sqlite3_bind_text(_sListRules,1,nwids,16,SQLITE_STATIC);
bool firstRule = true;
while (sqlite3_step(_sListRules) == SQLITE_ROW) {
responseBody.append(firstRule ? "\n\t{\n" : ",{\n");
Utils::snprintf(json,sizeof(json),"\t\truleId: %lld,\n",sqlite3_column_int64(_sListRules,0));
responseBody.append(json);
if (sqlite3_column_type(_sListRules,1) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tnodeId: \"%s\",\n",(const char *)sqlite3_column_text(_sListRules,1));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,2) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tvlanId: %d,\n",sqlite3_column_int(_sListRules,2));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,3) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tvlanPcp: %d,\n",sqlite3_column_int(_sListRules,3));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,4) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tetherType: %d,\n",sqlite3_column_int(_sListRules,4));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,5) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tmacSource: \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,5)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,6) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tmacDest: \"%s\",\n",MAC((const char *)sqlite3_column_text(_sListRules,6)).toString().c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,7) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipSource: \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,7)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,8) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipDest: \"%s\",\n",_jsonEscape((const char *)sqlite3_column_text(_sListRules,8)).c_str());
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,9) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipTos: %d,\n",sqlite3_column_int(_sListRules,9));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,10) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipProtocol: %d,\n",sqlite3_column_int(_sListRules,10));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,11) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipSourcePort: %d,\n",sqlite3_column_int(_sListRules,11));
responseBody.append(json);
}
if (sqlite3_column_type(_sListRules,12) != SQLITE_NULL) {
Utils::snprintf(json,sizeof(json),"\t\tipDestPort: %d,\n",sqlite3_column_int(_sListRules,12));
responseBody.append(json);
}
responseBody.append("\t\taction: \"");
responseBody.append(_jsonEscape((const char *)sqlite3_column_text(_sListRules,13)));
responseBody.append("\"\n\t}");
}
responseBody.append("]\n}\n");
responseContentType = "application/json";
return 200;
} // else 404
}
} else if (path.size() == 1) {
// list networks
sqlite3_reset(_sListNetworks);
responseContentType = "application/json";
responseBody = "[";
bool first = true;
while (sqlite3_step(_sListNetworks) == SQLITE_ROW) {
if (first) {
first = false;
responseBody.push_back('"');
} else responseBody.append(",\"");
responseBody.append((const char *)sqlite3_column_text(_sListNetworks,0));
responseBody.push_back('"');
}
responseBody.push_back(']');
return 200;
} // else 404
} // else 404
return 404;
}
unsigned int SqliteNetworkController::handleControlPlaneHttpPOST(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType)
{
if (path.empty())
return 404;
Mutex::Lock _l(_lock);
if (path[0] == "network") {
if ((path.size() >= 2)&&(path[1].length() == 16)) {
uint64_t nwid = Utils::hexStrToU64(path[1].c_str());
char nwids[24];
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
int64_t revision = 0;
sqlite3_reset(_sGetNetworkRevision);
sqlite3_bind_text(_sGetNetworkRevision,1,nwids,16,SQLITE_STATIC);
if (sqlite3_step(_sGetNetworkRevision) == SQLITE_ROW)
revision = sqlite3_column_int64(_sGetNetworkRevision,0);
if (path.size() >= 3) {
if ((path.size() == 4)&&(path[2] == "member")&&(path[3].length() == 10)) {
uint64_t address = Utils::hexStrToU64(path[3].c_str());
char addrs[24];
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",address);
return handleControlPlaneHttpGET(path,urlArgs,headers,body,responseBody,responseContentType);
} // else 404
} else {
if (revision <= 0) {
sqlite3_reset(_sCreateNetwork);
sqlite3_bind_text(_sCreateNetwork,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sCreateNetwork,2,nwids,16,SQLITE_STATIC); // default name, will be changed below if a name is specified in JSON
sqlite3_bind_int64(_sCreateNetwork,3,(long long)OSUtils::now());
if (sqlite3_step(_sCreateNetwork) != SQLITE_DONE)
return 500;
}
json_value *j = json_parse(body.c_str(),body.length());
if (j) {
if (j->type == json_object) {
for(unsigned int k=0;k<j->u.object.length;++k) {
sqlite3_reset(_sUpdateNetworkField);
sqlite3_bind_text(_sUpdateNetworkField,3,nwids,16,SQLITE_STATIC);
if (!strcmp(j->u.object.values[k].name,"name")) {
if ((j->u.object.values[k].value->type == json_string)&&(j->u.object.values[k].value->u.string.ptr[0])) {
sqlite3_bind_text(_sUpdateNetworkField,1,"name",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"private")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"private",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"enableBroadcast")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"enableBroadcast",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"allowPassiveBridging")) {
if (j->u.object.values[k].value->type == json_boolean) {
sqlite3_bind_text(_sUpdateNetworkField,1,"allowPassiveBridging",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(j->u.object.values[k].value->u.boolean == 0) ? 0 : 1);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"v4AssignMode")) {
if (j->u.object.values[k].value->type == json_string) {
sqlite3_bind_text(_sUpdateNetworkField,1,"v4AssignMode",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"v6AssignMode")) {
if (j->u.object.values[k].value->type == json_string) {
sqlite3_bind_text(_sUpdateNetworkField,1,"v6AssignMode",-1,SQLITE_STATIC);
sqlite3_bind_text(_sUpdateNetworkField,2,j->u.object.values[k].value->u.string.ptr,-1,SQLITE_STATIC);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"multicastLimit")) {
if (j->u.object.values[k].value->type == json_integer) {
sqlite3_bind_text(_sUpdateNetworkField,1,"multicastLimit",-1,SQLITE_STATIC);
sqlite3_bind_int(_sUpdateNetworkField,2,(int)j->u.object.values[k].value->u.integer);
sqlite3_step(_sUpdateNetworkField);
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"relays")) {
if (j->u.object.values[k].value->type == json_array) {
std::map<Address,InetAddress> nodeIdToPhyAddress;
for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
json_value *relay = j->u.object.values[k].value->u.array.values[kk];
const char *address = (const char *)0;
const char *phyAddress = (const char *)0;
if ((relay)&&(relay->type == json_object)) {
for(unsigned int rk=0;rk<relay->u.object.length;++rk) {
if ((!strcmp(relay->u.object.values[rk].name,"address"))&&(relay->u.object.values[rk].value->type == json_string))
address = relay->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(relay->u.object.values[rk].name,"phyAddress"))&&(relay->u.object.values[rk].value->type == json_string))
phyAddress = relay->u.object.values[rk].value->u.string.ptr;
else return 400;
}
}
if ((address)&&(phyAddress))
nodeIdToPhyAddress[Address(address)] = InetAddress(phyAddress);
}
sqlite3_reset(_sDeleteRelaysForNetwork);
sqlite3_bind_text(_sDeleteRelaysForNetwork,1,nwids,16,SQLITE_STATIC);
sqlite3_step(_sDeleteRelaysForNetwork);
for(std::map<Address,InetAddress>::iterator rl(nodeIdToPhyAddress.begin());rl!=nodeIdToPhyAddress.end();++rl) {
sqlite3_reset(_sCreateRelay);
sqlite3_bind_text(_sCreateRelay,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_text(_sCreateRelay,2,rl->first.toString().c_str(),-1,SQLITE_STATIC);
sqlite3_bind_text(_sCreateRelay,3,rl->second.toString().c_str(),-1,SQLITE_STATIC);
sqlite3_step(_sCreateRelay);
}
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"ipAssignmentPools")) {
if (j->u.object.values[k].value->type == json_array) {
std::set<InetAddress> pools;
for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
json_value *pool = j->u.object.values[k].value->u.array.values[kk];
const char *net = (const char *)0;
int bits = 0;
if ((pool)&&(pool->type == json_object)) {
for(unsigned int rk=0;rk<pool->u.object.length;++rk) {
if ((!strcmp(pool->u.object.values[rk].name,"network"))&&(pool->u.object.values[rk].value->type == json_string))
net = pool->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(pool->u.object.values[rk].name,"netmaskBits"))&&(pool->u.object.values[rk].value->type == json_integer))
bits = (int)pool->u.object.values[rk].value->u.integer;
else return 400;
}
}
if ((net)&&(bits > 0)) {
char tmp[128];
Utils::snprintf(tmp,sizeof(tmp),"%s/%d",net,bits);
InetAddress n(tmp);
if (((n.ss_family == AF_INET)&&(n.netmaskBits() < 32))||((n.ss_family == AF_INET6)&&(n.netmaskBits() < 128)))
pools.insert(n);
}
sqlite3_reset(_sDeleteIpAssignmentPoolsForNetwork);
sqlite3_bind_text(_sDeleteIpAssignmentPoolsForNetwork,1,nwids,16,SQLITE_STATIC);
sqlite3_step(_sDeleteIpAssignmentPoolsForNetwork);
for(std::set<InetAddress>::const_iterator p(pools.begin());p!=pools.end();++p) {
sqlite3_reset(_sCreateIpAssignmentPool);
sqlite3_bind_text(_sCreateIpAssignmentPool,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_blob(_sCreateIpAssignmentPool,2,p->rawIpData(),(p->ss_family == AF_INET6) ? 16 : 4,SQLITE_STATIC);
sqlite3_bind_int(_sCreateIpAssignmentPool,3,(int)p->netmaskBits());
sqlite3_bind_int(_sCreateIpAssignmentPool,4,(p->ss_family == AF_INET6) ? 6 : 4);
sqlite3_step(_sCreateIpAssignmentPool);
}
}
} else return 400;
} else if (!strcmp(j->u.object.values[k].name,"rules")) {
if (j->u.object.values[k].value->type == json_array) {
sqlite3_reset(_sDeleteRulesForNetwork);
sqlite3_bind_text(_sDeleteRulesForNetwork,1,nwids,16,SQLITE_STATIC);
sqlite3_step(_sDeleteRulesForNetwork);
for(unsigned int kk=0;kk<j->u.object.values[k].value->u.array.length;++kk) {
json_value *rj = j->u.object.values[k].value->u.array.values[kk];
if ((rj)&&(rj->type == json_object)) {
struct { // NULL pointers indicate missing or NULL -- wildcards
const json_int_t *ruleId;
const char *nodeId;
const json_int_t *vlanId;
const json_int_t *vlanPcp;
const json_int_t *etherType;
const char *macSource;
const char *macDest;
const char *ipSource;
const char *ipDest;
const json_int_t *ipTos;
const json_int_t *ipProtocol;
const json_int_t *ipSourcePort;
const json_int_t *ipDestPort;
const char *action;
} rule;
memset(&rule,0,sizeof(rule));
for(unsigned int rk=0;rk<rj->u.object.length;++rk) {
if ((!strcmp(rj->u.object.values[rk].name,"ruleId"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.ruleId = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"nodeId"))&&(rj->u.object.values[rk].value->type == json_string))
rule.nodeId = rj->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(rj->u.object.values[rk].name,"vlanId"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.vlanId = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"vlanPcp"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.vlanPcp = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"etherType"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.etherType = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"macSource"))&&(rj->u.object.values[rk].value->type == json_string))
rule.macSource = rj->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(rj->u.object.values[rk].name,"macDest"))&&(rj->u.object.values[rk].value->type == json_string))
rule.macDest = rj->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(rj->u.object.values[rk].name,"ipSource"))&&(rj->u.object.values[rk].value->type == json_string))
rule.ipSource = rj->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(rj->u.object.values[rk].name,"ipDest"))&&(rj->u.object.values[rk].value->type == json_string))
rule.ipDest = rj->u.object.values[rk].value->u.string.ptr;
else if ((!strcmp(rj->u.object.values[rk].name,"ipTos"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.ipTos = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"ipProtocol"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.ipProtocol = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"ipSourcePort"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.ipSourcePort = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"ipDestPort"))&&(rj->u.object.values[rk].value->type == json_integer))
rule.ipDestPort = &(rj->u.object.values[rk].value->u.integer);
else if ((!strcmp(rj->u.object.values[rk].name,"action"))&&(rj->u.object.values[rk].value->type == json_string))
rule.action = rj->u.object.values[rk].value->u.string.ptr;
}
if ((rule.ruleId)&&(rule.action)&&(rule.action[0])) {
char mactmp1[16],mactmp2[16];
sqlite3_reset(_sCreateRule);
sqlite3_bind_text(_sCreateRule,1,nwids,16,SQLITE_STATIC);
sqlite3_bind_int64(_sCreateRule,2,*rule.ruleId);
for(int i=3;i<=14;++i)
sqlite3_bind_null(_sCreateRule,i);
if ((rule.nodeId)&&(strlen(rule.nodeId) == 10)) sqlite3_bind_text(_sCreateRule,3,rule.nodeId,10,SQLITE_STATIC);
if (rule.vlanId) sqlite3_bind_int(_sCreateRule,4,(int)*rule.vlanId);
if (rule.vlanPcp) sqlite3_bind_int(_sCreateRule,5,(int)*rule.vlanPcp);
if (rule.etherType) sqlite3_bind_int(_sCreateRule,6,(int)*rule.etherType & (int)0xffff);
if (rule.macSource) {
MAC m(rule.macSource);
Utils::snprintf(mactmp1,sizeof(mactmp1),"%.12llx",(unsigned long long)m.toInt());
sqlite3_bind_text(_sCreateRule,7,mactmp1,-1,SQLITE_STATIC);
}
if (rule.macDest) {
MAC m(rule.macDest);
Utils::snprintf(mactmp2,sizeof(mactmp2),"%.12llx",(unsigned long long)m.toInt());
sqlite3_bind_text(_sCreateRule,8,mactmp2,-1,SQLITE_STATIC);
}
if (rule.ipSource) sqlite3_bind_text(_sCreateRule,9,rule.ipSource,-1,SQLITE_STATIC);
if (rule.ipDest) sqlite3_bind_text(_sCreateRule,10,rule.ipDest,-1,SQLITE_STATIC);
if (rule.ipTos) sqlite3_bind_int(_sCreateRule,11,(int)*rule.ipTos);
if (rule.ipProtocol) sqlite3_bind_int(_sCreateRule,12,(int)*rule.ipProtocol);
if (rule.ipSourcePort) sqlite3_bind_int(_sCreateRule,13,(int)*rule.ipSourcePort & (int)0xffff);
if (rule.ipDestPort) sqlite3_bind_int(_sCreateRule,14,(int)*rule.ipDestPort & (int)0xffff);
sqlite3_bind_text(_sCreateRule,15,rule.action,-1,SQLITE_STATIC);
sqlite3_step(_sCreateRule);
}
}
}
} else return 400;
}
}
}
json_value_free(j);
}
return handleControlPlaneHttpGET(path,urlArgs,headers,body,responseBody,responseContentType);
}
} // else 404
} // else 404
return 404;
}
unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType)
{
if (path.empty())
return 404;
Mutex::Lock _l(_lock);
return 404;
}
} // namespace ZeroTier } // namespace ZeroTier

View File

@ -39,19 +39,18 @@
#include "../node/Constants.hpp" #include "../node/Constants.hpp"
#include "../node/NetworkController.hpp" #include "../node/NetworkController.hpp"
#include "../node/Mutex.hpp" #include "../node/Mutex.hpp"
#include "../node/NonCopyable.hpp"
#include "../service/ControlPlaneSubsystem.hpp"
namespace ZeroTier { namespace ZeroTier {
class SqliteNetworkController : public NetworkController class SqliteNetworkController : public NetworkController,public ControlPlaneSubsystem
{ {
public: public:
class DBC;
friend class SqliteNetworkController::DBC;
SqliteNetworkController(const char *dbPath); SqliteNetworkController(const char *dbPath);
virtual ~SqliteNetworkController(); virtual ~SqliteNetworkController();
// NetworkController
virtual NetworkController::ResultCode doNetworkConfigRequest( virtual NetworkController::ResultCode doNetworkConfigRequest(
const InetAddress &fromAddr, const InetAddress &fromAddr,
const Identity &signingId, const Identity &signingId,
@ -61,12 +60,35 @@ public:
uint64_t haveRevision, uint64_t haveRevision,
Dictionary &netconf); Dictionary &netconf);
// ControlPlaneSubsystem
virtual unsigned int handleControlPlaneHttpGET(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
virtual unsigned int handleControlPlaneHttpPOST(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
virtual unsigned int handleControlPlaneHttpDELETE(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType);
private: private:
std::string _dbPath; std::string _dbPath;
sqlite3 *_db; sqlite3 *_db;
sqlite3_stmt *_sGetNetworkById; sqlite3_stmt *_sGetNetworkById;
sqlite3_stmt *_sGetMemberByNetworkAndNodeId; sqlite3_stmt *_sGetMember;
sqlite3_stmt *_sCreateMember; sqlite3_stmt *_sCreateMember;
sqlite3_stmt *_sGetNodeIdentity; sqlite3_stmt *_sGetNodeIdentity;
sqlite3_stmt *_sCreateNode; sqlite3_stmt *_sCreateNode;
@ -82,26 +104,23 @@ private:
sqlite3_stmt *_sAllocateIp; sqlite3_stmt *_sAllocateIp;
sqlite3_stmt *_sCacheNetconf; sqlite3_stmt *_sCacheNetconf;
sqlite3_stmt *_sGetRelays; sqlite3_stmt *_sGetRelays;
sqlite3_stmt *_sListNetworks;
sqlite3_stmt *_sListNetworkMembers;
sqlite3_stmt *_sGetMember2;
sqlite3_stmt *_sGetIpAssignmentPools2;
sqlite3_stmt *_sListRules;
sqlite3_stmt *_sCreateRule;
sqlite3_stmt *_sCreateNetwork;
sqlite3_stmt *_sUpdateNetworkField;
sqlite3_stmt *_sGetNetworkRevision;
sqlite3_stmt *_sGetIpAssignmentsForNode2;
sqlite3_stmt *_sDeleteRelaysForNetwork;
sqlite3_stmt *_sCreateRelay;
sqlite3_stmt *_sDeleteIpAssignmentPoolsForNetwork;
sqlite3_stmt *_sDeleteRulesForNetwork;
sqlite3_stmt *_sCreateIpAssignmentPool;
Mutex _lock; 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 } // namespace ZeroTier

View File

@ -21,8 +21,7 @@ CREATE TABLE IpAssignmentPool (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
ipNetwork blob(16) NOT NULL, ipNetwork blob(16) NOT NULL,
ipNetmaskBits integer NOT NULL, ipNetmaskBits integer NOT NULL,
ipVersion integer NOT NULL DEFAULT(4), ipVersion integer NOT NULL DEFAULT(4)
active integer NOT NULL DEFAULT(1)
); );
CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId); CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);
@ -71,9 +70,11 @@ CREATE TABLE Network (
CREATE TABLE Relay ( CREATE TABLE Relay (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
nodeId char(10) NOT NULL, nodeId char(10) NOT NULL,
address varchar(64) NOT NULL phyAddress varchar(64) NOT NULL
); );
CREATE INDEX Relay_networkId ON Relay (networkId);
CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId); CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId);
CREATE TABLE Node ( CREATE TABLE Node (
@ -86,6 +87,7 @@ CREATE TABLE Node (
CREATE TABLE Rule ( CREATE TABLE Rule (
networkId char(16) NOT NULL, networkId char(16) NOT NULL,
ruleId integer NOT NULL,
nodeId char(10), nodeId char(10),
vlanId integer, vlanId integer,
vlanPcp integer, vlanPcp integer,

View File

@ -22,8 +22,7 @@
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" ipNetwork blob(16) NOT NULL,\n"\ " ipNetwork blob(16) NOT NULL,\n"\
" ipNetmaskBits integer NOT NULL,\n"\ " ipNetmaskBits integer NOT NULL,\n"\
" ipVersion integer NOT NULL DEFAULT(4),\n"\ " ipVersion integer NOT NULL DEFAULT(4)\n"\
" active integer NOT NULL DEFAULT(1)\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\ "CREATE INDEX IpAssignmentPool_networkId ON IpAssignmentPool (networkId);\n"\
@ -72,9 +71,11 @@
"CREATE TABLE Relay (\n"\ "CREATE TABLE Relay (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" nodeId char(10) NOT NULL,\n"\ " nodeId char(10) NOT NULL,\n"\
" address varchar(64) NOT NULL\n"\ " phyAddress varchar(64) NOT NULL\n"\
");\n"\ ");\n"\
"\n"\ "\n"\
"CREATE INDEX Relay_networkId ON Relay (networkId);\n"\
"\n"\
"CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId);\n"\ "CREATE UNIQUE INDEX Relay_networkId_nodeId ON Relay (networkId, nodeId);\n"\
"\n"\ "\n"\
"CREATE TABLE Node (\n"\ "CREATE TABLE Node (\n"\
@ -87,6 +88,7 @@
"\n"\ "\n"\
"CREATE TABLE Rule (\n"\ "CREATE TABLE Rule (\n"\
" networkId char(16) NOT NULL,\n"\ " networkId char(16) NOT NULL,\n"\
" ruleId integer NOT NULL,\n"\
" nodeId char(10),\n"\ " nodeId char(10),\n"\
" vlanId integer,\n"\ " vlanId integer,\n"\
" vlanPcp integer,\n"\ " vlanPcp integer,\n"\

View File

@ -56,6 +56,9 @@ public:
((((uint64_t)e) & 0xffULL) << 8) | ((((uint64_t)e) & 0xffULL) << 8) |
(((uint64_t)f) & 0xffULL) ) {} (((uint64_t)f) & 0xffULL) ) {}
MAC(const char *s) throw() { fromString(s); }
MAC(const std::string &s) throw() { fromString(s.c_str()); }
MAC(const void *bits,unsigned int len) throw() { setTo(bits,len); } MAC(const void *bits,unsigned int len) throw() { setTo(bits,len); }
MAC(const Address &ztaddr,uint64_t nwid) throw() { fromAddress(ztaddr,nwid); } MAC(const Address &ztaddr,uint64_t nwid) throw() { fromAddress(ztaddr,nwid); }

View File

@ -241,9 +241,10 @@ static void _jsonAppend(unsigned int depth,std::string &buf,const ZT1_Peer *peer
buf.append(json); buf.append(json);
} }
ControlPlane::ControlPlane(OneService *svc,Node *n) : ControlPlane::ControlPlane(OneService *svc,Node *n,SqliteNetworkController *nc) :
_svc(svc), _svc(svc),
_node(n) _node(n),
_controller(nc)
{ {
} }
@ -264,6 +265,7 @@ unsigned int ControlPlane::handleRequest(
unsigned int scode = 404; unsigned int scode = 404;
std::vector<std::string> ps(Utils::split(path.c_str(),"/","","")); std::vector<std::string> ps(Utils::split(path.c_str(),"/","",""));
std::map<std::string,std::string> urlArgs; std::map<std::string,std::string> urlArgs;
Mutex::Lock _l(_lock);
if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6)))) if (!((fromAddress.ipsEqual(InetAddress::LO4))||(fromAddress.ipsEqual(InetAddress::LO6))))
return 403; // Forbidden: we only allow access from localhost right now return 403; // Forbidden: we only allow access from localhost right now
@ -291,7 +293,6 @@ unsigned int ControlPlane::handleRequest(
bool isAuth = false; bool isAuth = false;
{ {
Mutex::Lock _l(_authTokens_m);
std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth")); std::map<std::string,std::string>::const_iterator ah(headers.find("x-zt1-auth"));
if ((ah != headers.end())&&(_authTokens.count(ah->second) > 0)) { if ((ah != headers.end())&&(_authTokens.count(ah->second) > 0)) {
isAuth = true; isAuth = true;
@ -429,12 +430,19 @@ unsigned int ControlPlane::handleRequest(
} // else 404 } // else 404
_node->freeQueryResult((void *)pl); _node->freeQueryResult((void *)pl);
} else scode = 500; } else scode = 500;
} // else 404 } else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpGET(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
}
} else scode = 401; // isAuth == false } else scode = 401; // isAuth == false
} else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) { } else if ((httpMethod == HTTP_POST)||(httpMethod == HTTP_PUT)) {
if (isAuth) { if (isAuth) {
if (ps[0] == "config") { if (ps[0] == "config") {
// TODO // TODO
} else if (ps[0] == "network") { } else if (ps[0] == "network") {
@ -455,12 +463,19 @@ unsigned int ControlPlane::handleRequest(
_node->freeQueryResult((void *)nws); _node->freeQueryResult((void *)nws);
} else scode = 500; } else scode = 500;
} }
} // else 404 } else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpPOST(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
}
} else scode = 401; // isAuth == false } else scode = 401; // isAuth == false
} else if (httpMethod == HTTP_DELETE) { } else if (httpMethod == HTTP_DELETE) {
if (isAuth) { if (isAuth) {
if (ps[0] == "config") { if (ps[0] == "config") {
// TODO // TODO
} else if (ps[0] == "network") { } else if (ps[0] == "network") {
@ -480,7 +495,12 @@ unsigned int ControlPlane::handleRequest(
} // else 404 } // else 404
_node->freeQueryResult((void *)nws); _node->freeQueryResult((void *)nws);
} else scode = 500; } else scode = 500;
} // else 404 } else {
std::map<std::string,ControlPlaneSubsystem *>::const_iterator ss(_subsystems.find(ps[0]));
if (ss != _subsystems.end())
scode = ss->second->handleControlPlaneHttpDELETE(std::vector<std::string>(ps.begin()+1,ps.end()),urlArgs,headers,body,responseBody,responseContentType);
else scode = 404;
}
} else { } else {
scode = 401; // isAuth = false scode = 401; // isAuth = false
@ -492,7 +512,7 @@ unsigned int ControlPlane::handleRequest(
} }
// Wrap result in jsonp function call if the user included a jsonp= url argument. // Wrap result in jsonp function call if the user included a jsonp= url argument.
// Also double-check isAuth since it feels like the right thing to do. // Also double-check isAuth since forbidding this without auth feels safer.
std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp")); std::map<std::string,std::string>::const_iterator jsonp(urlArgs.find("jsonp"));
if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) { if ((isAuth)&&(jsonp != urlArgs.end())&&(responseContentType == "application/json")) {
if (responseBody.length() > 0) if (responseBody.length() > 0)

View File

@ -56,10 +56,27 @@ public:
*/ */
inline void addAuthToken(const char *tok) inline void addAuthToken(const char *tok)
{ {
Mutex::Lock _l(_authTokens_m); Mutex::Lock _l(_lock);
_authTokens.insert(std::string(tok)); _authTokens.insert(std::string(tok));
} }
/**
* Mount a subsystem under a prefix
*
* Note that the prefix must not contain a dot -- this is reserved for
* static pages -- and must not be a reserved prefix such as /peer
* or /network. Do not include path / characters in the prefix. Example
* would be 'controller' for SqliteNetworkController.
*
* @param prefix First element in URI path
* @param subsys Object to call for results of GET and POST/PUT operations
*/
inline void mount(const char *prefix,ControlPlaneSubsystem *subsys)
{
Mutex::Lock _l(_lock);
_subsystems[std::string(prefix)] = subsys;
}
/** /**
* Handle HTTP request * Handle HTTP request
* *
@ -85,7 +102,8 @@ private:
OneService *const _svc; OneService *const _svc;
Node *const _node; Node *const _node;
std::set<std::string> _authTokens; std::set<std::string> _authTokens;
Mutex _authTokens_m; std::map<std::string,ControlPlaneSubsystem *> _subsystems;
Mutex _lock;
}; };
} // namespace ZeroTier } // namespace ZeroTier

View File

@ -0,0 +1,76 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*
* --
*
* 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_CONTROLPLANESUBSYSTEM_HPP
#define ZT_CONTROLPLANESUBSYSTEM_HPP
#include <map>
#include <vector>
#include <string>
namespace ZeroTier {
/**
* Base class for subsystems that can be mounted under the HTTP control plane
*
* Handlers should fill in responseBody and responseContentType and return
* a HTTP status code or 0 on other errors.
*/
class ControlPlaneSubsystem
{
public:
ControlPlaneSubsystem() {}
virtual ~ControlPlaneSubsystem() {}
virtual unsigned int handleControlPlaneHttpGET(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
virtual unsigned int handleControlPlaneHttpPOST(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
virtual unsigned int handleControlPlaneHttpDELETE(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
const std::map<std::string,std::string> &headers,
const std::string &body,
std::string &responseBody,
std::string &responseContentType) = 0;
};
} // namespace ZeroTier
#endif

View File

@ -213,6 +213,8 @@ public:
_controlPlane = new ControlPlane(this,_node); _controlPlane = new ControlPlane(this,_node);
_controlPlane->addAuthToken(authToken.c_str()); _controlPlane->addAuthToken(authToken.c_str());
if (_master)
_controlPlane->mount("controller",_master);
{ // Remember networks from previous session { // Remember networks from previous session
std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str())); std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str()));