mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-07 19:24:13 +00:00
Revocation work in progress, add WATCH which is TEE with implicit rate sync (thanks JG@DCVC!), and clean up some cruft in Network.
This commit is contained in:
parent
29711e123f
commit
1f74dd4589
@ -140,6 +140,12 @@ static json _renderRule(ZT_VirtualNetworkRule &rule)
|
||||
r["flags"] = (unsigned int)rule.v.fwd.flags;
|
||||
r["length"] = (unsigned int)rule.v.fwd.length;
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
r["type"] = "ACTION_WATCH";
|
||||
r["address"] = Address(rule.v.fwd.address).toString();
|
||||
r["flags"] = (unsigned int)rule.v.fwd.flags;
|
||||
r["length"] = (unsigned int)rule.v.fwd.length;
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
r["type"] = "ACTION_REDIRECT";
|
||||
r["address"] = Address(rule.v.fwd.address).toString();
|
||||
@ -303,6 +309,12 @@ static bool _parseRule(json &r,ZT_VirtualNetworkRule &rule)
|
||||
rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL);
|
||||
rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL);
|
||||
return true;
|
||||
} else if (t == "ACTION_WATCH") {
|
||||
rule.t |= ZT_NETWORK_RULE_ACTION_WATCH;
|
||||
rule.v.fwd.address = Utils::hexStrToU64(_jS(r["address"],"0").c_str()) & 0xffffffffffULL;
|
||||
rule.v.fwd.flags = (uint32_t)(_jI(r["flags"],0ULL) & 0xffffffffULL);
|
||||
rule.v.fwd.length = (uint16_t)(_jI(r["length"],0ULL) & 0xffffULL);
|
||||
return true;
|
||||
} else if (t == "ACTION_REDIRECT") {
|
||||
rule.t |= ZT_NETWORK_RULE_ACTION_REDIRECT;
|
||||
rule.v.fwd.address = Utils::hexStrToU64(_jS(r["zt"],"0").c_str()) & 0xffffffffffULL;
|
||||
|
@ -516,15 +516,20 @@ enum ZT_VirtualNetworkRuleType
|
||||
*/
|
||||
ZT_NETWORK_RULE_ACTION_TEE = 2,
|
||||
|
||||
/**
|
||||
* Exactly like TEE but frames are dropped if previous TEEs were not acknowledged by the observer
|
||||
*/
|
||||
ZT_NETWORK_RULE_ACTION_WATCH = 3,
|
||||
|
||||
/**
|
||||
* Drop and redirect this frame to another node (by ZT address)
|
||||
*/
|
||||
ZT_NETWORK_RULE_ACTION_REDIRECT = 3,
|
||||
ZT_NETWORK_RULE_ACTION_REDIRECT = 4,
|
||||
|
||||
/**
|
||||
* Log if match and if rule debugging is enabled in the build, otherwise does nothing (for developers)
|
||||
*/
|
||||
ZT_NETWORK_RULE_ACTION_DEBUG_LOG = 4,
|
||||
ZT_NETWORK_RULE_ACTION_DEBUG_LOG = 5,
|
||||
|
||||
/**
|
||||
* Maximum ID for an ACTION, anything higher is a MATCH
|
||||
|
@ -174,6 +174,7 @@ public:
|
||||
b.append((uint8_t)0);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
b.append((uint8_t)14);
|
||||
b.append((uint64_t)rules[i].v.fwd.address);
|
||||
@ -270,6 +271,7 @@ public:
|
||||
default:
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
rules[ruleCount].v.fwd.address = b.template at<uint64_t>(p);
|
||||
rules[ruleCount].v.fwd.flags = b.template at<uint32_t>(p + 8);
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Revocation.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
@ -162,13 +163,8 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
|
||||
// Peers can send this in response to frames if they do not have a recent enough COM from us
|
||||
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
|
||||
const uint64_t now = RR->node->now();
|
||||
if ( (network) && (network->config().com) && (peer->rateGateComRequest(now)) ) {
|
||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||
network->config().com.serialize(outp);
|
||||
outp.append((uint8_t)0);
|
||||
outp.armor(peer->key(),true);
|
||||
_path->send(RR,outp.data(),outp.size(),now);
|
||||
}
|
||||
if ( (network) && (network->config().com) && (peer->rateGateComRequest(now)) )
|
||||
network->pushCredentialsNow(peer->address(),now);
|
||||
} break;
|
||||
|
||||
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
|
||||
@ -681,9 +677,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||
RR->node->putFrame(nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
|
||||
break;
|
||||
}
|
||||
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
|
||||
}
|
||||
|
||||
if ((flags & 0x10) != 0) {
|
||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||
outp.append((uint8_t)Packet::VERB_EXT_FRAME);
|
||||
outp.append((uint64_t)packetId());
|
||||
outp.armor(peer->key(),true);
|
||||
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||
}
|
||||
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
|
||||
} else {
|
||||
TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
|
||||
@ -775,6 +779,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||
CertificateOfMembership com;
|
||||
Capability cap;
|
||||
Tag tag;
|
||||
Revocation revocation;
|
||||
bool trustEstablished = false;
|
||||
|
||||
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
|
||||
@ -784,8 +789,14 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||
SharedPtr<Network> network(RR->node->network(com.networkId()));
|
||||
if (network) {
|
||||
switch (network->addCredential(com)) {
|
||||
case 0: trustEstablished = true; break;
|
||||
case 1: return false; // wait for WHOIS
|
||||
case Membership::ADD_REJECTED:
|
||||
break;
|
||||
case Membership::ADD_ACCEPTED_NEW:
|
||||
case Membership::ADD_ACCEPTED_REDUNDANT:
|
||||
trustEstablished = true;
|
||||
break;
|
||||
case Membership::ADD_DEFERRED_FOR_WHOIS:
|
||||
return false;
|
||||
}
|
||||
} else RR->mc->addCredential(com,false);
|
||||
}
|
||||
@ -799,8 +810,14 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||
SharedPtr<Network> network(RR->node->network(cap.networkId()));
|
||||
if (network) {
|
||||
switch (network->addCredential(cap)) {
|
||||
case 0: trustEstablished = true; break;
|
||||
case 1: return false; // wait for WHOIS
|
||||
case Membership::ADD_REJECTED:
|
||||
break;
|
||||
case Membership::ADD_ACCEPTED_NEW:
|
||||
case Membership::ADD_ACCEPTED_REDUNDANT:
|
||||
trustEstablished = true;
|
||||
break;
|
||||
case Membership::ADD_DEFERRED_FOR_WHOIS:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -811,11 +828,25 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||
SharedPtr<Network> network(RR->node->network(tag.networkId()));
|
||||
if (network) {
|
||||
switch (network->addCredential(tag)) {
|
||||
case 0: trustEstablished = true; break;
|
||||
case 1: return false; // wait for WHOIS
|
||||
case Membership::ADD_REJECTED:
|
||||
break;
|
||||
case Membership::ADD_ACCEPTED_NEW:
|
||||
case Membership::ADD_ACCEPTED_REDUNDANT:
|
||||
trustEstablished = true;
|
||||
break;
|
||||
case Membership::ADD_DEFERRED_FOR_WHOIS:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int numRevocations = at<uint16_t>(p); p += 2;
|
||||
for(unsigned int i=0;i<numRevocations;++i) {
|
||||
p += revocation.deserialize(*this,p);
|
||||
SharedPtr<Network> network(RR->node->network(revocation.networkId()));
|
||||
if (network) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
|
||||
@ -932,24 +963,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,cons
|
||||
const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
||||
bool trustEstablished = false;
|
||||
|
||||
if (Network::controllerFor(nwid) == peer->address()) {
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if (network) {
|
||||
network->requestConfiguration();
|
||||
trustEstablished = true;
|
||||
} else {
|
||||
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): not a member of %.16llx",source().toString().c_str(),_path->address().toString().c_str(),nwid);
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,false);
|
||||
return true;
|
||||
}
|
||||
|
||||
const unsigned int blacklistCount = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 8);
|
||||
unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 10;
|
||||
for(unsigned int i=0;i<blacklistCount;++i) {
|
||||
network->blacklistBefore(Address(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH),at<uint64_t>(ptr + 5));
|
||||
ptr += 13;
|
||||
}
|
||||
}
|
||||
|
||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,trustEstablished);
|
||||
} catch ( ... ) {
|
||||
|
@ -16,6 +16,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Membership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Peer.hpp"
|
||||
@ -28,28 +30,43 @@
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,const Capability *cap)
|
||||
Membership::Membership() :
|
||||
_lastUpdatedMulticast(0),
|
||||
_lastPushAttempt(0),
|
||||
_lastPushedCom(0),
|
||||
_comRevocationThreshold(0)
|
||||
{
|
||||
if ((now - _lastPushAttempt) < 2000ULL)
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) _remoteTags[i] = &(_tagMem[i]);
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) _remoteCaps[i] = &(_capMem[i]);
|
||||
}
|
||||
|
||||
void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
|
||||
{
|
||||
// This limits how often we go through this logic, which prevents us from
|
||||
// doing all this for every single packet or other event.
|
||||
if ( ((now - _lastPushAttempt) < 1000ULL) && (!force) )
|
||||
return;
|
||||
_lastPushAttempt = now;
|
||||
|
||||
try {
|
||||
bool unfinished;
|
||||
unsigned int localTagPtr = 0;
|
||||
bool needCom = ( (nconf.com) && ( ((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) );
|
||||
do {
|
||||
unfinished = false;
|
||||
Buffer<ZT_PROTO_MAX_PACKET_LENGTH> capsAndTags;
|
||||
|
||||
unsigned int appendedCaps = 0;
|
||||
if (cap) {
|
||||
if (localCapabilityIndex >= 0) {
|
||||
capsAndTags.addSize(2);
|
||||
std::map<uint32_t,CState>::iterator cs(_caps.find(cap->id()));
|
||||
if ((cs != _caps.end())&&((now - cs->second.lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY)) {
|
||||
cap->serialize(capsAndTags);
|
||||
cs->second.lastPushed = now;
|
||||
|
||||
if ( (_localCaps[localCapabilityIndex].id != nconf.capabilities[localCapabilityIndex].id()) || ((now - _localCaps[localCapabilityIndex].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
|
||||
_localCaps[localCapabilityIndex].lastPushed = now;
|
||||
_localCaps[localCapabilityIndex].id = nconf.capabilities[localCapabilityIndex].id();
|
||||
nconf.capabilities[localCapabilityIndex].serialize(capsAndTags);
|
||||
++appendedCaps;
|
||||
}
|
||||
|
||||
capsAndTags.setAt<uint16_t>(0,(uint16_t)appendedCaps);
|
||||
localCapabilityIndex = -1; // don't send this cap again on subsequent loops if force is true
|
||||
} else {
|
||||
capsAndTags.append((uint16_t)0);
|
||||
}
|
||||
@ -57,22 +74,17 @@ void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint
|
||||
unsigned int appendedTags = 0;
|
||||
const unsigned int tagCountPos = capsAndTags.size();
|
||||
capsAndTags.addSize(2);
|
||||
for(unsigned int i=0;i<nconf.tagCount;++i) {
|
||||
TState *const ts = _tags.get(nconf.tags[i].id());
|
||||
if ((now - ts->lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) {
|
||||
if ((capsAndTags.size() + sizeof(Tag)) >= (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership))) {
|
||||
unfinished = true;
|
||||
for(;localTagPtr<nconf.tagCount;++localTagPtr) {
|
||||
if ( (_localTags[localTagPtr].id != nconf.tags[localTagPtr].id()) || ((now - _localTags[localTagPtr].lastPushed) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) {
|
||||
if ((capsAndTags.size() + sizeof(Tag)) >= (ZT_PROTO_MAX_PACKET_LENGTH - sizeof(CertificateOfMembership)))
|
||||
break;
|
||||
}
|
||||
nconf.tags[i].serialize(capsAndTags);
|
||||
ts->lastPushed = now;
|
||||
nconf.tags[localTagPtr].serialize(capsAndTags);
|
||||
++appendedTags;
|
||||
}
|
||||
}
|
||||
capsAndTags.setAt<uint16_t>(tagCountPos,(uint16_t)appendedTags);
|
||||
|
||||
const bool needCom = ((nconf.com)&&((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY));
|
||||
if ( (needCom) || (appendedCaps) || (appendedTags) ) {
|
||||
if (needCom||appendedCaps||appendedTags) {
|
||||
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
|
||||
if (needCom) {
|
||||
nconf.com.serialize(outp);
|
||||
@ -80,110 +92,148 @@ void Membership::sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint
|
||||
}
|
||||
outp.append((uint8_t)0x00);
|
||||
outp.append(capsAndTags.data(),capsAndTags.size());
|
||||
outp.append((uint16_t)0); // no revocations, these propagate differently
|
||||
outp.compress();
|
||||
RR->sw->send(outp,true);
|
||||
needCom = false; // don't send COM again on subsequent loops if force is true
|
||||
}
|
||||
} while (unfinished); // if there are many tags, etc., we can send more than one
|
||||
} while (localTagPtr < nconf.tagCount);
|
||||
} catch ( ... ) {
|
||||
TRACE("unable to send credentials due to unexpected exception");
|
||||
}
|
||||
}
|
||||
|
||||
int Membership::addCredential(const RuntimeEnvironment *RR,const CertificateOfMembership &com)
|
||||
const Capability *Membership::getCapability(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
if (_com == com) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (redundant)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return 0;
|
||||
const _RemoteCapability *const *c = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)id,_RemoteCredentialSorter<_RemoteCapability>());
|
||||
return ( ((c != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*c)->id == (uint64_t)id)) ? ((((*c)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*c)->cap,**c))) ? &((*c)->cap) : (const Capability *)0) : (const Capability *)0);
|
||||
}
|
||||
|
||||
const Tag *Membership::getTag(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
const _RemoteTag *const *t = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)id,_RemoteCredentialSorter<_RemoteTag>());
|
||||
return ( ((t != &(_remoteTags[ZT_MAX_NETWORK_CAPABILITIES]))&&((*t)->id == (uint64_t)id)) ? ((((*t)->lastReceived)&&(_isCredentialTimestampValid(nconf,(*t)->tag,**t))) ? &((*t)->tag) : (const Tag *)0) : (const Tag *)0);
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com)
|
||||
{
|
||||
const uint64_t newts = com.timestamp().first;
|
||||
if (newts <= _comRevocationThreshold) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (revoked)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
|
||||
const int vr = com.verify(RR);
|
||||
const uint64_t oldts = _com.timestamp().first;
|
||||
if (newts < oldts) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (older than current)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if ((newts == oldts)&&(_com == com)) {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (redundant)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
|
||||
if (vr == 0) {
|
||||
if (com.timestamp().first >= _com.timestamp().first) {
|
||||
switch(com.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (invalid signature or object)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED (new)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
_com = com;
|
||||
} else {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx ACCEPTED but not used (OK but older than current)",com.issuedTo().toString().c_str(),com.networkId());
|
||||
}
|
||||
} else {
|
||||
TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (%d)",com.issuedTo().toString().c_str(),com.networkId(),vr);
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
|
||||
return vr;
|
||||
}
|
||||
|
||||
int Membership::addCredential(const RuntimeEnvironment *RR,const Tag &tag)
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag)
|
||||
{
|
||||
TState *t = _tags.get(tag.id());
|
||||
if ((t)&&(t->lastReceived != 0)&&(t->tag == tag)) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (redundant)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return 0;
|
||||
_RemoteTag *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)tag.id(),_RemoteCredentialSorter<_RemoteTag>());
|
||||
_RemoteTag *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)tag.id())) ? *htmp : (_RemoteTag *)0;
|
||||
if (have) {
|
||||
if ( (!_isCredentialTimestampValid(nconf,tag,*have)) || (have->tag.timestamp() > tag.timestamp()) ) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (revoked or too old)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (have->tag == tag) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (redundant)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
const int vr = tag.verify(RR);
|
||||
if (vr == 0) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (new)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
if (!t) {
|
||||
while (_tags.size() >= ZT_MAX_NETWORK_TAGS) {
|
||||
uint32_t oldest = 0;
|
||||
uint64_t oldestLastReceived = 0xffffffffffffffffULL;
|
||||
uint32_t *i = (uint32_t *)0;
|
||||
TState *ts = (TState *)0;
|
||||
Hashtable<uint32_t,TState>::Iterator tsi(_tags);
|
||||
while (tsi.next(i,ts)) {
|
||||
if (ts->lastReceived < oldestLastReceived) {
|
||||
oldestLastReceived = ts->lastReceived;
|
||||
oldest = *i;
|
||||
|
||||
switch(tag.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (invalid)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (new)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
if (have) {
|
||||
have->lastReceived = RR->node->now();
|
||||
have->tag = tag;
|
||||
} else {
|
||||
uint64_t minlr = 0xffffffffffffffffULL;
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) {
|
||||
if (_remoteTags[i]->id == 0xffffffffffffffffULL) {
|
||||
have = _remoteTags[i];
|
||||
break;
|
||||
} else if (_remoteTags[i]->lastReceived <= minlr) {
|
||||
have = _remoteTags[i];
|
||||
minlr = _remoteTags[i]->lastReceived;
|
||||
}
|
||||
}
|
||||
if (oldestLastReceived != 0xffffffffffffffffULL)
|
||||
_tags.erase(oldest);
|
||||
have->lastReceived = RR->node->now();
|
||||
have->tag = tag;
|
||||
std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),_RemoteCredentialSorter<_RemoteTag>());
|
||||
}
|
||||
t = &(_tags[tag.id()]);
|
||||
}
|
||||
if (t->tag.timestamp() <= tag.timestamp()) {
|
||||
t->lastReceived = RR->node->now();
|
||||
t->tag = tag;
|
||||
}
|
||||
} else {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (%d)",tag.issuedTo().toString().c_str(),tag.networkId(),vr);
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
return vr;
|
||||
}
|
||||
|
||||
int Membership::addCredential(const RuntimeEnvironment *RR,const Capability &cap)
|
||||
Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap)
|
||||
{
|
||||
std::map<uint32_t,CState>::iterator c(_caps.find(cap.id()));
|
||||
if ((c != _caps.end())&&(c->second.lastReceived != 0)&&(c->second.cap == cap)) {
|
||||
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (redundant)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
return 0;
|
||||
_RemoteCapability *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)cap.id(),_RemoteCredentialSorter<_RemoteCapability>());
|
||||
_RemoteCapability *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)cap.id())) ? *htmp : (_RemoteCapability *)0;
|
||||
if (have) {
|
||||
if ( (!_isCredentialTimestampValid(nconf,cap,*have)) || (have->cap.timestamp() > cap.timestamp()) ) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (revoked or too old)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
}
|
||||
if (have->cap == cap) {
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (redundant)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_ACCEPTED_REDUNDANT;
|
||||
}
|
||||
}
|
||||
const int vr = cap.verify(RR);
|
||||
if (vr == 0) {
|
||||
TRACE("addCredential(Capability) for %s on %.16llx ACCEPTED (new)",cap.issuedTo().toString().c_str(),cap.networkId());
|
||||
if (c == _caps.end()) {
|
||||
while (_caps.size() >= ZT_MAX_NETWORK_CAPABILITIES) {
|
||||
std::map<uint32_t,CState>::iterator oldest;
|
||||
uint64_t oldestLastReceived = 0xffffffffffffffffULL;
|
||||
for(std::map<uint32_t,CState>::iterator i(_caps.begin());i!=_caps.end();++i) {
|
||||
if (i->second.lastReceived < oldestLastReceived) {
|
||||
oldestLastReceived = i->second.lastReceived;
|
||||
oldest = i;
|
||||
|
||||
switch(cap.verify(RR)) {
|
||||
default:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx REJECTED (invalid)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
return ADD_REJECTED;
|
||||
case 0:
|
||||
TRACE("addCredential(Tag) for %s on %.16llx ACCEPTED (new)",tag.issuedTo().toString().c_str(),tag.networkId());
|
||||
if (have) {
|
||||
have->lastReceived = RR->node->now();
|
||||
have->cap = cap;
|
||||
} else {
|
||||
uint64_t minlr = 0xffffffffffffffffULL;
|
||||
for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) {
|
||||
if (_remoteCaps[i]->id == 0xffffffffffffffffULL) {
|
||||
have = _remoteCaps[i];
|
||||
break;
|
||||
} else if (_remoteCaps[i]->lastReceived <= minlr) {
|
||||
have = _remoteCaps[i];
|
||||
minlr = _remoteCaps[i]->lastReceived;
|
||||
}
|
||||
}
|
||||
if (oldestLastReceived != 0xffffffffffffffffULL)
|
||||
_caps.erase(oldest);
|
||||
have->lastReceived = RR->node->now();
|
||||
have->cap = cap;
|
||||
std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),_RemoteCredentialSorter<_RemoteCapability>());
|
||||
}
|
||||
CState &c2 = _caps[cap.id()];
|
||||
c2.lastReceived = RR->node->now();
|
||||
c2.cap = cap;
|
||||
} else if (c->second.cap.timestamp() <= cap.timestamp()) {
|
||||
c->second.lastReceived = RR->node->now();
|
||||
c->second.cap = cap;
|
||||
}
|
||||
} else {
|
||||
TRACE("addCredential(Capability) for %s on %.16llx REJECTED (%d)",cap.issuedTo().toString().c_str(),cap.networkId(),vr);
|
||||
return ADD_ACCEPTED_NEW;
|
||||
case 1:
|
||||
return ADD_DEFERRED_FOR_WHOIS;
|
||||
}
|
||||
return vr;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
@ -21,14 +21,12 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
#include "Revocation.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
@ -40,77 +38,135 @@ class Network;
|
||||
* A container for certificates of membership and other network credentials
|
||||
*
|
||||
* This is kind of analogous to a join table between Peer and Network. It is
|
||||
* presently held by the Network object for each participating Peer.
|
||||
* held by the Network object for each participating Peer.
|
||||
*
|
||||
* This is not thread safe. It must be locked externally.
|
||||
* This class is not thread safe. It must be locked externally.
|
||||
*/
|
||||
class Membership
|
||||
{
|
||||
private:
|
||||
// Tags and related state
|
||||
struct TState
|
||||
struct _RemoteTag
|
||||
{
|
||||
TState() : lastPushed(0),lastReceived(0) {}
|
||||
// Last time we pushed OUR tag to this peer (with this ID)
|
||||
uint64_t lastPushed;
|
||||
_RemoteTag() : id(0xffffffffffffffffULL),lastReceived(0),revocationThreshold(0) {}
|
||||
// Tag ID (last 32 bits, first 32 bits are set in unused entries to sort them to end)
|
||||
uint64_t id;
|
||||
// Last time we received THEIR tag (with this ID)
|
||||
uint64_t lastReceived;
|
||||
// Revocation blacklist threshold or 0 if none
|
||||
uint64_t revocationThreshold;
|
||||
// THEIR tag
|
||||
Tag tag;
|
||||
};
|
||||
|
||||
// Credentials and related state
|
||||
struct CState
|
||||
struct _RemoteCapability
|
||||
{
|
||||
CState() : lastPushed(0),lastReceived(0) {}
|
||||
// Last time we pushed OUR capability to this peer (with this ID)
|
||||
uint64_t lastPushed;
|
||||
_RemoteCapability() : id(0xffffffffffffffffULL),lastReceived(0),revocationThreshold(0) {}
|
||||
// Capability ID (last 32 bits, first 32 bits are set in unused entries to sort them to end)
|
||||
uint64_t id;
|
||||
// Last time we received THEIR capability (with this ID)
|
||||
uint64_t lastReceived;
|
||||
// Revocation blacklist threshold or 0 if none
|
||||
uint64_t revocationThreshold;
|
||||
// THEIR capability
|
||||
Capability cap;
|
||||
};
|
||||
|
||||
// Comparison operator for remote credential entries
|
||||
template<typename T>
|
||||
struct _RemoteCredentialSorter
|
||||
{
|
||||
inline bool operator()(const T *a,const T *b) const { return (a->id < b->id); }
|
||||
inline bool operator()(const uint64_t a,const T *b) const { return (a < b->id); }
|
||||
inline bool operator()(const T *a,const uint64_t b) const { return (a->id < b); }
|
||||
inline bool operator()(const uint64_t a,const uint64_t b) const { return (a < b); }
|
||||
};
|
||||
|
||||
// Used to track push state for network config tags[] and capabilities[] entries
|
||||
struct _LocalCredentialPushState
|
||||
{
|
||||
_LocalCredentialPushState() : lastPushed(0),id(0) {}
|
||||
uint64_t lastPushed;
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
public:
|
||||
enum AddCredentialResult
|
||||
{
|
||||
ADD_REJECTED,
|
||||
ADD_ACCEPTED_NEW,
|
||||
ADD_ACCEPTED_REDUNDANT,
|
||||
ADD_DEFERRED_FOR_WHOIS
|
||||
};
|
||||
|
||||
/**
|
||||
* A wrapper to iterate through member capabilities in ascending order of capability ID and return only valid ones
|
||||
* Iterator to scan forward through capabilities in ascending order of ID
|
||||
*/
|
||||
class CapabilityIterator
|
||||
{
|
||||
public:
|
||||
CapabilityIterator(const Membership &m) :
|
||||
_m(m),
|
||||
_i(m._caps.begin()),
|
||||
_e(m._caps.end())
|
||||
{
|
||||
}
|
||||
CapabilityIterator(const Membership &m,const NetworkConfig &nconf) :
|
||||
_m(&m),
|
||||
_c(&nconf),
|
||||
_i(&(m._remoteCaps[0])) {}
|
||||
|
||||
inline const Capability *next(const NetworkConfig &nconf)
|
||||
inline const Capability *next()
|
||||
{
|
||||
while (_i != _e) {
|
||||
if ((_i->second.lastReceived)&&(_m.isCredentialTimestampValid(nconf,_i->second.cap)))
|
||||
return &((_i++)->second.cap);
|
||||
else ++_i;
|
||||
for(;;) {
|
||||
if ((_i != &(_m->_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*_i)->id != 0xffffffffffffffffULL)) {
|
||||
const Capability *tmp = &((*_i)->cap);
|
||||
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
|
||||
++_i;
|
||||
return tmp;
|
||||
} else ++_i;
|
||||
} else {
|
||||
return (const Capability *)0;
|
||||
}
|
||||
}
|
||||
return (const Capability *)0;
|
||||
}
|
||||
|
||||
private:
|
||||
const Membership &_m;
|
||||
std::map<uint32_t,CState>::const_iterator _i,_e;
|
||||
const Membership *_m;
|
||||
const NetworkConfig *_c;
|
||||
const _RemoteCapability *const *_i;
|
||||
};
|
||||
friend class CapabilityIterator;
|
||||
|
||||
Membership() :
|
||||
_lastUpdatedMulticast(0),
|
||||
_lastPushAttempt(0),
|
||||
_lastPushedCom(0),
|
||||
_blacklistBefore(0),
|
||||
_com(),
|
||||
_caps(),
|
||||
_tags(8)
|
||||
/**
|
||||
* Iterator to scan forward through tags in ascending order of ID
|
||||
*/
|
||||
class TagIterator
|
||||
{
|
||||
}
|
||||
public:
|
||||
TagIterator(const Membership &m,const NetworkConfig &nconf) :
|
||||
_m(&m),
|
||||
_c(&nconf),
|
||||
_i(&(m._remoteTags[0])) {}
|
||||
|
||||
inline const Tag *next()
|
||||
{
|
||||
for(;;) {
|
||||
if ((_i != &(_m->_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*_i)->id != 0xffffffffffffffffULL)) {
|
||||
const Tag *tmp = &((*_i)->tag);
|
||||
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
|
||||
++_i;
|
||||
return tmp;
|
||||
} else ++_i;
|
||||
} else {
|
||||
return (const Tag *)0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const Membership *_m;
|
||||
const NetworkConfig *_c;
|
||||
const _RemoteTag *const *_i;
|
||||
};
|
||||
friend class TagIterator;
|
||||
|
||||
Membership();
|
||||
|
||||
/**
|
||||
* Send COM and other credentials to this peer if needed
|
||||
@ -122,9 +178,10 @@ public:
|
||||
* @param now Current time
|
||||
* @param peerAddress Address of member peer (the one that this Membership describes)
|
||||
* @param nconf My network config
|
||||
* @param cap Capability to send or 0 if none
|
||||
* @param localCapabilityIndex Index of local capability to include (in nconf.capabilities[]) or -1 if none
|
||||
* @param force If true, send objects regardless of last push time
|
||||
*/
|
||||
void sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,const Capability *cap);
|
||||
void pushCredentials(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
|
||||
|
||||
/**
|
||||
* Check whether we should push MULTICAST_LIKEs to this peer
|
||||
@ -142,6 +199,8 @@ public:
|
||||
inline void likingMulticasts(const uint64_t now) { _lastUpdatedMulticast = now; }
|
||||
|
||||
/**
|
||||
* Check whether the peer represented by this Membership should be allowed on this network at all
|
||||
*
|
||||
* @param nconf Our network config
|
||||
* @return True if this peer is allowed on this network at all
|
||||
*/
|
||||
@ -149,126 +208,48 @@ public:
|
||||
{
|
||||
if (nconf.isPublic())
|
||||
return true;
|
||||
if ((_blacklistBefore)&&(_com.timestamp().first <= _blacklistBefore))
|
||||
if ((_comRevocationThreshold)&&(_com.timestamp().first <= _comRevocationThreshold))
|
||||
return false;
|
||||
return nconf.com.agreesWith(_com);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a capability or tag is within its max delta from the timestamp of our network config and newer than any blacklist cutoff time
|
||||
*
|
||||
* @param cred Credential to check -- must have timestamp() accessor method
|
||||
* @return True if credential is NOT expired
|
||||
*/
|
||||
template<typename C>
|
||||
inline bool isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred) const
|
||||
{
|
||||
const uint64_t ts = cred.timestamp();
|
||||
const uint64_t delta = (ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts);
|
||||
return ((delta <= nconf.credentialTimeMaxDelta)&&(ts > _blacklistBefore));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nconf Network configuration
|
||||
* @param id Tag ID
|
||||
* @return Pointer to tag or NULL if not found
|
||||
*/
|
||||
inline const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
const TState *t = _tags.get(id);
|
||||
return ((t) ? (((t->lastReceived != 0)&&(isCredentialTimestampValid(nconf,t->tag))) ? &(t->tag) : (const Tag *)0) : (const Tag *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nconf Network configuration
|
||||
* @param ids Array to store IDs into
|
||||
* @param values Array to store values into
|
||||
* @param maxTags Capacity of ids[] and values[]
|
||||
* @return Number of tags added to arrays
|
||||
*/
|
||||
inline unsigned int getAllTags(const NetworkConfig &nconf,uint32_t *ids,uint32_t *values,unsigned int maxTags) const
|
||||
{
|
||||
unsigned int n = 0;
|
||||
uint32_t *id = (uint32_t *)0;
|
||||
TState *ts = (TState *)0;
|
||||
Hashtable<uint32_t,TState>::Iterator i(const_cast<Membership *>(this)->_tags);
|
||||
while (i.next(id,ts)) {
|
||||
if ((ts->lastReceived)&&(isCredentialTimestampValid(nconf,ts->tag))) {
|
||||
if (n >= maxTags)
|
||||
return n;
|
||||
ids[n] = *id;
|
||||
values[n] = ts->tag.value();
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param nconf Network configuration
|
||||
* @param id Capablity ID
|
||||
* @return Pointer to capability or NULL if not found
|
||||
*/
|
||||
inline const Capability *getCapability(const NetworkConfig &nconf,const uint32_t id) const
|
||||
{
|
||||
std::map<uint32_t,CState>::const_iterator c(_caps.find(id));
|
||||
return ((c != _caps.end()) ? (((c->second.lastReceived != 0)&&(isCredentialTimestampValid(nconf,c->second.cap))) ? &(c->second.cap) : (const Capability *)0) : (const Capability *)0);
|
||||
}
|
||||
const Capability *getCapability(const NetworkConfig &nconf,const uint32_t id) const;
|
||||
|
||||
/**
|
||||
* @param nconf Network configuration
|
||||
* @param id Tag ID
|
||||
* @return Pointer to tag or NULL if not found
|
||||
*/
|
||||
const Tag *getTag(const NetworkConfig &nconf,const uint32_t id) const;
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param com Certificate of membership
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
int addCredential(const RuntimeEnvironment *RR,const CertificateOfMembership &com);
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
int addCredential(const RuntimeEnvironment *RR,const Tag &tag);
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag);
|
||||
|
||||
/**
|
||||
* Validate and add a credential if signature is okay and it's otherwise good
|
||||
*
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
*/
|
||||
int addCredential(const RuntimeEnvironment *RR,const Capability &cap);
|
||||
|
||||
/**
|
||||
* Blacklist COM, tags, and capabilities before this time
|
||||
*
|
||||
* @param ts Blacklist cutoff
|
||||
*/
|
||||
inline void blacklistBefore(const uint64_t ts) { _blacklistBefore = ts; }
|
||||
|
||||
/**
|
||||
* Clean up old or stale entries
|
||||
*
|
||||
* @param nconf Network config
|
||||
*/
|
||||
inline void clean(const NetworkConfig &nconf)
|
||||
{
|
||||
for(std::map<uint32_t,CState>::iterator i(_caps.begin());i!=_caps.end();) {
|
||||
if (!isCredentialTimestampValid(nconf,i->second.cap)) {
|
||||
_caps.erase(i++);
|
||||
} else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *i = (uint32_t *)0;
|
||||
TState *ts = (TState *)0;
|
||||
Hashtable<uint32_t,TState>::Iterator tsi(_tags);
|
||||
while (tsi.next(i,ts)) {
|
||||
if (!isCredentialTimestampValid(nconf,ts->tag))
|
||||
_tags.erase(*i);
|
||||
}
|
||||
}
|
||||
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap);
|
||||
|
||||
private:
|
||||
template<typename C,typename CS>
|
||||
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred,const CS &state) const
|
||||
{
|
||||
const uint64_t ts = cred.timestamp();
|
||||
return ( (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) && (ts > state.revocationThreshold) );
|
||||
}
|
||||
|
||||
// Last time we pushed MULTICAST_LIKE(s)
|
||||
uint64_t _lastUpdatedMulticast;
|
||||
|
||||
@ -278,17 +259,23 @@ private:
|
||||
// Last time we pushed our COM to this peer
|
||||
uint64_t _lastPushedCom;
|
||||
|
||||
// Time before which to blacklist credentials from this peer
|
||||
uint64_t _blacklistBefore;
|
||||
// Revocation threshold for COM or 0 if none
|
||||
uint64_t _comRevocationThreshold;
|
||||
|
||||
// COM from this peer
|
||||
// Remote member's latest network COM
|
||||
CertificateOfMembership _com;
|
||||
|
||||
// Capability-related state (we need an ordered container here, hence std::map)
|
||||
std::map<uint32_t,CState> _caps;
|
||||
// Sorted (in ascending order of ID) arrays of pointers to remote tags and capabilities
|
||||
_RemoteTag *_remoteTags[ZT_MAX_NETWORK_TAGS];
|
||||
_RemoteCapability *_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
|
||||
// Tag-related state
|
||||
Hashtable<uint32_t,TState> _tags;
|
||||
// This is the RAM allocated for remote tags and capabilities from which the sorted arrays are populated
|
||||
_RemoteTag _tagMem[ZT_MAX_NETWORK_TAGS];
|
||||
_RemoteCapability _capMem[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
|
||||
// Local credential push state tracking
|
||||
_LocalCredentialPushState _localTags[ZT_MAX_NETWORK_TAGS];
|
||||
_LocalCredentialPushState _localCaps[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
291
node/Network.cpp
291
node/Network.cpp
@ -36,7 +36,7 @@
|
||||
#include "Peer.hpp"
|
||||
|
||||
// Uncomment to make the rules engine dump trace info to stdout
|
||||
//#define ZT_RULES_ENGINE_DEBUGGING 1
|
||||
#define ZT_RULES_ENGINE_DEBUGGING 1
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
@ -155,24 +155,21 @@ enum _doZtFilterResult
|
||||
static _doZtFilterResult _doZtFilter(
|
||||
const RuntimeEnvironment *RR,
|
||||
const NetworkConfig &nconf,
|
||||
const Membership *membership, // can be NULL
|
||||
const bool inbound,
|
||||
const Address &ztSource,
|
||||
Address &ztDest, // MUTABLE
|
||||
Address &ztDest, // MUTABLE -- is changed on REDIRECT actions
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *const frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId,
|
||||
const ZT_VirtualNetworkRule *rules,
|
||||
const ZT_VirtualNetworkRule *rules, // cannot be NULL
|
||||
const unsigned int ruleCount,
|
||||
const Tag *localTags,
|
||||
const unsigned int localTagCount,
|
||||
const uint32_t *const remoteTagIds,
|
||||
const uint32_t *const remoteTagValues,
|
||||
const unsigned int remoteTagCount,
|
||||
Address &cc, // MUTABLE
|
||||
unsigned int &ccLength) // MUTABLE
|
||||
Address &cc, // MUTABLE -- set to TEE destination if TEE action is taken or left alone otherwise
|
||||
unsigned int &ccLength, // MUTABLE -- set to length of packet payload to TEE
|
||||
bool &ccWatch) // MUTABLE -- set to true for WATCH target as opposed to normal TEE
|
||||
{
|
||||
#ifdef ZT_RULES_ENGINE_DEBUGGING
|
||||
char dpbuf[1024]; // used by FILTER_TRACE macro
|
||||
@ -204,6 +201,7 @@ static _doZtFilterResult _doZtFilter(
|
||||
|
||||
// These are initially handled together since preliminary logic is common
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_WATCH:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT: {
|
||||
const Address fwdAddr(rules[rn].v.fwd.address);
|
||||
if (fwdAddr == ztSource) {
|
||||
@ -242,6 +240,7 @@ static _doZtFilterResult _doZtFilter(
|
||||
#endif // ZT_RULES_ENGINE_DEBUGGING
|
||||
cc = fwdAddr;
|
||||
ccLength = (rules[rn].v.fwd.length != 0) ? ((frameLen < (unsigned int)rules[rn].v.fwd.length) ? frameLen : (unsigned int)rules[rn].v.fwd.length) : frameLen;
|
||||
ccWatch = (rt == ZT_NETWORK_RULE_ACTION_WATCH);
|
||||
}
|
||||
}
|
||||
} continue;
|
||||
@ -508,25 +507,29 @@ static _doZtFilterResult _doZtFilter(
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR:
|
||||
case ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR: {
|
||||
const Tag *lt = (const Tag *)0;
|
||||
for(unsigned int i=0;i<localTagCount;++i) {
|
||||
if (rules[rn].v.tag.id == localTags[i].id()) {
|
||||
lt = &(localTags[i]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!lt) {
|
||||
thisRuleMatches = 0;
|
||||
FILTER_TRACE("%u %s %c local tag %u not found -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
|
||||
} else {
|
||||
const uint32_t *rtv = (const uint32_t *)0;
|
||||
for(unsigned int i=0;i<remoteTagCount;++i) {
|
||||
if (rules[rn].v.tag.id == remoteTagIds[i]) {
|
||||
rtv = &(remoteTagValues[i]);
|
||||
break;
|
||||
const Tag *const localTag = std::lower_bound(&(nconf.tags[0]),&(nconf.tags[nconf.tagCount]),rules[rn].v.tag.id,Tag::IdComparePredicate());
|
||||
if ((localTag != &(nconf.tags[nconf.tagCount]))&&(localTag->id() == rules[rn].v.tag.id)) {
|
||||
const Tag *const remoteTag = ((membership) ? membership->getTag(nconf,rules[rn].v.tag.id) : (const Tag *)0);
|
||||
if (remoteTag) {
|
||||
const uint32_t ltv = localTag->value();
|
||||
const uint32_t rtv = remoteTag->value();
|
||||
if (rt == ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE) {
|
||||
const uint32_t diff = (ltv > rtv) ? (ltv - rtv) : (rtv - ltv);
|
||||
thisRuleMatches = (uint8_t)(diff <= rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%u remote:%u difference:%u<=%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,diff,(unsigned int)rules[rn].v.tag.value,thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
|
||||
thisRuleMatches = (uint8_t)((ltv & rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x & remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
|
||||
thisRuleMatches = (uint8_t)((ltv | rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x | remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
|
||||
thisRuleMatches = (uint8_t)((ltv ^ rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x ^ remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,ltv,rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else { // sanity check, can't really happen
|
||||
thisRuleMatches = 0;
|
||||
}
|
||||
}
|
||||
if (!rtv) {
|
||||
} else {
|
||||
if (inbound) {
|
||||
thisRuleMatches = 0;
|
||||
FILTER_TRACE("%u %s %c remote tag %u not found -> 0 (inbound side is strict)",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
|
||||
@ -534,24 +537,10 @@ static _doZtFilterResult _doZtFilter(
|
||||
thisRuleMatches = 1;
|
||||
FILTER_TRACE("%u %s %c remote tag %u not found -> 1 (outbound side is not strict)",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
|
||||
}
|
||||
} else {
|
||||
if (rt == ZT_NETWORK_RULE_MATCH_TAGS_DIFFERENCE) {
|
||||
const uint32_t diff = (lt->value() > *rtv) ? (lt->value() - *rtv) : (*rtv - lt->value());
|
||||
thisRuleMatches = (uint8_t)(diff <= rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%u remote:%u difference:%u<=%u -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,lt->value(),*rtv,diff,(unsigned int)rules[rn].v.tag.value,thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_AND) {
|
||||
thisRuleMatches = (uint8_t)((lt->value() & *rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x & remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,lt->value(),*rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_OR) {
|
||||
thisRuleMatches = (uint8_t)((lt->value() | *rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x | remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,lt->value(),*rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else if (rt == ZT_NETWORK_RULE_MATCH_TAGS_BITWISE_XOR) {
|
||||
thisRuleMatches = (uint8_t)((lt->value() ^ *rtv) == rules[rn].v.tag.value);
|
||||
FILTER_TRACE("%u %s %c TAG %u local:%.8x ^ remote:%.8x == %.8x -> %u",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id,lt->value(),*rtv,(unsigned int)rules[rn].v.tag.value,(unsigned int)thisRuleMatches);
|
||||
} else { // sanity check, can't really happen
|
||||
thisRuleMatches = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
thisRuleMatches = 0;
|
||||
FILTER_TRACE("%u %s %c local tag %u not found -> 0",rn,_rtn(rt),(((rules[rn].t & 0x80) != 0) ? '!' : '='),(unsigned int)rules[rn].v.tag.id);
|
||||
}
|
||||
} break;
|
||||
|
||||
@ -582,7 +571,6 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
|
||||
_portInitialized(false),
|
||||
_inboundConfigPacketId(0),
|
||||
_lastConfigUpdate(0),
|
||||
_lastRequestedConfiguration(0),
|
||||
_destroyed(false),
|
||||
_netconfFailure(NETCONF_FAILURE_NONE),
|
||||
_portError(0)
|
||||
@ -598,7 +586,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
|
||||
if (conf.length()) {
|
||||
dconf->load(conf.c_str());
|
||||
if (nconf->fromDictionary(*dconf)) {
|
||||
this->setConfiguration(*nconf,false);
|
||||
this->_setConfiguration(*nconf,false);
|
||||
_lastConfigUpdate = 0; // we still want to re-request a new config from the network
|
||||
gotConf = true;
|
||||
}
|
||||
@ -646,32 +634,27 @@ bool Network::filterOutgoingPacket(
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId)
|
||||
{
|
||||
uint32_t remoteTagIds[ZT_MAX_NETWORK_TAGS];
|
||||
uint32_t remoteTagValues[ZT_MAX_NETWORK_TAGS];
|
||||
Address ztFinalDest(ztDest);
|
||||
Address cc;
|
||||
const Capability *relevantCap = (const Capability *)0;
|
||||
unsigned int ccLength = 0;
|
||||
bool accept = false;
|
||||
const uint64_t now = RR->node->now();
|
||||
Address ztFinalDest(ztDest);
|
||||
int localCapabilityIndex = -1;
|
||||
bool accept = false;
|
||||
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
Membership *m = (Membership *)0;
|
||||
unsigned int remoteTagCount = 0;
|
||||
if (ztDest) {
|
||||
m = &(_memberships[ztDest]);
|
||||
remoteTagCount = m->getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
||||
}
|
||||
Membership *const membership = (ztDest) ? _memberships.get(ztDest) : (Membership *)0;
|
||||
|
||||
switch(_doZtFilter(RR,_config,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
||||
Address cc;
|
||||
unsigned int ccLength = 0;
|
||||
bool ccWatch = false;
|
||||
switch(_doZtFilter(RR,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
|
||||
|
||||
case DOZTFILTER_NO_MATCH:
|
||||
for(unsigned int c=0;c<_config.capabilityCount;++c) {
|
||||
ztFinalDest = ztDest; // sanity check
|
||||
ztFinalDest = ztDest; // sanity check, shouldn't be possible if there was no match
|
||||
Address cc2;
|
||||
unsigned int ccLength2 = 0;
|
||||
switch (_doZtFilter(RR,_config,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc2,ccLength2)) {
|
||||
bool ccWatch2 = false;
|
||||
switch (_doZtFilter(RR,_config,membership,false,ztSource,ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.capabilities[c].rules(),_config.capabilities[c].ruleCount(),cc2,ccLength2,ccWatch2)) {
|
||||
case DOZTFILTER_NO_MATCH:
|
||||
case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
|
||||
break;
|
||||
@ -679,16 +662,16 @@ bool Network::filterOutgoingPacket(
|
||||
case DOZTFILTER_REDIRECT: // interpreted as ACCEPT but ztFinalDest will have been changed in _doZtFilter()
|
||||
case DOZTFILTER_ACCEPT:
|
||||
case DOZTFILTER_SUPER_ACCEPT: // no difference in behavior on outbound side
|
||||
relevantCap = &(_config.capabilities[c]);
|
||||
localCapabilityIndex = (int)c;
|
||||
accept = true;
|
||||
|
||||
if ((!noTee)&&(cc2)) {
|
||||
Membership &m2 = _membership(cc2);
|
||||
m2.sendCredentialsIfNeeded(RR,now,cc2,_config,relevantCap);
|
||||
m2.pushCredentials(RR,now,cc2,_config,localCapabilityIndex,false);
|
||||
|
||||
Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02
|
||||
outp.append((uint8_t)(ccWatch2 ? 0x16 : 0x02));
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -715,13 +698,16 @@ bool Network::filterOutgoingPacket(
|
||||
}
|
||||
|
||||
if (accept) {
|
||||
if (membership)
|
||||
membership->pushCredentials(RR,now,ztDest,_config,localCapabilityIndex,false);
|
||||
|
||||
if ((!noTee)&&(cc)) {
|
||||
Membership &m2 = _membership(cc);
|
||||
m2.sendCredentialsIfNeeded(RR,now,cc,_config,relevantCap);
|
||||
m2.pushCredentials(RR,now,cc,_config,localCapabilityIndex,false);
|
||||
|
||||
Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02
|
||||
outp.append((uint8_t)(ccWatch ? 0x16 : 0x02));
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -732,11 +718,11 @@ bool Network::filterOutgoingPacket(
|
||||
|
||||
if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
|
||||
Membership &m2 = _membership(ztFinalDest);
|
||||
m2.sendCredentialsIfNeeded(RR,now,ztFinalDest,_config,relevantCap);
|
||||
m2.pushCredentials(RR,now,ztFinalDest,_config,localCapabilityIndex,false);
|
||||
|
||||
Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x02); // TEE/REDIRECT from outbound side: 0x02
|
||||
outp.append((uint8_t)0x04);
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -745,11 +731,9 @@ bool Network::filterOutgoingPacket(
|
||||
RR->sw->send(outp,true);
|
||||
|
||||
return false; // DROP locally, since we redirected
|
||||
} else if (m) {
|
||||
m->sendCredentialsIfNeeded(RR,now,ztDest,_config,relevantCap);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@ -765,28 +749,27 @@ int Network::filterIncomingPacket(
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId)
|
||||
{
|
||||
uint32_t remoteTagIds[ZT_MAX_NETWORK_TAGS];
|
||||
uint32_t remoteTagValues[ZT_MAX_NETWORK_TAGS];
|
||||
Address ztFinalDest(ztDest);
|
||||
Address cc;
|
||||
unsigned int ccLength = 0;
|
||||
int accept = 0;
|
||||
|
||||
Mutex::Lock _l(_lock);
|
||||
|
||||
Membership &m = _membership(sourcePeer->address());
|
||||
const unsigned int remoteTagCount = m.getAllTags(_config,remoteTagIds,remoteTagValues,ZT_MAX_NETWORK_TAGS);
|
||||
Membership &membership = _membership(sourcePeer->address());
|
||||
|
||||
switch (_doZtFilter(RR,_config,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc,ccLength)) {
|
||||
Address cc;
|
||||
unsigned int ccLength = 0;
|
||||
bool ccWatch = false;
|
||||
switch (_doZtFilter(RR,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,_config.rules,_config.ruleCount,cc,ccLength,ccWatch)) {
|
||||
|
||||
case DOZTFILTER_NO_MATCH: {
|
||||
Membership::CapabilityIterator mci(m);
|
||||
Membership::CapabilityIterator mci(membership,_config);
|
||||
const Capability *c;
|
||||
while ((c = mci.next(_config))) {
|
||||
ztFinalDest = ztDest; // sanity check
|
||||
while ((c = mci.next())) {
|
||||
ztFinalDest = ztDest; // sanity check, should be unmodified if there was no match
|
||||
Address cc2;
|
||||
unsigned int ccLength2 = 0;
|
||||
switch(_doZtFilter(RR,_config,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),_config.tags,_config.tagCount,remoteTagIds,remoteTagValues,remoteTagCount,cc2,ccLength2)) {
|
||||
bool ccWatch2 = false;
|
||||
switch(_doZtFilter(RR,_config,&membership,true,sourcePeer->address(),ztFinalDest,macSource,macDest,frameData,frameLen,etherType,vlanId,c->rules(),c->ruleCount(),cc2,ccLength2,ccWatch2)) {
|
||||
case DOZTFILTER_NO_MATCH:
|
||||
case DOZTFILTER_DROP: // explicit DROP in a capability just terminates its evaluation and is an anti-pattern
|
||||
break;
|
||||
@ -801,11 +784,11 @@ int Network::filterIncomingPacket(
|
||||
|
||||
if (accept) {
|
||||
if (cc2) {
|
||||
_membership(cc2).sendCredentialsIfNeeded(RR,RR->node->now(),cc2,_config,(const Capability *)0);
|
||||
_membership(cc2).pushCredentials(RR,RR->node->now(),cc2,_config,-1,false);
|
||||
|
||||
Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06
|
||||
outp.append((uint8_t)(ccWatch2 ? 0x1c : 0x08));
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -832,11 +815,11 @@ int Network::filterIncomingPacket(
|
||||
|
||||
if (accept) {
|
||||
if (cc) {
|
||||
_membership(cc).sendCredentialsIfNeeded(RR,RR->node->now(),cc,_config,(const Capability *)0);
|
||||
_membership(cc).pushCredentials(RR,RR->node->now(),cc,_config,-1,false);
|
||||
|
||||
Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06
|
||||
outp.append((uint8_t)(ccWatch ? 0x1c : 0x08));
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -846,11 +829,11 @@ int Network::filterIncomingPacket(
|
||||
}
|
||||
|
||||
if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
|
||||
_membership(ztFinalDest).sendCredentialsIfNeeded(RR,RR->node->now(),ztFinalDest,_config,(const Capability *)0);
|
||||
_membership(ztFinalDest).pushCredentials(RR,RR->node->now(),ztFinalDest,_config,-1,false);
|
||||
|
||||
Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(_id);
|
||||
outp.append((uint8_t)0x06); // TEE/REDIRECT from inbound side: 0x06
|
||||
outp.append((uint8_t)0x0a);
|
||||
macDest.appendTo(outp);
|
||||
macSource.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
@ -892,60 +875,6 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg)
|
||||
_myMulticastGroups.erase(i);
|
||||
}
|
||||
|
||||
bool Network::applyConfiguration(const NetworkConfig &conf)
|
||||
{
|
||||
if (_destroyed) // sanity check
|
||||
return false;
|
||||
try {
|
||||
if ((conf.networkId == _id)&&(conf.issuedTo == RR->identity.address())) {
|
||||
ZT_VirtualNetworkConfig ctmp;
|
||||
bool portInitialized;
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_config = conf;
|
||||
_lastConfigUpdate = RR->node->now();
|
||||
_netconfFailure = NETCONF_FAILURE_NONE;
|
||||
_externalConfig(&ctmp);
|
||||
portInitialized = _portInitialized;
|
||||
_portInitialized = true;
|
||||
}
|
||||
_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(portInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
|
||||
return true;
|
||||
} else {
|
||||
TRACE("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
|
||||
}
|
||||
} catch (std::exception &exc) {
|
||||
TRACE("ignored invalid configuration for network %.16llx (%s)",(unsigned long long)_id,exc.what());
|
||||
} catch ( ... ) {
|
||||
TRACE("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
|
||||
{
|
||||
try {
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
if (_config == nconf)
|
||||
return 1; // OK config, but duplicate of what we already have
|
||||
}
|
||||
if (applyConfiguration(nconf)) {
|
||||
if (saveToDisk) {
|
||||
char n[64];
|
||||
Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
|
||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> d;
|
||||
if (nconf.toDictionary(d,false))
|
||||
RR->node->dataStorePut(n,(const void *)d.data(),d.sizeBytes(),true);
|
||||
}
|
||||
return 2; // OK and configuration has changed
|
||||
}
|
||||
} catch ( ... ) {
|
||||
TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Network::handleInboundConfigChunk(const uint64_t inRePacketId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize)
|
||||
{
|
||||
std::string newConfig;
|
||||
@ -979,7 +908,8 @@ void Network::handleInboundConfigChunk(const uint64_t inRePacketId,const void *d
|
||||
Identity controllerId(RR->topology->getIdentity(this->controller()));
|
||||
if (controllerId) {
|
||||
if (nc->fromDictionary(*dict)) {
|
||||
this->setConfiguration(*nc,true);
|
||||
Mutex::Lock _l(_lock);
|
||||
this->_setConfiguration(*nc,true);
|
||||
} else {
|
||||
TRACE("error parsing new config with length %u: deserialization of NetworkConfig failed (certificate error?)",(unsigned int)newConfig.length());
|
||||
}
|
||||
@ -997,12 +927,6 @@ void Network::handleInboundConfigChunk(const uint64_t inRePacketId,const void *d
|
||||
|
||||
void Network::requestConfiguration()
|
||||
{
|
||||
// Sanity limit: do not request more often than once per second
|
||||
const uint64_t now = RR->node->now();
|
||||
if ((now - _lastRequestedConfiguration) < 1000ULL)
|
||||
return;
|
||||
_lastRequestedConfiguration = RR->node->now();
|
||||
|
||||
const Address ctrl(controller());
|
||||
|
||||
Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> rmd;
|
||||
@ -1024,9 +948,10 @@ void Network::requestConfiguration()
|
||||
if (RR->localNetworkController) {
|
||||
NetworkConfig nconf;
|
||||
switch(RR->localNetworkController->doNetworkConfigRequest(InetAddress(),RR->identity,RR->identity,_id,rmd,nconf)) {
|
||||
case NetworkController::NETCONF_QUERY_OK:
|
||||
this->setConfiguration(nconf,true);
|
||||
return;
|
||||
case NetworkController::NETCONF_QUERY_OK: {
|
||||
Mutex::Lock _l(_lock);
|
||||
this->_setConfiguration(nconf,true);
|
||||
} return;
|
||||
case NetworkController::NETCONF_QUERY_OBJECT_NOT_FOUND:
|
||||
this->setNotFound();
|
||||
return;
|
||||
@ -1073,7 +998,7 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
|
||||
if ( (_config.isPublic()) || ((m)&&(m->isAllowedOnNetwork(_config))) ) {
|
||||
if (!m)
|
||||
m = &(_membership(peer->address()));
|
||||
m->sendCredentialsIfNeeded(RR,now,peer->address(),_config,(const Capability *)0);
|
||||
m->pushCredentials(RR,now,peer->address(),_config,-1,false);
|
||||
if (m->shouldLikeMulticasts(now)) {
|
||||
_announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
|
||||
m->likingMulticasts(now);
|
||||
@ -1124,9 +1049,8 @@ void Network::clean()
|
||||
Membership *m = (Membership *)0;
|
||||
Hashtable<Address,Membership>::Iterator i(_memberships);
|
||||
while (i.next(a,m)) {
|
||||
if (RR->topology->getPeerNoCache(*a))
|
||||
m->clean(_config);
|
||||
else _memberships.erase(*a);
|
||||
if (!RR->topology->getPeerNoCache(*a))
|
||||
_memberships.erase(*a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1177,21 +1101,25 @@ void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
|
||||
_sendUpdatesToMembers(&mg);
|
||||
}
|
||||
|
||||
int Network::addCredential(const CertificateOfMembership &com)
|
||||
Membership::AddCredentialResult Network::addCredential(const CertificateOfMembership &com)
|
||||
{
|
||||
if (com.networkId() != _id)
|
||||
return -1;
|
||||
return Membership::ADD_REJECTED;
|
||||
const Address a(com.issuedTo());
|
||||
Mutex::Lock _l(_lock);
|
||||
Membership &m = _membership(a);
|
||||
const int result = m.addCredential(RR,com);
|
||||
if (result == 0) {
|
||||
m.sendCredentialsIfNeeded(RR,RR->node->now(),a,_config,(const Capability *)0);
|
||||
const Membership::AddCredentialResult result = m.addCredential(RR,_config,com);
|
||||
if ((result == Membership::ADD_ACCEPTED_NEW)||(result == Membership::ADD_ACCEPTED_REDUNDANT)) {
|
||||
m.pushCredentials(RR,RR->node->now(),a,_config,-1,false);
|
||||
RR->mc->addCredential(com,true);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Membership::AddCredentialResult Network::addCredential(const Revocation &rev)
|
||||
{
|
||||
}
|
||||
|
||||
void Network::destroy()
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
@ -1215,6 +1143,39 @@ ZT_VirtualNetworkStatus Network::_status() const
|
||||
}
|
||||
}
|
||||
|
||||
int Network::_setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
|
||||
{
|
||||
// assumes _lock is locked
|
||||
try {
|
||||
if ((nconf.issuedTo != RR->identity.address())||(nconf.networkId != _id))
|
||||
return 0;
|
||||
if (_config == nconf)
|
||||
return 1; // OK config, but duplicate of what we already have
|
||||
|
||||
ZT_VirtualNetworkConfig ctmp;
|
||||
_config = nconf;
|
||||
_lastConfigUpdate = RR->node->now();
|
||||
_netconfFailure = NETCONF_FAILURE_NONE;
|
||||
_externalConfig(&ctmp);
|
||||
const bool oldPortInitialized = _portInitialized;
|
||||
_portInitialized = true;
|
||||
_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
|
||||
|
||||
if (saveToDisk) {
|
||||
char n[64];
|
||||
Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
|
||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> d;
|
||||
if (nconf.toDictionary(d,false))
|
||||
RR->node->dataStorePut(n,(const void *)d.data(),d.sizeBytes(),true);
|
||||
}
|
||||
|
||||
return 2; // OK and configuration has changed
|
||||
} catch ( ... ) {
|
||||
TRACE("ignored invalid configuration for network %.16llx",(unsigned long long)_id);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
// assumes _lock is locked
|
||||
@ -1308,7 +1269,7 @@ void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGrou
|
||||
Membership *m = (Membership *)0;
|
||||
Hashtable<Address,Membership>::Iterator i(_memberships);
|
||||
while (i.next(a,m)) {
|
||||
m->sendCredentialsIfNeeded(RR,now,*a,_config,(const Capability *)0);
|
||||
m->pushCredentials(RR,now,*a,_config,-1,false);
|
||||
if ( ((newMulticastGroup)||(m->shouldLikeMulticasts(now))) && (m->isAllowedOnNetwork(_config)) ) {
|
||||
if (!newMulticastGroup)
|
||||
m->likingMulticasts(now);
|
||||
|
191
node/Network.hpp
191
node/Network.hpp
@ -62,6 +62,11 @@ public:
|
||||
*/
|
||||
static const MulticastGroup BROADCAST;
|
||||
|
||||
/**
|
||||
* Compute primary controller device ID from network ID
|
||||
*/
|
||||
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
||||
|
||||
/**
|
||||
* Construct a new network
|
||||
*
|
||||
@ -76,14 +81,24 @@ public:
|
||||
|
||||
~Network();
|
||||
|
||||
inline uint64_t id() const { return _id; }
|
||||
inline Address controller() const { return Address(_id >> 24); }
|
||||
inline bool multicastEnabled() const { return (_config.multicastLimit > 0); }
|
||||
inline bool hasConfig() const { return (_config); }
|
||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||
inline ZT_VirtualNetworkStatus status() const { Mutex::Lock _l(_lock); return _status(); }
|
||||
inline const NetworkConfig &config() const { return _config; }
|
||||
inline const MAC &mac() const { return _mac; }
|
||||
|
||||
/**
|
||||
* Apply filters to an outgoing packet
|
||||
*
|
||||
* This applies filters from our network config and, if that doesn't match,
|
||||
* our capabilities in ascending order of capability ID. Additional actions
|
||||
* such as TEE may be taken, and credentials may be pushed.
|
||||
* such as TEE may be taken, and credentials may be pushed, so this is not
|
||||
* side-effect-free. It's basically step one in sending something over VL2.
|
||||
*
|
||||
* @param noTee If true, do not TEE anything anywhere
|
||||
* @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
|
||||
* @param ztSource Source ZeroTier address
|
||||
* @param ztDest Destination ZeroTier address
|
||||
* @param macSource Ethernet layer source address
|
||||
@ -134,42 +149,10 @@ public:
|
||||
const unsigned int vlanId);
|
||||
|
||||
/**
|
||||
* @return Network ID
|
||||
*/
|
||||
inline uint64_t id() const throw() { return _id; }
|
||||
|
||||
/**
|
||||
* @return Address of network's controller (most significant 40 bits of ID)
|
||||
*/
|
||||
inline Address controller() const throw() { return Address(_id >> 24); }
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @return Address of network's controller
|
||||
*/
|
||||
static inline Address controllerFor(uint64_t nwid) throw() { return Address(nwid >> 24); }
|
||||
|
||||
/**
|
||||
* @return Multicast group memberships for this network's port (local, not learned via bridging)
|
||||
*/
|
||||
inline std::vector<MulticastGroup> multicastGroups() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _myMulticastGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return All multicast groups including learned groups that are behind any bridges we're attached to
|
||||
*/
|
||||
inline std::vector<MulticastGroup> allMulticastGroups() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _allMulticastGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether we are subscribed to a multicast group
|
||||
*
|
||||
* @param mg Multicast group
|
||||
* @param includeBridgedGroups If true, also include any groups we've learned via bridging
|
||||
* @param includeBridgedGroups If true, also check groups we've learned via bridging
|
||||
* @return True if this network endpoint / peer is a member
|
||||
*/
|
||||
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
|
||||
@ -188,37 +171,19 @@ public:
|
||||
*/
|
||||
void multicastUnsubscribe(const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Apply a NetworkConfig to this network
|
||||
*
|
||||
* @param conf Configuration in NetworkConfig form
|
||||
* @return True if configuration was accepted
|
||||
*/
|
||||
bool applyConfiguration(const NetworkConfig &conf);
|
||||
|
||||
/**
|
||||
* Set or update this network's configuration
|
||||
*
|
||||
* @param nconf Network configuration
|
||||
* @param saveToDisk IF true (default), write config to disk
|
||||
* @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
|
||||
*/
|
||||
int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
|
||||
|
||||
/**
|
||||
* Handle an inbound network config chunk
|
||||
*
|
||||
* Only chunks whose inRePacketId matches the packet ID of the last request
|
||||
* are handled. If this chunk completes the config, it is decoded and
|
||||
* setConfiguration() is called.
|
||||
* This is called from IncomingPacket when we receive a chunk from a network
|
||||
* controller.
|
||||
*
|
||||
* @param inRePacketId In-re packet ID from OK(NETWORK_CONFIG_REQUEST)
|
||||
* @param requestId An ID for grouping chunks, e.g. in-re packet ID for OK(NETWORK_CONFIG_REQUEST)
|
||||
* @param data Chunk data
|
||||
* @param chunkSize Size of data[]
|
||||
* @param chunkIndex Index of chunk in full config
|
||||
* @param totalSize Total size of network config
|
||||
*/
|
||||
void handleInboundConfigChunk(const uint64_t inRePacketId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize);
|
||||
void handleInboundConfigChunk(const uint64_t requestId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize);
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
|
||||
@ -230,7 +195,7 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
|
||||
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
|
||||
*/
|
||||
inline void setNotFound()
|
||||
{
|
||||
@ -240,10 +205,6 @@ public:
|
||||
|
||||
/**
|
||||
* Causes this network to request an updated configuration from its master node now
|
||||
*
|
||||
* There is a circuit breaker here to prevent this from being done more often
|
||||
* than once per second. This is to prevent things like NETWORK_CONFIG_REFRESH
|
||||
* from causing multiple requests.
|
||||
*/
|
||||
void requestConfiguration();
|
||||
|
||||
@ -251,7 +212,7 @@ public:
|
||||
* Determine whether this peer is permitted to communicate on this network
|
||||
*
|
||||
* This also performs certain periodic actions such as pushing renewed
|
||||
* credentials to peers or requesting them if not present.
|
||||
* credentials to peers, so like the filters it is not side-effect-free.
|
||||
*
|
||||
* @param peer Peer to check
|
||||
* @param verb Packet verb
|
||||
@ -266,7 +227,7 @@ public:
|
||||
bool gateMulticastGatherReply(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
|
||||
|
||||
/**
|
||||
* Perform cleanup and possibly save state
|
||||
* Do periodic cleanup and housekeeping tasks
|
||||
*/
|
||||
void clean();
|
||||
|
||||
@ -279,46 +240,6 @@ public:
|
||||
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time of last updated configuration or 0 if none
|
||||
*/
|
||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||
|
||||
/**
|
||||
* @return Status of this network
|
||||
*/
|
||||
inline ZT_VirtualNetworkStatus status() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _status();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ec Buffer to fill with externally-visible network configuration
|
||||
*/
|
||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_externalConfig(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current network config
|
||||
*
|
||||
* @return Network configuration (may be a null config if we don't have one yet)
|
||||
*/
|
||||
inline const NetworkConfig &config() const { return _config; }
|
||||
|
||||
/**
|
||||
* @return True if this network has a valid config
|
||||
*/
|
||||
inline bool hasConfig() const { return (_config); }
|
||||
|
||||
/**
|
||||
* @return Ethernet MAC address for this network's local interface
|
||||
*/
|
||||
inline const MAC &mac() const { return _mac; }
|
||||
|
||||
/**
|
||||
* Find the node on this network that has this MAC behind it (if any)
|
||||
*
|
||||
@ -349,44 +270,47 @@ public:
|
||||
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
||||
|
||||
/**
|
||||
* @param com Certificate of membership
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
int addCredential(const CertificateOfMembership &com);
|
||||
Membership::AddCredentialResult addCredential(const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* @param cap Capability
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline int addCredential(const Capability &cap)
|
||||
inline Membership::AddCredentialResult addCredential(const Capability &cap)
|
||||
{
|
||||
if (cap.networkId() != _id)
|
||||
return -1;
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(cap.issuedTo()).addCredential(RR,cap);
|
||||
return _membership(cap.issuedTo()).addCredential(RR,_config,cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param cap Tag
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline int addCredential(const Tag &tag)
|
||||
inline Membership::AddCredentialResult addCredential(const Tag &tag)
|
||||
{
|
||||
if (tag.networkId() != _id)
|
||||
return -1;
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(tag.issuedTo()).addCredential(RR,tag);
|
||||
return _membership(tag.issuedTo()).addCredential(RR,_config,tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blacklist COM, tags, and capabilities before this time
|
||||
*
|
||||
* @param ts Blacklist cutoff
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline void blacklistBefore(const Address &peerAddress,const uint64_t ts)
|
||||
Membership::AddCredentialResult addCredential(const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Force push credentials (COM, etc.) to a peer now
|
||||
*
|
||||
* @param to Destination peer address
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void pushCredentialsNow(const Address &to,const uint64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_membership(peerAddress).blacklistBefore(ts);
|
||||
_membership(to).pushCredentials(RR,now,to,_config,-1,true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -399,11 +323,23 @@ public:
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @return Pointer to user PTR (modifiable user ptr used in API)
|
||||
* Get this network's config for export via the ZT core API
|
||||
*
|
||||
* @param ec Buffer to fill with externally-visible network configuration
|
||||
*/
|
||||
inline void externalConfig(ZT_VirtualNetworkConfig *ec) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_externalConfig(ec);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Externally usable pointer-to-pointer exported via the core API
|
||||
*/
|
||||
inline void **userPtr() throw() { return &_uPtr; }
|
||||
|
||||
private:
|
||||
int _setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
|
||||
ZT_VirtualNetworkStatus _status() const;
|
||||
void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
|
||||
bool _gate(const SharedPtr<Peer> &peer);
|
||||
@ -412,9 +348,9 @@ private:
|
||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||
Membership &_membership(const Address &a);
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
const RuntimeEnvironment *const RR;
|
||||
void *_uPtr;
|
||||
uint64_t _id;
|
||||
const uint64_t _id;
|
||||
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||
MAC _mac; // local MAC address
|
||||
volatile bool _portInitialized;
|
||||
@ -428,7 +364,6 @@ private:
|
||||
|
||||
NetworkConfig _config;
|
||||
volatile uint64_t _lastConfigUpdate;
|
||||
volatile uint64_t _lastRequestedConfiguration;
|
||||
|
||||
volatile bool _destroyed;
|
||||
|
||||
|
@ -655,15 +655,27 @@ public:
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Certificate of network membership attached (DEPRECATED)
|
||||
* 0x02 - This is a TEE'd or REDIRECT'ed packet
|
||||
* 0x04 - TEE/REDIRECT'ed packet is from inbound side
|
||||
* 0x02 - Most significant bit of subtype (see below)
|
||||
* 0x04 - Middle bit of subtype (see below)
|
||||
* 0x08 - Least significant bit of subtype (see below)
|
||||
* 0x10 - ACK requested in the form of OK(EXT_FRAME)
|
||||
*
|
||||
* Subtypes (0..7):
|
||||
* 0x0 - Normal frame (bridging can be determined by checking MAC)
|
||||
* 0x1 - TEEd outbound frame
|
||||
* 0x2 - REDIRECTed outbound frame
|
||||
* 0x3 - WATCHed outbound frame (TEE with ACK, ACK bit also set)
|
||||
* 0x4 - TEEd inbound frame
|
||||
* 0x5 - REDIRECTed inbound frame
|
||||
* 0x6 - WATCHed inbound frame
|
||||
* 0x7 - (reserved for future use)
|
||||
*
|
||||
* An extended frame carries full MAC addressing, making them a
|
||||
* superset of VERB_FRAME. They're used for bridging or when we
|
||||
* want to attach a certificate since FRAME does not support that.
|
||||
*
|
||||
* ERROR may be generated if a membership certificate is needed for a
|
||||
* closed network. Payload will be network ID.
|
||||
* If the ACK flag (0x08) is set, an OK(EXT_FRAME) is sent with
|
||||
* no payload to acknowledge receipt of the frame.
|
||||
*/
|
||||
VERB_EXT_FRAME = 0x07,
|
||||
|
||||
@ -698,7 +710,7 @@ public:
|
||||
VERB_MULTICAST_LIKE = 0x09,
|
||||
|
||||
/**
|
||||
* Network membership credential push:
|
||||
* Network credentials push:
|
||||
* <[...] serialized certificate of membership>
|
||||
* [<[...] additional certificates of membership>]
|
||||
* <[1] 0x00, null byte marking end of COM array>
|
||||
@ -706,12 +718,12 @@ public:
|
||||
* <[...] one or more serialized Capability>
|
||||
* <[2] 16-bit number of tags>
|
||||
* <[...] one or more serialized Tags>
|
||||
* <[2] 16-bit number of revocations>
|
||||
* <[...] one or more serialized Revocations>
|
||||
*
|
||||
* This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
|
||||
* be pushed at any other time to keep exchanged certificates up to date.
|
||||
*
|
||||
* COMs and other credentials need not be for the same network, since each
|
||||
* includes its own network ID and signature.
|
||||
* This can be sent by anyone at any time to push network credentials.
|
||||
* These will of course only be accepted if they are properly signed.
|
||||
* Credentials can be for any number of networks.
|
||||
*
|
||||
* OK/ERROR are not generated.
|
||||
*/
|
||||
@ -742,23 +754,18 @@ public:
|
||||
VERB_NETWORK_CONFIG_REQUEST = 0x0b,
|
||||
|
||||
/**
|
||||
* Network configuration update push:
|
||||
* <[8] network ID to refresh>
|
||||
* <[2] 16-bit number of address/timestamp pairs to blacklist>
|
||||
* [<[5] ZeroTier address of peer being revoked>]
|
||||
* [<[8] blacklist credentials older than this timestamp>]
|
||||
* [<[...] additional address/timestamp pairs>]
|
||||
* Network configuration push:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[8] 64-bit value used to group chunks in this push>
|
||||
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||
* <[...] network configuration dictionary (may be incomplete)>
|
||||
* <[4] 32-bit total length of assembled dictionary>
|
||||
* <[4] 32-bit index of chunk in this reply>
|
||||
*
|
||||
* This can be sent by a network controller to both request that a network
|
||||
* config be updated and push instantaneous revocations of specific peers
|
||||
* or peer credentials.
|
||||
*
|
||||
* Specific revocations can be pushed to blacklist a specific peer's
|
||||
* credentials (COM, tags, and capabilities) if older than a specified
|
||||
* timestamp. This can be used to accomplish expedited revocation of
|
||||
* a peer's access to things on a network or to the network itself among
|
||||
* those other peers that can currently reach the controller. This is not
|
||||
* the only mechanism for revocation of course, but it's the fastest.
|
||||
* This is a direct push variant for network config updates. It otherwise
|
||||
* carries the same payload as OK(NETWORK_CONFIG_REQUEST). There is an
|
||||
* extra number after network ID in this version that is used in place of
|
||||
* the in-re packet ID sent with OKs to group chunks together.
|
||||
*/
|
||||
VERB_NETWORK_CONFIG_REFRESH = 0x0c,
|
||||
|
||||
|
46
node/Revocation.cpp
Normal file
46
node/Revocation.cpp
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#include "Revocation.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
int Revocation::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
|
||||
return -1;
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_signedBy);
|
||||
return 1;
|
||||
}
|
||||
try {
|
||||
Buffer<sizeof(Revocation) + 64> tmp;
|
||||
this->serialize(tmp,true);
|
||||
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
|
||||
} catch ( ... ) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
178
node/Revocation.hpp
Normal file
178
node/Revocation.hpp
Normal file
@ -0,0 +1,178 @@
|
||||
/*
|
||||
* ZeroTier One - Network Virtualization Everywhere
|
||||
* Copyright (C) 2011-2016 ZeroTier, Inc. https://www.zerotier.com/
|
||||
*
|
||||
* 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/>.
|
||||
*/
|
||||
|
||||
#ifndef ZT_REVOCATION_HPP
|
||||
#define ZT_REVOCATION_HPP
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
#include "Address.hpp"
|
||||
#include "C25519.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Identity.hpp"
|
||||
|
||||
/**
|
||||
* Flag: fast propagation via rumor mill algorithm
|
||||
*/
|
||||
#define ZT_REVOCATION_FLAG_FAST_PROPAGATE 0x1ULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Revocation certificate to instantaneously revoke a COM, capability, or tag
|
||||
*/
|
||||
class Revocation
|
||||
{
|
||||
public:
|
||||
enum CredentialType
|
||||
{
|
||||
CREDENTIAL_TYPE_NIL = 0,
|
||||
CREDENTIAL_TYPE_COM = 1,
|
||||
CREDENTIAL_TYPE_CAPABILITY = 2,
|
||||
CREDENTIAL_TYPE_TAG = 3
|
||||
};
|
||||
|
||||
Revocation()
|
||||
{
|
||||
memset(this,0,sizeof(Revocation));
|
||||
}
|
||||
|
||||
Revocation(const uint64_t i,const uint64_t nwid,const uint64_t cid,const uint64_t thr,const uint64_t fl,const Address &tgt,const CredentialType ct) :
|
||||
_id(i),
|
||||
_networkId(nwid),
|
||||
_credentialId(cid),
|
||||
_threshold(thr),
|
||||
_flags(fl),
|
||||
_target(tgt),
|
||||
_signedBy(),
|
||||
_type(ct) {}
|
||||
|
||||
inline uint64_t id() const { return _id; }
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline uint64_t credentialId() const { return _credentialId; }
|
||||
inline uint64_t threshold() const { return _threshold; }
|
||||
inline const Address &target() const { return _target; }
|
||||
inline const Address &signer() const { return _signedBy; }
|
||||
inline CredentialType type() const { return _type; }
|
||||
|
||||
inline bool fastPropagate() const { return ((_flags & ZT_REVOCATION_FLAG_FAST_PROPAGATE) != 0); }
|
||||
|
||||
/**
|
||||
* @param signer Signing identity, must have private key
|
||||
* @return True if signature was successful
|
||||
*/
|
||||
inline bool sign(const Identity &signer)
|
||||
{
|
||||
if (signer.hasPrivate()) {
|
||||
Buffer<sizeof(Revocation) + 64> tmp;
|
||||
this->serialize(tmp,true);
|
||||
_signedBy = signer.address();
|
||||
_signature = signer.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify this revocation's signature
|
||||
*
|
||||
* @param RR Runtime environment to provide for peer lookup, etc.
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) const;
|
||||
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||
{
|
||||
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
|
||||
b.append(_id);
|
||||
b.append(_networkId);
|
||||
b.append(_credentialId);
|
||||
b.append(_threshold);
|
||||
b.append(_flags);
|
||||
_target.appendTo(b);
|
||||
_signedBy.appendTo(b);
|
||||
b.append((uint8_t)_type);
|
||||
|
||||
if (!forSign) {
|
||||
b.append((uint8_t)1); // 1 == Ed25519 signature
|
||||
b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
}
|
||||
|
||||
// This is the size of any additional fields, currently 0.
|
||||
b.append((uint16_t)0);
|
||||
|
||||
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
memset(this,0,sizeof(Revocation));
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
_id = b.template at<uint64_t>(p); p += 8;
|
||||
_networkId = b.template at<uint64_t>(p); p += 8;
|
||||
_credentialId = b.template at<uint64_t>(p); p += 8;
|
||||
_threshold = b.template at<uint64_t>(p); p += 8;
|
||||
_flags = b.template at<uint64_t>(p); p += 8;
|
||||
_target.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
|
||||
_signedBy.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
|
||||
_type = (CredentialType)b[p++];
|
||||
|
||||
if (b[p++] == 1) {
|
||||
if (b.template at<uint16_t>(p) == ZT_C25519_SIGNATURE_LEN) {
|
||||
p += 2;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN);
|
||||
p += ZT_C25519_SIGNATURE_LEN;
|
||||
} else throw std::runtime_error("invalid signature");
|
||||
}
|
||||
|
||||
p += 2 + b.template at<uint16_t>(p);
|
||||
if (p > b.size())
|
||||
throw std::runtime_error("extended field overflow");
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _id;
|
||||
uint64_t _networkId;
|
||||
uint64_t _credentialId;
|
||||
uint64_t _threshold;
|
||||
uint64_t _flags;
|
||||
Address _target;
|
||||
Address _signedBy;
|
||||
CredentialType _type;
|
||||
C25519::Signature _signature;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif
|
@ -27,7 +27,7 @@ namespace ZeroTier {
|
||||
|
||||
int Tag::verify(const RuntimeEnvironment *RR) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_nwid)))
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
|
||||
return -1;
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
|
30
node/Tag.hpp
30
node/Tag.hpp
@ -67,7 +67,7 @@ public:
|
||||
* @param value Tag value
|
||||
*/
|
||||
Tag(const uint64_t nwid,const uint64_t ts,const Address &issuedTo,const uint32_t id,const uint32_t value) :
|
||||
_nwid(nwid),
|
||||
_networkId(nwid),
|
||||
_ts(ts),
|
||||
_id(id),
|
||||
_value(value),
|
||||
@ -76,7 +76,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
inline uint64_t networkId() const { return _nwid; }
|
||||
inline uint64_t networkId() const { return _networkId; }
|
||||
inline uint64_t timestamp() const { return _ts; }
|
||||
inline uint32_t id() const { return _id; }
|
||||
inline const uint32_t &value() const { return _value; }
|
||||
@ -91,13 +91,13 @@ public:
|
||||
*/
|
||||
inline bool sign(const Identity &signer)
|
||||
{
|
||||
try {
|
||||
Buffer<(sizeof(Tag) * 2)> tmp;
|
||||
if (signer.hasPrivate()) {
|
||||
Buffer<sizeof(Tag) + 64> tmp;
|
||||
_signedBy = signer.address();
|
||||
this->serialize(tmp,true);
|
||||
_signature = signer.sign(tmp.data(),tmp.size());
|
||||
return true;
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -115,7 +115,7 @@ public:
|
||||
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
|
||||
// These are the same between Tag and Capability
|
||||
b.append(_nwid);
|
||||
b.append(_networkId);
|
||||
b.append(_ts);
|
||||
b.append(_id);
|
||||
|
||||
@ -140,7 +140,7 @@ public:
|
||||
unsigned int p = startAt;
|
||||
|
||||
// These are the same between Tag and Capability
|
||||
_nwid = b.template at<uint64_t>(p); p += 8;
|
||||
_networkId = b.template at<uint64_t>(p); p += 8;
|
||||
_ts = b.template at<uint64_t>(p); p += 8;
|
||||
_id = b.template at<uint32_t>(p); p += 4;
|
||||
|
||||
@ -168,8 +168,22 @@ public:
|
||||
inline bool operator==(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) == 0); }
|
||||
inline bool operator!=(const Tag &t) const { return (memcmp(this,&t,sizeof(Tag)) != 0); }
|
||||
|
||||
// For searching sorted arrays or lists of Tags by ID
|
||||
struct IdComparePredicate
|
||||
{
|
||||
inline bool operator()(const Tag &a,const Tag &b) const { return (a.id() < b.id()); }
|
||||
inline bool operator()(const uint32_t a,const Tag &b) const { return (a < b.id()); }
|
||||
inline bool operator()(const Tag &a,const uint32_t b) const { return (a.id() < b); }
|
||||
inline bool operator()(const Tag *a,const Tag *b) const { return (a->id() < b->id()); }
|
||||
inline bool operator()(const Tag *a,const Tag &b) const { return (a->id() < b.id()); }
|
||||
inline bool operator()(const Tag &a,const Tag *b) const { return (a.id() < b->id()); }
|
||||
inline bool operator()(const uint32_t a,const Tag *b) const { return (a < b->id()); }
|
||||
inline bool operator()(const Tag *a,const uint32_t b) const { return (a->id() < b); }
|
||||
inline bool operator()(const uint32_t a,const uint32_t b) const { return (a < b); }
|
||||
};
|
||||
|
||||
private:
|
||||
uint64_t _nwid;
|
||||
uint64_t _networkId;
|
||||
uint64_t _ts;
|
||||
uint32_t _id;
|
||||
uint32_t _value;
|
||||
|
@ -17,6 +17,7 @@ OBJS=\
|
||||
node/Path.o \
|
||||
node/Peer.o \
|
||||
node/Poly1305.o \
|
||||
node/Revocation.o \
|
||||
node/Salsa20.o \
|
||||
node/SelfAwareness.o \
|
||||
node/SHA512.o \
|
||||
|
Loading…
x
Reference in New Issue
Block a user