mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2024-12-20 21:43:08 +00:00
Merge branch 'adamierymenko-dev' into netcon
This commit is contained in:
commit
19c0923a86
@ -210,11 +210,6 @@ SqliteNetworkController::SqliteNetworkController(const char *dbPath) :
|
||||
||(sqlite3_prepare_v2(_db,"DELETE FROM Gateway WHERE networkId = ?",-1,&_sDeleteGateways,(const char **)0) != SQLITE_OK)
|
||||
||(sqlite3_prepare_v2(_db,"INSERT INTO Gateway (networkId,\"ip\",ipVersion,metric) VALUES (?,?,?,?)",-1,&_sCreateGateway,(const char **)0) != SQLITE_OK)
|
||||
|
||||
/* Log */
|
||||
||(sqlite3_prepare_v2(_db,"INSERT INTO \"Log\" (networkId,nodeId,\"ts\",\"authorized\",\"version\",fromAddr) VALUES (?,?,?,?,?,?)",-1,&_sPutLog,(const char **)0) != SQLITE_OK)
|
||||
||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? AND \"ts\" >= ? ORDER BY \"ts\" ASC",-1,&_sGetMemberLog,(const char **)0) != SQLITE_OK)
|
||||
||(sqlite3_prepare_v2(_db,"SELECT \"ts\",\"authorized\",\"version\",fromAddr FROM \"Log\" WHERE networkId = ? AND nodeId = ? ORDER BY \"ts\" DESC LIMIT 10",-1,&_sGetRecentMemberLog,(const char **)0) != SQLITE_OK)
|
||||
|
||||
/* Config */
|
||||
||(sqlite3_prepare_v2(_db,"SELECT \"v\" FROM \"Config\" WHERE \"k\" = ?",-1,&_sGetConfig,(const char **)0) != SQLITE_OK)
|
||||
||(sqlite3_prepare_v2(_db,"INSERT OR REPLACE INTO \"Config\" (\"k\",\"v\") VALUES (?,?)",-1,&_sSetConfig,(const char **)0) != SQLITE_OK)
|
||||
@ -296,9 +291,6 @@ SqliteNetworkController::~SqliteNetworkController()
|
||||
sqlite3_finalize(_sIncrementMemberRevisionCounter);
|
||||
sqlite3_finalize(_sGetConfig);
|
||||
sqlite3_finalize(_sSetConfig);
|
||||
sqlite3_finalize(_sPutLog);
|
||||
sqlite3_finalize(_sGetMemberLog);
|
||||
sqlite3_finalize(_sGetRecentMemberLog);
|
||||
sqlite3_close(_db);
|
||||
}
|
||||
}
|
||||
@ -1074,28 +1066,35 @@ unsigned int SqliteNetworkController::_doCPGet(
|
||||
|
||||
responseBody.append("],\n\t\"recentLog\": [");
|
||||
|
||||
sqlite3_reset(_sGetRecentMemberLog);
|
||||
sqlite3_bind_text(_sGetRecentMemberLog,1,nwids,16,SQLITE_STATIC);
|
||||
sqlite3_bind_text(_sGetRecentMemberLog,2,addrs,10,SQLITE_STATIC);
|
||||
bool firstLog = true;
|
||||
while (sqlite3_step(_sGetRecentMemberLog) == SQLITE_ROW) {
|
||||
responseBody.append(firstLog ? "{" : ",{");
|
||||
firstLog = false;
|
||||
responseBody.append("\"ts\":");
|
||||
responseBody.append(reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,0)));
|
||||
responseBody.append((sqlite3_column_int(_sGetRecentMemberLog,1) == 0) ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":");
|
||||
const char *ver = reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,2));
|
||||
if ((ver)&&(ver[0])) {
|
||||
responseBody.push_back('"');
|
||||
responseBody.append(_jsonEscape(ver));
|
||||
responseBody.append("\",\"fromAddr\":");
|
||||
} else responseBody.append("null,\"fromAddr\":");
|
||||
const char *fa = reinterpret_cast<const char *>(sqlite3_column_text(_sGetRecentMemberLog,3));
|
||||
if ((fa)&&(fa[0])) {
|
||||
responseBody.push_back('"');
|
||||
responseBody.append(_jsonEscape(fa));
|
||||
responseBody.append("\"}");
|
||||
} else responseBody.append("null}");
|
||||
{
|
||||
std::map< std::pair<Address,uint64_t>,_LLEntry >::const_iterator lli(_lastLog.find(std::pair<Address,uint64_t>(Address(address),nwid)));
|
||||
if (lli != _lastLog.end()) {
|
||||
const _LLEntry &lastLogEntry = lli->second;
|
||||
uint64_t eptr = lastLogEntry.totalRequests;
|
||||
for(int k=0;k<ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;++k) {
|
||||
if (!eptr--)
|
||||
break;
|
||||
const unsigned long ptr = (unsigned long)eptr % ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;
|
||||
|
||||
char tsbuf[64];
|
||||
Utils::snprintf(tsbuf,sizeof(tsbuf),"%llu",(unsigned long long)lastLogEntry.l[ptr].ts);
|
||||
|
||||
responseBody.append((k == 0) ? "{" : ",{");
|
||||
responseBody.append("\"ts\":");
|
||||
responseBody.append(tsbuf);
|
||||
responseBody.append(lastLogEntry.l[ptr].authorized ? ",\"authorized\":false,\"version\":" : ",\"authorized\":true,\"version\":");
|
||||
if (lastLogEntry.l[ptr].version[0]) {
|
||||
responseBody.push_back('"');
|
||||
responseBody.append(_jsonEscape(lastLogEntry.l[ptr].version));
|
||||
responseBody.append("\",\"fromAddr\":");
|
||||
} else responseBody.append("null,\"fromAddr\":");
|
||||
if (lastLogEntry.l[ptr].fromAddr) {
|
||||
responseBody.push_back('"');
|
||||
responseBody.append(_jsonEscape(lastLogEntry.l[ptr].fromAddr.toString()));
|
||||
responseBody.append("\"}");
|
||||
} else responseBody.append("null}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
responseBody.append("]\n}\n");
|
||||
@ -1430,14 +1429,12 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
|
||||
return NetworkController::NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
||||
// Check rate limit
|
||||
|
||||
{
|
||||
uint64_t &lrt = _lastRequestTime[std::pair<Address,uint64_t>(identity.address(),nwid)];
|
||||
uint64_t lrt2 = lrt;
|
||||
if (((lrt = OSUtils::now()) - lrt2) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
|
||||
return NetworkController::NETCONF_QUERY_IGNORE;
|
||||
}
|
||||
// Check rate limit circuit breaker to prevent flooding
|
||||
const uint64_t now = OSUtils::now();
|
||||
_LLEntry &lastLogEntry = _lastLog[std::pair<Address,uint64_t>(identity.address(),nwid)];
|
||||
if ((now - lastLogEntry.lastRequestTime) <= ZT_NETCONF_MIN_REQUEST_PERIOD)
|
||||
return NetworkController::NETCONF_QUERY_IGNORE;
|
||||
lastLogEntry.lastRequestTime = now;
|
||||
|
||||
NetworkRecord network;
|
||||
memset(&network,0,sizeof(network));
|
||||
@ -1523,28 +1520,18 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
|
||||
sqlite3_step(_sIncrementMemberRevisionCounter);
|
||||
}
|
||||
|
||||
// Add log entry
|
||||
// Add log entry to in-memory circular log
|
||||
|
||||
{
|
||||
char ver[16];
|
||||
std::string fa;
|
||||
if (fromAddr) {
|
||||
fa = fromAddr.toString();
|
||||
if (fa.length() > 64)
|
||||
fa = fa.substr(0,64);
|
||||
}
|
||||
sqlite3_reset(_sPutLog);
|
||||
sqlite3_bind_text(_sPutLog,1,network.id,16,SQLITE_STATIC);
|
||||
sqlite3_bind_text(_sPutLog,2,member.nodeId,10,SQLITE_STATIC);
|
||||
sqlite3_bind_int64(_sPutLog,3,(long long)OSUtils::now());
|
||||
sqlite3_bind_int(_sPutLog,4,member.authorized ? 1 : 0);
|
||||
if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0)) {
|
||||
Utils::snprintf(ver,sizeof(ver),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision);
|
||||
sqlite3_bind_text(_sPutLog,5,ver,-1,SQLITE_STATIC);
|
||||
} else sqlite3_bind_null(_sPutLog,5);
|
||||
if (fa.length() > 0)
|
||||
sqlite3_bind_text(_sPutLog,6,fa.c_str(),-1,SQLITE_STATIC);
|
||||
else sqlite3_bind_null(_sPutLog,6);
|
||||
sqlite3_step(_sPutLog);
|
||||
const unsigned long ptr = (unsigned long)lastLogEntry.totalRequests % ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;
|
||||
lastLogEntry.l[ptr].ts = now;
|
||||
lastLogEntry.l[ptr].fromAddr = fromAddr;
|
||||
if ((clientMajorVersion > 0)||(clientMinorVersion > 0)||(clientRevision > 0))
|
||||
Utils::snprintf(lastLogEntry.l[ptr].version,sizeof(lastLogEntry.l[ptr].version),"%u.%u.%u",clientMajorVersion,clientMinorVersion,clientRevision);
|
||||
else lastLogEntry.l[ptr].version[0] = (char)0;
|
||||
lastLogEntry.l[ptr].authorized = member.authorized;
|
||||
++lastLogEntry.totalRequests;
|
||||
// TODO: push or save these somewhere
|
||||
}
|
||||
|
||||
// Check member authorization
|
||||
@ -1557,7 +1544,7 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
|
||||
netconf.clear();
|
||||
{
|
||||
char tss[24],rs[24];
|
||||
Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)OSUtils::now());
|
||||
Utils::snprintf(tss,sizeof(tss),"%.16llx",(unsigned long long)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;
|
||||
@ -1801,10 +1788,13 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
|
||||
netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s;
|
||||
}
|
||||
|
||||
// TODO: IPv6 auto-assign once it's supported in UI
|
||||
if ((network.v6AssignMode)&&(!strcmp(network.v6AssignMode,"rfc4193"))) {
|
||||
InetAddress rfc4193Addr(InetAddress::makeIpv6rfc4193(nwid,identity.address().toInt()));
|
||||
netconf[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = rfc4193Addr.toString();
|
||||
}
|
||||
|
||||
if (network.isPrivate) {
|
||||
CertificateOfMembership com(OSUtils::now(),ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),nwid,identity.address());
|
||||
CertificateOfMembership com(now,ZT_NETWORK_AUTOCONF_DELAY + (ZT_NETWORK_AUTOCONF_DELAY / 2),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 {
|
||||
@ -1813,7 +1803,7 @@ NetworkController::ResultCode SqliteNetworkController::_doNetworkConfigRequest(c
|
||||
}
|
||||
}
|
||||
|
||||
if (!netconf.sign(signingId,OSUtils::now())) {
|
||||
if (!netconf.sign(signingId,now)) {
|
||||
netconf["error"] = "unable to sign netconf dictionary";
|
||||
return NETCONF_QUERY_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
|
@ -40,6 +40,9 @@
|
||||
#include "../node/NetworkController.hpp"
|
||||
#include "../node/Mutex.hpp"
|
||||
|
||||
// Number of in-memory last log entries to maintain per user
|
||||
#define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class SqliteNetworkController : public NetworkController
|
||||
@ -104,7 +107,34 @@ private:
|
||||
std::string _dbPath;
|
||||
std::string _instanceId;
|
||||
|
||||
std::map< std::pair<Address,uint64_t>,uint64_t > _lastRequestTime;
|
||||
// A circular buffer last log
|
||||
struct _LLEntry
|
||||
{
|
||||
_LLEntry()
|
||||
{
|
||||
for(long i=0;i<ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE;++i)
|
||||
this->l[i].ts = 0;
|
||||
this->lastRequestTime = 0;
|
||||
this->totalRequests = 0;
|
||||
}
|
||||
|
||||
// Circular buffer of last log entries
|
||||
struct {
|
||||
uint64_t ts; // timestamp or 0 if circular buffer entry unused
|
||||
char version[64];
|
||||
InetAddress fromAddr;
|
||||
bool authorized;
|
||||
} l[ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE];
|
||||
|
||||
// Time of last request whether successful or not
|
||||
uint64_t lastRequestTime;
|
||||
|
||||
// Total requests by this address / network ID pair (also serves mod IN_MEMORY_LOG_SIZE as circular buffer ptr)
|
||||
uint64_t totalRequests;
|
||||
};
|
||||
|
||||
// Last log entries by address and network ID pair
|
||||
std::map< std::pair<Address,uint64_t>,_LLEntry > _lastLog;
|
||||
|
||||
sqlite3 *_db;
|
||||
|
||||
@ -151,9 +181,6 @@ private:
|
||||
sqlite3_stmt *_sIncrementMemberRevisionCounter;
|
||||
sqlite3_stmt *_sGetConfig;
|
||||
sqlite3_stmt *_sSetConfig;
|
||||
sqlite3_stmt *_sPutLog;
|
||||
sqlite3_stmt *_sGetMemberLog;
|
||||
sqlite3_stmt *_sGetRecentMemberLog;
|
||||
|
||||
Mutex _lock;
|
||||
};
|
||||
|
@ -77,19 +77,6 @@ CREATE TABLE Member (
|
||||
CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);
|
||||
CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);
|
||||
|
||||
CREATE TABLE Log (
|
||||
networkId char(16) NOT NULL,
|
||||
nodeId char(10) NOT NULL,
|
||||
ts integer NOT NULL,
|
||||
authorized integer NOT NULL,
|
||||
authTokenId integer,
|
||||
version varchar(16),
|
||||
fromAddr varchar(64)
|
||||
);
|
||||
|
||||
CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId);
|
||||
CREATE INDEX Log_ts ON Log(ts);
|
||||
|
||||
CREATE TABLE Relay (
|
||||
networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,
|
||||
address char(10) NOT NULL,
|
||||
|
@ -78,19 +78,6 @@
|
||||
"CREATE INDEX Member_networkId_activeBridge ON Member(networkId, activeBridge);\n"\
|
||||
"CREATE INDEX Member_networkId_memberRevision ON Member(networkId, memberRevision);\n"\
|
||||
"\n"\
|
||||
"CREATE TABLE Log (\n"\
|
||||
" networkId char(16) NOT NULL,\n"\
|
||||
" nodeId char(10) NOT NULL,\n"\
|
||||
" ts integer NOT NULL,\n"\
|
||||
" authorized integer NOT NULL,\n"\
|
||||
" authTokenId integer,\n"\
|
||||
" version varchar(16),\n"\
|
||||
" fromAddr varchar(64)\n"\
|
||||
");\n"\
|
||||
"\n"\
|
||||
"CREATE INDEX Log_networkId_nodeId ON Log(networkId, nodeId);\n"\
|
||||
"CREATE INDEX Log_ts ON Log(ts);\n"\
|
||||
"\n"\
|
||||
"CREATE TABLE Relay (\n"\
|
||||
" networkId char(16) NOT NULL REFERENCES Network(id) ON DELETE CASCADE,\n"\
|
||||
" address char(10) NOT NULL,\n"\
|
||||
|
@ -399,4 +399,30 @@ InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
|
||||
return InetAddress(sin6);
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
throw()
|
||||
{
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfd;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32);
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[9] = 0x99;
|
||||
sin6->sin6_addr.s6_addr[10] = 0x93;
|
||||
sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
@ -375,6 +375,50 @@ struct InetAddress : public sockaddr_storage
|
||||
*/
|
||||
static InetAddress makeIpv6LinkLocal(const MAC &mac)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Compute private IPv6 unicast address from network ID and ZeroTier address
|
||||
*
|
||||
* This generates a private unicast IPv6 address that is mostly compliant
|
||||
* with the letter of RFC4193 and certainly compliant in spirit.
|
||||
*
|
||||
* RFC4193 specifies a format of:
|
||||
*
|
||||
* | 7 bits |1| 40 bits | 16 bits | 64 bits |
|
||||
* | Prefix |L| Global ID | Subnet ID | Interface ID |
|
||||
*
|
||||
* The 'L' bit is set to 1, yielding an address beginning with 0xfd. Then
|
||||
* the network ID is filled into the global ID, subnet ID, and first byte
|
||||
* of the "interface ID" field. Since the first 40 bits of the network ID
|
||||
* is the unique ZeroTier address of its controller, this makes a very
|
||||
* good random global ID. Since network IDs have 24 more bits, we let it
|
||||
* overflow into the interface ID.
|
||||
*
|
||||
* After that we pad with two bytes: 0x99, 0x93, namely the default ZeroTier
|
||||
* port in hex.
|
||||
*
|
||||
* Finally we fill the remaining 40 bits of the interface ID field with
|
||||
* the 40-bit unique ZeroTier device ID of the network member.
|
||||
*
|
||||
* This yields a valid RFC4193 address with a random global ID, a
|
||||
* meaningful subnet ID, and a unique interface ID, all mappable back onto
|
||||
* ZeroTier space.
|
||||
*
|
||||
* This in turn could allow us, on networks numbered this way, to emulate
|
||||
* IPv6 NDP and eliminate all multicast. This could be beneficial for
|
||||
* small devices and huge networks, e.g. IoT applications.
|
||||
*
|
||||
* The returned address is given an odd prefix length of /88, since within
|
||||
* a given network only the last 40 bits (device ID) are variable. This
|
||||
* is a bit unusual but as far as we know should not cause any problems with
|
||||
* any non-braindead IPv6 stack.
|
||||
*
|
||||
* @param nwid 64-bit network ID
|
||||
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
|
||||
* @return IPv6 private unicast address with /88 netmask
|
||||
*/
|
||||
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
throw();
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
Loading…
Reference in New Issue
Block a user