mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-06-05 03:53:44 +02:00
Cleanup, new C++ netconf code is almost ready to test!
This commit is contained in:
parent
a369c69091
commit
60fb28a90a
8 changed files with 370 additions and 63 deletions
|
@ -1,7 +1,7 @@
|
||||||
CC=clang
|
CC=clang
|
||||||
CXX=clang++
|
CXX=clang++
|
||||||
|
|
||||||
INCLUDES=
|
INCLUDES=-I/usr/local/include
|
||||||
DEFS=
|
DEFS=
|
||||||
LIBS=
|
LIBS=
|
||||||
|
|
||||||
|
|
|
@ -92,12 +92,12 @@ public:
|
||||||
enum ReservedId
|
enum ReservedId
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Timestamp of certificate issue in milliseconds since epoch
|
* Revision number of certificate
|
||||||
*
|
*
|
||||||
* maxDelta here defines certificate lifetime, and certs are lazily
|
* Certificates may differ in revision number by a designated max
|
||||||
* pushed to other peers on a net with a frequency of 1/2 this time.
|
* delta. Differences wider than this cause certificates not to agree.
|
||||||
*/
|
*/
|
||||||
COM_RESERVED_ID_TIMESTAMP = 0,
|
COM_RESERVED_ID_REVISION = 0,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Network ID for which certificate was issued
|
* Network ID for which certificate was issued
|
||||||
|
@ -123,14 +123,14 @@ public:
|
||||||
/**
|
/**
|
||||||
* Create from required fields common to all networks
|
* Create from required fields common to all networks
|
||||||
*
|
*
|
||||||
* @param timestamp Timestamp of cert
|
* @param revision Revision number of certificate
|
||||||
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
||||||
* @param nwid Network ID
|
* @param nwid Network ID
|
||||||
* @param issuedTo Certificate recipient
|
* @param issuedTo Certificate recipient
|
||||||
*/
|
*/
|
||||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||||
{
|
{
|
||||||
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_TIMESTAMP,timestamp,timestampMaxDelta));
|
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_REVISION,revision,revisionMaxDelta));
|
||||||
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_NETWORK_ID,nwid,0));
|
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_NETWORK_ID,nwid,0));
|
||||||
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_ISSUED_TO,issuedTo.toInt(),0xffffffffffffffffULL));
|
_qualifiers.push_back(_Qualifier(COM_RESERVED_ID_ISSUED_TO,issuedTo.toInt(),0xffffffffffffffffULL));
|
||||||
memset(_signature.data,0,_signature.size());
|
memset(_signature.data,0,_signature.size());
|
||||||
|
@ -182,7 +182,7 @@ public:
|
||||||
{
|
{
|
||||||
if (_qualifiers.size() < 3)
|
if (_qualifiers.size() < 3)
|
||||||
return false;
|
return false;
|
||||||
if (_qualifiers[0].id != COM_RESERVED_ID_TIMESTAMP)
|
if (_qualifiers[0].id != COM_RESERVED_ID_REVISION)
|
||||||
return false;
|
return false;
|
||||||
if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
|
if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
|
||||||
return false;
|
return false;
|
||||||
|
@ -192,26 +192,26 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Maximum delta for mandatory timestamp field or 0 if field missing
|
* @return Maximum delta for mandatory revision field or 0 if field missing
|
||||||
*/
|
*/
|
||||||
inline uint64_t timestampMaxDelta() const
|
inline uint64_t revisionMaxDelta() const
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) {
|
for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) {
|
||||||
if (q->id == COM_RESERVED_ID_TIMESTAMP)
|
if (q->id == COM_RESERVED_ID_REVISION)
|
||||||
return q->maxDelta;
|
return q->maxDelta;
|
||||||
}
|
}
|
||||||
return 0ULL;
|
return 0ULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Timestamp for this cert in ms since epoch (according to netconf's clock)
|
* @return Revision number for this cert
|
||||||
*/
|
*/
|
||||||
inline uint64_t timestamp() const
|
inline uint64_t revision() const
|
||||||
throw()
|
throw()
|
||||||
{
|
{
|
||||||
for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) {
|
for(std::vector<_Qualifier>::const_iterator q(_qualifiers.begin());q!=_qualifiers.end();++q) {
|
||||||
if (q->id == COM_RESERVED_ID_TIMESTAMP)
|
if (q->id == COM_RESERVED_ID_REVISION)
|
||||||
return q->value;
|
return q->value;
|
||||||
}
|
}
|
||||||
return 0ULL;
|
return 0ULL;
|
||||||
|
|
|
@ -369,14 +369,6 @@
|
||||||
*/
|
*/
|
||||||
#define ZT_ANTIRECURSION_HISTORY_SIZE 16
|
#define ZT_ANTIRECURSION_HISTORY_SIZE 16
|
||||||
|
|
||||||
/**
|
|
||||||
* TTL for certificates of membership on private networks
|
|
||||||
*
|
|
||||||
* This is the max delta for the timestamp field of a COM, so it's a window
|
|
||||||
* plus or minus the certificate's timestamp. In milliseconds.
|
|
||||||
*/
|
|
||||||
#define ZT_NETWORK_CERTIFICATE_TTL_WINDOW (ZT_NETWORK_AUTOCONF_DELAY * 4)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How often to broadcast beacons over physical local LANs
|
* How often to broadcast beacons over physical local LANs
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
#include "Utils.hpp"
|
||||||
|
|
||||||
// Three fields are added/updated by sign()
|
// Three fields are added/updated by sign()
|
||||||
#define ZT_DICTIONARY_SIGNATURE "~!ed25519"
|
#define ZT_DICTIONARY_SIGNATURE "~!ed25519"
|
||||||
|
@ -107,6 +108,128 @@ public:
|
||||||
return e->second;
|
return e->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to get
|
||||||
|
* @param dfl Default boolean result if key not found or empty (default: false)
|
||||||
|
* @return Boolean value of key
|
||||||
|
*/
|
||||||
|
inline bool getBoolean(const std::string &key,bool dfl = false) const
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
return dfl;
|
||||||
|
if (e->second.length() < 1)
|
||||||
|
return dfl;
|
||||||
|
switch(e->second[0]) {
|
||||||
|
case '1':
|
||||||
|
case 't':
|
||||||
|
case 'T':
|
||||||
|
case 'y':
|
||||||
|
case 'Y':
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to get
|
||||||
|
* @param dfl Default value if not present (default: 0)
|
||||||
|
* @return Value converted to unsigned 64-bit int or 0 if not found
|
||||||
|
*/
|
||||||
|
inline uint64_t getUInt(const std::string &key,uint64_t dfl = 0) const
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
return dfl;
|
||||||
|
return Utils::strToU64(e->second.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to get
|
||||||
|
* @param dfl Default value if not present (default: 0)
|
||||||
|
* @return Value converted to unsigned 64-bit int or 0 if not found
|
||||||
|
*/
|
||||||
|
inline uint64_t getHexUInt(const std::string &key,uint64_t dfl = 0) const
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
return dfl;
|
||||||
|
return Utils::hexStrToU64(e->second.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to get
|
||||||
|
* @param dfl Default value if not present (default: 0)
|
||||||
|
* @return Value converted to signed 64-bit int or 0 if not found
|
||||||
|
*/
|
||||||
|
inline int64_t getInt(const std::string &key,int64_t dfl = 0) const
|
||||||
|
{
|
||||||
|
const_iterator e(find(key));
|
||||||
|
if (e == end())
|
||||||
|
return dfl;
|
||||||
|
return Utils::strTo64(e->second.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value String value
|
||||||
|
*/
|
||||||
|
inline void set(const std::string &key,const char *value)
|
||||||
|
{
|
||||||
|
(*this)[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value String value
|
||||||
|
*/
|
||||||
|
inline void set(const std::string &key,const std::string &value)
|
||||||
|
{
|
||||||
|
(*this)[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value Boolean value
|
||||||
|
*/
|
||||||
|
inline void set(const std::string &key,bool value)
|
||||||
|
{
|
||||||
|
(*this)[key] = ((value) ? "1" : "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value Integer value
|
||||||
|
*/
|
||||||
|
inline void set(const std::string &key,uint64_t value)
|
||||||
|
{
|
||||||
|
char tmp[24];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%llu",(unsigned long long)value);
|
||||||
|
(*this)[key] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value Integer value
|
||||||
|
*/
|
||||||
|
inline void set(const std::string &key,int64_t value)
|
||||||
|
{
|
||||||
|
char tmp[24];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%lld",(long long)value);
|
||||||
|
(*this)[key] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param key Key to set
|
||||||
|
* @param value Integer value
|
||||||
|
*/
|
||||||
|
inline void setHex(const std::string &key,uint64_t value)
|
||||||
|
{
|
||||||
|
char tmp[24];
|
||||||
|
Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
|
||||||
|
(*this)[key] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param key Key to check
|
* @param key Key to check
|
||||||
* @return True if dictionary contains key
|
* @return True if dictionary contains key
|
||||||
|
|
|
@ -352,7 +352,7 @@ void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we made it past authentication, update cert
|
// If we made it past authentication, update cert
|
||||||
if (cert.timestamp() >= old.timestamp())
|
if (cert.revision() != old.revision())
|
||||||
old = cert;
|
old = cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,17 +360,10 @@ bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now)
|
||||||
{
|
{
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
if ((_config)&&(!_config->isPublic())&&(_config->com())) {
|
if ((_config)&&(!_config->isPublic())&&(_config->com())) {
|
||||||
uint64_t pushInterval = _config->com().timestampMaxDelta() / 2;
|
uint64_t &lastPushed = _lastPushedMembershipCertificate[to];
|
||||||
if (pushInterval) {
|
if ((now - lastPushed) > (ZT_NETWORK_AUTOCONF_DELAY / 2)) {
|
||||||
// Give a 1s margin around +/- 1/2 max delta to account for network latency
|
lastPushed = now;
|
||||||
if (pushInterval > 1000)
|
return true;
|
||||||
pushInterval -= 1000;
|
|
||||||
|
|
||||||
uint64_t &lastPushed = _lastPushedMembershipCertificate[to];
|
|
||||||
if ((now - lastPushed) > pushInterval) {
|
|
||||||
lastPushed = now;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -421,7 +414,7 @@ void Network::clean()
|
||||||
|
|
||||||
// Clean entries from the last pushed tracking map if they're so old as
|
// Clean entries from the last pushed tracking map if they're so old as
|
||||||
// to be no longer relevant.
|
// to be no longer relevant.
|
||||||
uint64_t forgetIfBefore = now - (_config->com().timestampMaxDelta() * 3ULL);
|
uint64_t forgetIfBefore = now - (ZT_PEER_ACTIVITY_TIMEOUT * 16); // arbitrary reasonable cutoff
|
||||||
for(std::map<Address,uint64_t>::iterator lp(_lastPushedMembershipCertificate.begin());lp!=_lastPushedMembershipCertificate.end();) {
|
for(std::map<Address,uint64_t>::iterator lp(_lastPushedMembershipCertificate.begin());lp!=_lastPushedMembershipCertificate.end();) {
|
||||||
if (lp->second < forgetIfBefore)
|
if (lp->second < forgetIfBefore)
|
||||||
_lastPushedMembershipCertificate.erase(lp++);
|
_lastPushedMembershipCertificate.erase(lp++);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "Constants.hpp"
|
#include "Constants.hpp"
|
||||||
|
#include "NetworkConfigMaster.hpp"
|
||||||
|
|
||||||
#ifdef ZT_ENABLE_NETCONF_MASTER
|
#ifdef ZT_ENABLE_NETCONF_MASTER
|
||||||
|
|
||||||
|
@ -37,7 +38,6 @@
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "NetworkConfigMaster.hpp"
|
|
||||||
#include "RuntimeEnvironment.hpp"
|
#include "RuntimeEnvironment.hpp"
|
||||||
#include "Switch.hpp"
|
#include "Switch.hpp"
|
||||||
#include "Packet.hpp"
|
#include "Packet.hpp"
|
||||||
|
@ -45,6 +45,9 @@
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Node.hpp"
|
#include "Node.hpp"
|
||||||
#include "Logger.hpp"
|
#include "Logger.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Peer.hpp"
|
||||||
|
#include "CertificateOfMembership.hpp"
|
||||||
|
|
||||||
// Redis timeout in seconds
|
// Redis timeout in seconds
|
||||||
#define ZT_NETCONF_REDIS_TIMEOUT 10
|
#define ZT_NETCONF_REDIS_TIMEOUT 10
|
||||||
|
@ -74,13 +77,93 @@ NetworkConfigMaster::~NetworkConfigMaster()
|
||||||
redisFree(_rc);
|
redisFree(_rc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkConfigMaster::doNetworkConfigRequest(
|
void NetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uint64_t packetId,const Address &member,uint64_t nwid,const Dictionary &metaData,uint64_t haveTimestamp)
|
||||||
uint64_t packetId,
|
|
||||||
const Address &from,
|
|
||||||
uint64_t nwid,
|
|
||||||
const Dictionary &metaData,
|
|
||||||
uint64_t haveTimestamp)
|
|
||||||
{
|
{
|
||||||
|
char memberKey[256],nwids[24],addrs[16],nwKey[256];
|
||||||
|
Dictionary memberRecord;
|
||||||
|
std::string revision,tmps2;
|
||||||
|
|
||||||
|
Mutex::Lock _l(_lock);
|
||||||
|
|
||||||
|
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
|
||||||
|
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.toInt());
|
||||||
|
Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs);
|
||||||
|
Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids);
|
||||||
|
|
||||||
|
TRACE("netconf: request from %s for %s (if newer than %llu)",addrs,nwids,(unsigned long long)haveTimestamp);
|
||||||
|
|
||||||
|
if (!_hget(nwKey,"id",tmps2)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s/id",nwKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tmps2 != nwids) {
|
||||||
|
TRACE("netconf: network %s not found",nwids);
|
||||||
|
Packet outp(member,RR->identity.address(),Packet::VERB_ERROR);
|
||||||
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
|
outp.append(packetId);
|
||||||
|
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
|
||||||
|
outp.append(nwid);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hget(nwKey,"revision",revision)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s/revision",nwKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!revision.length())
|
||||||
|
revision = "0";
|
||||||
|
|
||||||
|
if (!_hgetall(memberKey,memberRecord)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s",memberKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((memberRecord.size() == 0)||(memberRecord.get("id","") != addrs)||(memberRecord.get("nwid","") != nwids)) {
|
||||||
|
if (!_initNewMember(nwid,member,metaData,memberRecord))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (memberRecord.getBoolean("authorized")) {
|
||||||
|
uint64_t ts = memberRecord.getHexUInt("netconfTimestamp",0);
|
||||||
|
std::string netconf(memberRecord.get("netconf",""));
|
||||||
|
|
||||||
|
Dictionary upd;
|
||||||
|
upd.setHex("netconfClientTimestamp",haveTimestamp);
|
||||||
|
if (fromAddr)
|
||||||
|
upd.set("lastAt",fromAddr.toString());
|
||||||
|
upd.setHex("lastSeen",Utils::now());
|
||||||
|
_hmset(memberKey,upd);
|
||||||
|
|
||||||
|
if (((ts == 0)||(netconf.length() == 0))||(memberRecord.get("netconfRevision","") != revision)) {
|
||||||
|
if (!_generateNetconf(nwid,member,metaData,netconf,ts))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts > haveTimestamp) {
|
||||||
|
TRACE("netconf: sending %u bytes of netconf data to %s",netconf.length(),addrs);
|
||||||
|
Packet outp(member,RR->identity.address(),Packet::VERB_OK);
|
||||||
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
|
outp.append(packetId);
|
||||||
|
outp.append(nwid);
|
||||||
|
outp.append((uint16_t)netconf.length());
|
||||||
|
outp.append(netconf.data(),netconf.length());
|
||||||
|
outp.compress();
|
||||||
|
if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check -- this would be weird
|
||||||
|
TRACE("netconf: compressed packet exceeds ZT_PROTO_MAX_PACKET_LENGTH!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("netconf: access denied for %s on %s",addrs,nwids);
|
||||||
|
Packet outp(member,RR->identity.address(),Packet::VERB_ERROR);
|
||||||
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
|
outp.append(packetId);
|
||||||
|
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
|
||||||
|
outp.append(nwid);
|
||||||
|
RR->sw->send(outp,true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkConfigMaster::_reconnect()
|
bool NetworkConfigMaster::_reconnect()
|
||||||
|
@ -92,7 +175,7 @@ bool NetworkConfigMaster::_reconnect()
|
||||||
|
|
||||||
tv.tv_sec = ZT_NETCONF_REDIS_TIMEOUT;
|
tv.tv_sec = ZT_NETCONF_REDIS_TIMEOUT;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
_rc = redisConnectWithTimeout(_redisHost.c_str(),_redisPort,&tv);
|
_rc = redisConnectWithTimeout(_redisHost.c_str(),_redisPort,tv);
|
||||||
if (!_rc)
|
if (!_rc)
|
||||||
return false;
|
return false;
|
||||||
if (_rc->err) {
|
if (_rc->err) {
|
||||||
|
@ -100,14 +183,14 @@ bool NetworkConfigMaster::_reconnect()
|
||||||
_rc = (redisContext *)0;
|
_rc = (redisContext *)0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
redisSetTimeout(_rc,&tv); // necessary???
|
redisSetTimeout(_rc,tv); // necessary???
|
||||||
|
|
||||||
// TODO: support AUTH and SELECT !!!
|
// TODO: support AUTH and SELECT !!!
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkConfigMaster::_hgetall(const char *key,std::map<std::string,std::string> &hdata)
|
bool NetworkConfigMaster::_hgetall(const char *key,Dictionary &hdata)
|
||||||
{
|
{
|
||||||
if (!_rc) {
|
if (!_rc) {
|
||||||
if (!_reconnect())
|
if (!_reconnect())
|
||||||
|
@ -125,11 +208,11 @@ bool NetworkConfigMaster::_hgetall(const char *key,std::map<std::string,std::str
|
||||||
if (reply->type == REDIS_REPLY_ARRAY) {
|
if (reply->type == REDIS_REPLY_ARRAY) {
|
||||||
for(long i=0;i<reply->elements;) {
|
for(long i=0;i<reply->elements;) {
|
||||||
try {
|
try {
|
||||||
const char *k = reply->elements[i]->str;
|
const char *k = reply->element[i]->str;
|
||||||
if (++i >= reply->elements)
|
if (++i >= reply->elements)
|
||||||
break;
|
break;
|
||||||
if ((k)&&(reply->elements[i]->str))
|
if ((k)&&(reply->element[i]->str))
|
||||||
hdata[k] = reply->elements[i]->str;
|
hdata[k] = reply->element[i]->str;
|
||||||
++i;
|
++i;
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
break; // memory safety
|
break; // memory safety
|
||||||
|
@ -142,9 +225,9 @@ bool NetworkConfigMaster::_hgetall(const char *key,std::map<std::string,std::str
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NetworkConfigMaster::_hmset(const char *key,const std::map<std::string,std::string> &hdata)
|
bool NetworkConfigMaster::_hmset(const char *key,const Dictionary &hdata)
|
||||||
{
|
{
|
||||||
const const char *hargv[1024];
|
const char *hargv[1024];
|
||||||
|
|
||||||
if (!hdata.size())
|
if (!hdata.size())
|
||||||
return true;
|
return true;
|
||||||
|
@ -157,7 +240,7 @@ bool NetworkConfigMaster::_hmset(const char *key,const std::map<std::string,std:
|
||||||
hargv[0] = "HMSET";
|
hargv[0] = "HMSET";
|
||||||
hargv[1] = key;
|
hargv[1] = key;
|
||||||
int hargc = 2;
|
int hargc = 2;
|
||||||
for(std::map<std::string,std::string>::const_iterator i(hdata.begin());i!=hdata.end();++i) {
|
for(Dictionary::const_iterator i(hdata.begin());i!=hdata.end();++i) {
|
||||||
if (hargc >= 1024)
|
if (hargc >= 1024)
|
||||||
break;
|
break;
|
||||||
hargv[hargc++] = i->first.c_str();
|
hargv[hargc++] = i->first.c_str();
|
||||||
|
@ -228,6 +311,113 @@ bool NetworkConfigMaster::_hset(const char *key,const char *hashKey,const char *
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NetworkConfigMaster::_initNewMember(uint64_t nwid,const Address &member,const Dictionary &metaData,Dictionary &memberRecord)
|
||||||
|
{
|
||||||
|
char memberKey[256],nwids[24],addrs[16],nwKey[256];
|
||||||
|
Dictionary networkRecord;
|
||||||
|
|
||||||
|
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
|
||||||
|
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.toInt());
|
||||||
|
Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs);
|
||||||
|
Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids);
|
||||||
|
|
||||||
|
if (!_hgetall(nwKey,networkRecord)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s",nwKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (networkRecord.get("id","") != nwids) {
|
||||||
|
TRACE("netconf: network %s not found (initNewMember)",nwids);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memberRecord.clear();
|
||||||
|
memberRecord["id"] = addrs;
|
||||||
|
memberRecord["nwid"] = nwids;
|
||||||
|
memberRecord["authorized"] = (networkRecord.getBoolean("private",true) ? "0" : "1"); // auto-authorize on public networks
|
||||||
|
memberRecord.setHex("firstSeen",Utils::now());
|
||||||
|
{
|
||||||
|
SharedPtr<Peer> peer(RR->topology->getPeer(member));
|
||||||
|
if (peer)
|
||||||
|
memberRecord["identity"] = peer->identity().toString(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hmset(memberKey,memberRecord)) {
|
||||||
|
LOG("netconf: Redis error storing %s for new member %s",memberKey,addrs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NetworkConfigMaster::_generateNetconf(uint64_t nwid,const Address &member,const Dictionary &metaData,std::string &netconf,uint64_t &ts)
|
||||||
|
{
|
||||||
|
char memberKey[256],nwids[24],addrs[16],tss[24],nwKey[256];
|
||||||
|
Dictionary networkRecord,memberRecord,nc;
|
||||||
|
|
||||||
|
Utils::snprintf(nwids,sizeof(nwids),"%.16llx",(unsigned long long)nwid);
|
||||||
|
Utils::snprintf(addrs,sizeof(addrs),"%.10llx",(unsigned long long)member.toInt());
|
||||||
|
Utils::snprintf(memberKey,sizeof(memberKey),"zt1:network:%s:member:%s:~",nwids,addrs);
|
||||||
|
Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids);
|
||||||
|
|
||||||
|
if (!_hgetall(nwKey,networkRecord)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s",nwKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (networkRecord.get("id","") != nwids) {
|
||||||
|
TRACE("netconf: network %s not found (generateNetconf)",nwids);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_hgetall(memberKey,memberRecord)) {
|
||||||
|
LOG("netconf: Redis error retrieving %s",memberKey);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t revision = networkRecord.getHexUInt("revision",0);
|
||||||
|
bool isPrivate = networkRecord.getBoolean("private",true);
|
||||||
|
ts = Utils::now();
|
||||||
|
Utils::snprintf(tss,sizeof(tss),"%llx",ts);
|
||||||
|
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP] = tss;
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID] = nwids;
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO] = addrs;
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_PRIVATE] = isPrivate ? "1" : "0";
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_NAME] = networkRecord.get("name",nwids);
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_DESC] = networkRecord.get("desc","");
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST] = networkRecord.getBoolean("enableBroadcast",true) ? "1" : "0";
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING] = networkRecord.getBoolean("allowPassiveBridging",false) ? "1" : "0";
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES] = networkRecord.get("etherTypes","");
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_RATES] = networkRecord.get("multicastRates","");
|
||||||
|
|
||||||
|
uint64_t ml = networkRecord.getHexUInt("multicastLimit",0);
|
||||||
|
if (ml > 0)
|
||||||
|
nc.setHex(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,ml);
|
||||||
|
|
||||||
|
std::string activeBridgeList;
|
||||||
|
if (activeBridgeList.length() > 0)
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES] = activeBridgeList;
|
||||||
|
|
||||||
|
std::string v4s,v6s;
|
||||||
|
if (v4s.length())
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC] = v4s;
|
||||||
|
if (v6s.length())
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC] = v6s;
|
||||||
|
|
||||||
|
if (isPrivate) {
|
||||||
|
CertificateOfMembership com(revision,2,nwid,member);
|
||||||
|
if (com.sign(RR->identity))
|
||||||
|
nc[ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP] = com.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
netconf = nc.toString();
|
||||||
|
|
||||||
|
_hset(memberKey,"netconf",netconf.c_str());
|
||||||
|
_hset(memberKey,"netconfTimestamp",tss);
|
||||||
|
_hset(memberKey,"netconfRevision",networkRecord.get("revision","0").c_str());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
||||||
#endif // ZT_ENABLE_NETCONF_MASTER
|
#endif // ZT_ENABLE_NETCONF_MASTER
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "Dictionary.hpp"
|
#include "Dictionary.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
|
#include "InetAddress.hpp"
|
||||||
|
|
||||||
#include <hiredis/hiredis.h>
|
#include <hiredis/hiredis.h>
|
||||||
|
|
||||||
|
@ -82,15 +83,17 @@ public:
|
||||||
* This is a blocking call, so rate is limited by Redis. It will fail
|
* This is a blocking call, so rate is limited by Redis. It will fail
|
||||||
* and log its failure if the Redis server is not available or times out.
|
* and log its failure if the Redis server is not available or times out.
|
||||||
*
|
*
|
||||||
|
* @param fromAddr Originating IP address
|
||||||
* @param packetId 64-bit packet ID
|
* @param packetId 64-bit packet ID
|
||||||
* @param from Originating peer ZeroTier address
|
* @param member Originating peer ZeroTier address
|
||||||
* @param nwid 64-bit network ID
|
* @param nwid 64-bit network ID
|
||||||
* @param metaData Meta-data bundled with request (empty if none)
|
* @param metaData Meta-data bundled with request (empty if none)
|
||||||
* @param haveTimestamp Timestamp requesting peer has or 0 if none or not included
|
* @param haveTimestamp Timestamp requesting peer has or 0 if none or not included
|
||||||
*/
|
*/
|
||||||
void doNetworkConfigRequest(
|
void doNetworkConfigRequest(
|
||||||
|
const InetAddress &fromAddr,
|
||||||
uint64_t packetId,
|
uint64_t packetId,
|
||||||
const Address &from,
|
const Address &member,
|
||||||
uint64_t nwid,
|
uint64_t nwid,
|
||||||
const Dictionary &metaData,
|
const Dictionary &metaData,
|
||||||
uint64_t haveTimestamp);
|
uint64_t haveTimestamp);
|
||||||
|
@ -98,11 +101,14 @@ public:
|
||||||
private:
|
private:
|
||||||
// These assume _lock is locked
|
// These assume _lock is locked
|
||||||
bool _reconnect();
|
bool _reconnect();
|
||||||
bool _hgetall(const char *key,std::map<std::string,std::string> &hdata);
|
bool _hgetall(const char *key,Dictionary &hdata);
|
||||||
bool _hmset(const char *key,const std::map<std::string,std::string> &hdata);
|
bool _hmset(const char *key,const Dictionary &hdata);
|
||||||
bool _hget(const char *key,const char *hashKey,std::string &value);
|
bool _hget(const char *key,const char *hashKey,std::string &value);
|
||||||
bool _hset(const char *key,const char *hashKey,const char *value);
|
bool _hset(const char *key,const char *hashKey,const char *value);
|
||||||
|
|
||||||
|
bool _initNewMember(uint64_t nwid,const Address &member,const Dictionary &metaData,Dictionary &memberRecord);
|
||||||
|
bool _generateNetconf(uint64_t nwid,const Address &member,const Dictionary &metaData,std::string &netconf,uint64_t &ts);
|
||||||
|
|
||||||
Mutex _lock;
|
Mutex _lock;
|
||||||
|
|
||||||
std::string _redisHost;
|
std::string _redisHost;
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
- : is used as the key namespace separator as per de-facto Redis standard.
|
- : is used as the key namespace separator as per de-facto Redis standard.
|
||||||
- A top-level record may have a :~ child containing a hash. This is the root hash and contains any simple key=value properties of the record.
|
- A top-level record may have a :~ child containing a hash. This is the root hash and contains any simple key=value properties of the record.
|
||||||
- Booleans: any value other than "1" or "true" is false.
|
- Booleans: any value other than "1" or "true" is false.
|
||||||
- Timestamps are in milliseconds since the epoch and are stored as base-10 integers.
|
- Unless otherwise indicated *all integer values are in hexadecimal!*
|
||||||
|
- Timestamps are in milliseconds since the epoch
|
||||||
- IPv4 addresees: stored in standard dot notation e.g. 1.2.3.4
|
- IPv4 addresees: stored in standard dot notation e.g. 1.2.3.4
|
||||||
- IPv6 addresses: :'s are optional and addresses must be stored *without* shortening, e.g. with :0000: instead of ::. It must be possible to strip :'s from the address and get 128 bits of straight hex.
|
- IPv6 addresses: :'s are optional and addresses must be stored *without* shortening, e.g. with :0000: instead of ::. It must be possible to strip :'s from the address and get 128 bits of straight hex.
|
||||||
- Hexadecimal: all hex values must be lower case
|
- Hexadecimal: all hex values must be lower case
|
||||||
|
@ -23,8 +24,6 @@ Network records are used by the network configuration master to issue configurat
|
||||||
|
|
||||||
### zt1:network:\<nwid\>:~
|
### zt1:network:\<nwid\>:~
|
||||||
|
|
||||||
Each network has a network record indexed by its 64-bit network ID in lower-case hexadecimal. Unless otherwise indicated all integer values are in hexadecimal.
|
|
||||||
|
|
||||||
- !R id :: must be \<nwid\>
|
- !R id :: must be \<nwid\>
|
||||||
- !M name :: network's globally unique short name, which can contain only characters valid in an e-mail address. It's the job of the code that populates this DB to ensure that this is globally unique.
|
- !M name :: network's globally unique short name, which can contain only characters valid in an e-mail address. It's the job of the code that populates this DB to ensure that this is globally unique.
|
||||||
- R owner :: id of user who owns this network (not used by netconf master, only for web UI and web API)
|
- R owner :: id of user who owns this network (not used by netconf master, only for web UI and web API)
|
||||||
|
@ -34,7 +33,7 @@ Each network has a network record indexed by its 64-bit network ID in lower-case
|
||||||
- R infrastructure :: if true, network can't be deleted through API or web UI
|
- R infrastructure :: if true, network can't be deleted through API or web UI
|
||||||
- M private :: if true, network requires authentication
|
- M private :: if true, network requires authentication
|
||||||
- R creationTime :: timestamp of network creation
|
- R creationTime :: timestamp of network creation
|
||||||
- M etherTypes :: comma-delimited list of integers indicating Ethernet types permitted on network
|
- M etherTypes :: comma-delimited list of HEX integers indicating Ethernet types permitted on network
|
||||||
- M enableBroadcast :: if true, ff:ff:ff:ff:ff:ff is enabled network-wide
|
- M enableBroadcast :: if true, ff:ff:ff:ff:ff:ff is enabled network-wide
|
||||||
- M v4AssignMode :: 'none' (or null/empty/etc.), 'zt', 'dhcp'
|
- M v4AssignMode :: 'none' (or null/empty/etc.), 'zt', 'dhcp'
|
||||||
- M v4AssignPool :: network/bits from which to assign IPs
|
- M v4AssignPool :: network/bits from which to assign IPs
|
||||||
|
@ -42,11 +41,14 @@ Each network has a network record indexed by its 64-bit network ID in lower-case
|
||||||
- M v6AssignPool :: network/bits from which to assign IPs
|
- M v6AssignPool :: network/bits from which to assign IPs
|
||||||
- M allowPassiveBridging :: if true, allow passive bridging
|
- M allowPassiveBridging :: if true, allow passive bridging
|
||||||
- M multicastLimit :: maximum number of recipients to receive a multicast on this network
|
- M multicastLimit :: maximum number of recipients to receive a multicast on this network
|
||||||
- M multicastRates :: packed JSON containing multicast rates (see below)
|
- M multicastRates :: string-encoded dictionary containing multicast groups and rates (see below)
|
||||||
- M subscriptions :: comma-delimited list of subscriptions for this network
|
- M subscriptions :: comma-delimited list of subscriptions for this network
|
||||||
|
- M revision :: network revision number
|
||||||
- M ui :: arbitrary field that can be used by the UI to store stuff
|
- M ui :: arbitrary field that can be used by the UI to store stuff
|
||||||
|
|
||||||
Multicast rates are encoded as a JSON document. Each key is a multicast group in "MAC/ADI" format (e.g. *ff:ff:ff:ff:ff:ff/0*), and each value is a comma-delimited tuple of hex integer values: preload, max balance, and rate of accrual in bytes per second. An entry for *0* (or *0/0* or *00:00:00:00:00:00/0*) indicates the default setting for all unspecified multicast groups. Setting a rate limit like *ffffffff,ffffffff,ffffffff* as default will effectively turn off rate limits.
|
Multicast rates are encoded as a dictionary. Each key is a multicast group in "MAC/ADI" format (e.g. *ff:ff:ff:ff:ff:ff/0*), and each value is a comma-delimited tuple of hex integer values: preload, max balance, and rate of accrual in bytes per second. An entry for *0* (or *0/0* or *00:00:00:00:00:00/0*) indicates the default setting for all unspecified multicast groups. Setting a rate limit like *ffffffff,ffffffff,ffffffff* as default will effectively turn off rate limits.
|
||||||
|
|
||||||
|
Incrementing the network's revision number causes network configurations to be regenerated automatically on next query by a peer. It's important to note that certificates of membership for private networks permit revision numbers to vary by up to **2**. Thus, revision should be incremented once for changes that do not have authorization implications and twice when de-authorizing a member from a network. For better continuity this double-increment can happen with a time delay between each increment to give still-authorized peers more time to get an updated certificate.
|
||||||
|
|
||||||
### zt1:network:\<nwid\>:member:\<address\>:~
|
### zt1:network:\<nwid\>:member:\<address\>:~
|
||||||
|
|
||||||
|
@ -65,7 +67,8 @@ Each member of a network has a hash containing its configuration and authorizati
|
||||||
- R lastSeen :: time node was most recently seen
|
- R lastSeen :: time node was most recently seen
|
||||||
- R lastAt :: real Internet IP/port where node was most recently seen
|
- R lastAt :: real Internet IP/port where node was most recently seen
|
||||||
- R ipAssignments :: comma-delimited list of IP address assignments (see below)
|
- R ipAssignments :: comma-delimited list of IP address assignments (see below)
|
||||||
- R netconf :: most recent network configuration dictionary (updated on changes)
|
- R netconf :: most recent network configuration dictionary
|
||||||
|
- R netconfRevision :: revision of network when most recent netconf was generated
|
||||||
- R netconfTimestamp :: timestamp from netconf dictionary
|
- R netconfTimestamp :: timestamp from netconf dictionary
|
||||||
- R netconfClientTimestamp :: timestamp client most recently reported
|
- R netconfClientTimestamp :: timestamp client most recently reported
|
||||||
- M ui :: string-serialized JSON blob for use by the user interface (unused by netconf-master)
|
- M ui :: string-serialized JSON blob for use by the user interface (unused by netconf-master)
|
||||||
|
|
Loading…
Add table
Reference in a new issue