summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--controller/EmbeddedNetworkController.cpp12
-rw-r--r--include/ZeroTierOne.h9
-rw-r--r--node/Capability.hpp2
-rw-r--r--node/IncomingPacket.cpp76
-rw-r--r--node/Membership.cpp234
-rw-r--r--node/Membership.hpp263
-rw-r--r--node/Network.cpp289
-rw-r--r--node/Network.hpp189
-rw-r--r--node/Packet.hpp63
-rw-r--r--node/Revocation.cpp46
-rw-r--r--node/Revocation.hpp178
-rw-r--r--node/Tag.cpp2
-rw-r--r--node/Tag.hpp30
-rw-r--r--objects.mk1
14 files changed, 803 insertions, 591 deletions
diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
index 53b345b4..5ba8cf98 100644
--- a/controller/EmbeddedNetworkController.cpp
+++ b/controller/EmbeddedNetworkController.cpp
@@ -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;
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index e0f6ca28..e43c8541 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -517,14 +517,19 @@ 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
diff --git a/node/Capability.hpp b/node/Capability.hpp
index e23d7943..2cf54b5c 100644
--- a/node/Capability.hpp
+++ b/node/Capability.hpp
@@ -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);
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index b3925773..12766fe2 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -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 ( ... ) {
diff --git a/node/Membership.cpp b/node/Membership.cpp
index 8c2ba673..d579d303 100644
--- a/node/Membership.cpp
+++ b/node/Membership.cpp
@@ -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)
+{
+ 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)
{
- if ((now - _lastPushAttempt) < 2000ULL)
+ // 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
diff --git a/node/Membership.hpp b/node/Membership.hpp
index 5eb68d34..421e3ee8 100644
--- a/node/Membership.hpp
+++ b/node/Membership.hpp
@@ -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
+ * @param nconf Network configuration
+ * @param id Capablity ID
+ * @return Pointer to capability or NULL if not found
*/
- 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));
- }
+ 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
*/
- 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 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);
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,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)
+private:
+ template<typename C,typename CS>
+ inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred,const CS &state) const
{
- 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);
- }
+ const uint64_t ts = cred.timestamp();
+ return ( (((ts >= nconf.timestamp) ? (ts - nconf.timestamp) : (nconf.timestamp - ts)) <= nconf.credentialTimeMaxDelta) && (ts > state.revocationThreshold) );
}
-private:
// 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];
+
+ // 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];
- // Tag-related state
- Hashtable<uint32_t,TState> _tags;
+ // Local credential push state tracking
+ _LocalCredentialPushState _localTags[ZT_MAX_NETWORK_TAGS];
+ _LocalCredentialPushState _localCaps[ZT_MAX_NETWORK_CAPABILITIES];
};
} // namespace ZeroTier
diff --git a/node/Network.cpp b/node/Network.cpp
index 455e185e..0fab6a27 100644
--- a/node/Network.cpp
+++ b/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,50 +507,40 @@ 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;
- }
- }
- if (!rtv) {
- 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);
- } else {
- 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 {
+ 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 = (lt->value() > *rtv) ? (lt->value() - *rtv) : (*rtv - lt->value());
+ 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,lt->value(),*rtv,diff,(unsigned int)rules[rn].v.tag.value,thisRuleMatches);
+ 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)((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);
+ 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)((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);
+ 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)((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);
+ 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;
}
+ } 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);
+ } else {
+ 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 {
+ 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];
+ const uint64_t now = RR->node->now();
Address ztFinalDest(ztDest);
- Address cc;
- const Capability *relevantCap = (const Capability *)0;
- unsigned int ccLength = 0;
+ int localCapabilityIndex = -1;
bool accept = false;
- const uint64_t now = RR->node->now();
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);
diff --git a/node/Network.hpp b/node/Network.hpp
index c85e5993..a151fb88 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -63,6 +63,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
*
* Note that init() should be called immediately after the network is
@@ -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;
@@ -189,36 +172,18 @@ 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();
@@ -280,46 +241,6 @@ public:
}
/**
- * @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)
*
* @param mac MAC address
@@ -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
+ * Validate a credential and learn it if it passes certificate and other checks
+ */
+ Membership::AddCredentialResult addCredential(const Revocation &rev);
+
+ /**
+ * Force push credentials (COM, etc.) to a peer now
*
- * @param ts Blacklist cutoff
+ * @param to Destination peer address
+ * @param now Current time
*/
- inline void blacklistBefore(const Address &peerAddress,const uint64_t ts)
+ 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;
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 2ca73a84..03b9b113 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -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>]
- *
- * 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.
+ * 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 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,
diff --git a/node/Revocation.cpp b/node/Revocation.cpp
new file mode 100644
index 00000000..420476a4
--- /dev/null
+++ b/node/Revocation.cpp
@@ -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
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
new file mode 100644
index 00000000..58757465
--- /dev/null
+++ b/node/Revocation.hpp
@@ -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
diff --git a/node/Tag.cpp b/node/Tag.cpp
index 352ecde8..eb4026bc 100644
--- a/node/Tag.cpp
+++ b/node/Tag.cpp
@@ -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) {
diff --git a/node/Tag.hpp b/node/Tag.hpp
index 14cc3a5d..97228157 100644
--- a/node/Tag.hpp
+++ b/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;
diff --git a/objects.mk b/objects.mk
index f92a907e..5738e769 100644
--- a/objects.mk
+++ b/objects.mk
@@ -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 \