summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
Diffstat (limited to 'node')
-rw-r--r--node/IncomingPacket.cpp53
-rw-r--r--node/IncomingPacket.hpp2
-rw-r--r--node/Membership.cpp143
-rw-r--r--node/Membership.hpp21
-rw-r--r--node/Network.cpp37
-rw-r--r--node/Network.hpp2
-rw-r--r--node/Packet.cpp2
-rw-r--r--node/Packet.hpp28
-rw-r--r--node/Revocation.hpp2
9 files changed, 219 insertions, 71 deletions
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 12766fe2..c50db794 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -67,7 +67,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
return _doHELLO(RR,false);
}
- SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
if (peer) {
if (!trusted) {
if (!dearmor(peer->key())) {
@@ -100,7 +100,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,peer);
case Packet::VERB_NETWORK_CREDENTIALS: return _doNETWORK_CREDENTIALS(RR,peer);
case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer);
+ case Packet::VERB_NETWORK_CONFIG: return _doNETWORK_CONFIG(RR,peer);
case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer);
case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer);
case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer);
@@ -131,12 +131,18 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
//TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
+ /* Security note: we do not gate doERROR() with expectingReplyTo() to
+ * avoid having to log every outgoing packet ID. Instead we put the
+ * logic to determine whether we should consider an ERROR in each
+ * error handler. In most cases these are only trusted in specific
+ * circumstances. */
+
switch(errorCode) {
case Packet::ERROR_OBJ_NOT_FOUND:
// Object not found, currently only meaningful from network controllers.
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setNotFound();
}
@@ -147,7 +153,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
// consider it meaningful from network controllers. This would indicate
// that the queried node does not support acting as a controller.
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setNotFound();
}
@@ -161,7 +167,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
// 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 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)) )
network->pushCredentialsNow(peer->address(),now);
@@ -169,7 +175,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
// Network controller: network access denied.
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setAccessDenied();
} break;
@@ -177,9 +183,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_UNWANTED_MULTICAST: {
// Members of networks can use this error to indicate that they no longer
// want to receive multicasts on a given channel.
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->gate(peer,verb(),packetId()))) {
- MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
RR->mc->remove(network->id(),mg,peer->address());
}
@@ -371,7 +377,6 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
bool trustEstablished = false;
- // Don't parse OK packets that are not in response to a packet ID we sent
if (!RR->node->expectingReplyTo(inRePacketId)) {
TRACE("%s(%s): OK(%s) DROPPED: not expecting reply to %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb),packetId());
return true;
@@ -450,7 +455,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
case Packet::VERB_MULTICAST_GATHER: {
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
- SharedPtr<Network> network(RR->node->network(nwid));
+ const SharedPtr<Network> network(RR->node->network(nwid));
if ((network)&&(network->gateMulticastGatherReply(peer,verb(),packetId()))) {
trustEstablished = true;
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
@@ -467,7 +472,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
//TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),flags);
- SharedPtr<Network> network(RR->node->network(nwid));
+ const SharedPtr<Network> network(RR->node->network(nwid));
if (network) {
unsigned int offset = 0;
@@ -683,6 +688,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((uint8_t)Packet::VERB_EXT_FRAME);
outp.append((uint64_t)packetId());
+ outp.append((uint64_t)nwid);
outp.armor(peer->key(),true);
_path->send(RR,outp.data(),outp.size(),RR->node->now());
}
@@ -727,7 +733,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
try {
const uint64_t now = RR->node->now();
- uint64_t authOnNetwork[256];
+ uint64_t authOnNetwork[256]; // cache for approved network IDs
unsigned int authOnNetworkCount = 0;
SharedPtr<Network> network;
bool trustEstablished = false;
@@ -786,7 +792,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
while ((p < size())&&((*this)[p])) {
p += com.deserialize(*this,p);
if (com) {
- SharedPtr<Network> network(RR->node->network(com.networkId()));
+ const SharedPtr<Network> network(RR->node->network(com.networkId()));
if (network) {
switch (network->addCredential(com)) {
case Membership::ADD_REJECTED:
@@ -803,11 +809,11 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
}
++p; // skip trailing 0 after COMs if present
- if (p < size()) { // check if new capabilities and tags fields are present
+ if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numCapabilities;++i) {
p += cap.deserialize(*this,p);
- SharedPtr<Network> network(RR->node->network(cap.networkId()));
+ const SharedPtr<Network> network(RR->node->network(cap.networkId()));
if (network) {
switch (network->addCredential(cap)) {
case Membership::ADD_REJECTED:
@@ -825,7 +831,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
const unsigned int numTags = at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numTags;++i) {
p += tag.deserialize(*this,p);
- SharedPtr<Network> network(RR->node->network(tag.networkId()));
+ const SharedPtr<Network> network(RR->node->network(tag.networkId()));
if (network) {
switch (network->addCredential(tag)) {
case Membership::ADD_REJECTED:
@@ -843,8 +849,18 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
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()));
+ const SharedPtr<Network> network(RR->node->network(revocation.networkId()));
if (network) {
+ switch(network->addCredential(peer->address(),revocation)) {
+ 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;
+ }
}
}
}
@@ -879,6 +895,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
try {
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
dconf->wrapWithSignature(ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE,RR->identity.privateKeyPair());
+
const unsigned int totalSize = dconf->sizeBytes();
unsigned int chunkIndex = 0;
while (chunkIndex < totalSize) {
@@ -957,7 +974,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
return true;
}
-bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
{
try {
const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp
index dbaf67b8..86c2b5e7 100644
--- a/node/IncomingPacket.hpp
+++ b/node/IncomingPacket.hpp
@@ -146,7 +146,7 @@ private:
bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
- bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
+ bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
diff --git a/node/Membership.cpp b/node/Membership.cpp
index d579d303..b7e33936 100644
--- a/node/Membership.cpp
+++ b/node/Membership.cpp
@@ -167,24 +167,9 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
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;
- }
- }
- have->lastReceived = RR->node->now();
- have->tag = tag;
- std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),_RemoteCredentialSorter<_RemoteTag>());
- }
+ if (!have) have = _newTag(tag.id());
+ have->lastReceived = RR->node->now();
+ have->tag = tag;
return ADD_ACCEPTED_NEW;
case 1:
return ADD_DEFERRED_FOR_WHOIS;
@@ -212,28 +197,114 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
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;
- }
- }
- have->lastReceived = RR->node->now();
- have->cap = cap;
- std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),_RemoteCredentialSorter<_RemoteCapability>());
- }
+ if (!have) have = _newCapability(cap.id());
+ have->lastReceived = RR->node->now();
+ have->cap = cap;
return ADD_ACCEPTED_NEW;
case 1:
return ADD_DEFERRED_FOR_WHOIS;
}
}
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev)
+{
+ switch(rev.verify(RR)) {
+ default:
+ return ADD_REJECTED;
+ case 0: {
+ const uint64_t now = RR->node->now();
+ switch(rev.type()) {
+ default:
+ //case Revocation::CREDENTIAL_TYPE_ALL:
+ return ( (_revokeCom(rev)||_revokeCap(rev,now)||_revokeTag(rev,now)) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT );
+ case Revocation::CREDENTIAL_TYPE_COM:
+ return (_revokeCom(rev) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
+ case Revocation::CREDENTIAL_TYPE_CAPABILITY:
+ return (_revokeCap(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
+ case Revocation::CREDENTIAL_TYPE_TAG:
+ return (_revokeTag(rev,now) ? ADD_ACCEPTED_NEW : ADD_ACCEPTED_REDUNDANT);
+ }
+ }
+ case 1:
+ return ADD_DEFERRED_FOR_WHOIS;
+ }
+}
+
+Membership::_RemoteTag *Membership::_newTag(const uint64_t id)
+{
+ _RemoteTag *t;
+ uint64_t minlr = 0xffffffffffffffffULL;
+ for(unsigned int i=0;i<ZT_MAX_NETWORK_TAGS;++i) {
+ if (_remoteTags[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
+ t = _remoteTags[i];
+ break;
+ } else if (_remoteTags[i]->lastReceived <= minlr) {
+ t = _remoteTags[i];
+ minlr = _remoteTags[i]->lastReceived;
+ }
+ }
+ t->id = id;
+ t->lastReceived = 0;
+ t->revocationThreshold = 0;
+ t->tag = Tag();
+ std::sort(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),_RemoteCredentialSorter<_RemoteTag>());
+ return t;
+}
+
+Membership::_RemoteCapability *Membership::_newCapability(const uint64_t id)
+{
+ _RemoteCapability *c;
+ uint64_t minlr = 0xffffffffffffffffULL;
+ for(unsigned int i=0;i<ZT_MAX_NETWORK_CAPABILITIES;++i) {
+ if (_remoteCaps[i]->id == ZT_MEMBERSHIP_CRED_ID_UNUSED) {
+ c = _remoteCaps[i];
+ break;
+ } else if (_remoteCaps[i]->lastReceived <= minlr) {
+ c = _remoteCaps[i];
+ minlr = _remoteCaps[i]->lastReceived;
+ }
+ }
+ c->id = id;
+ c->lastReceived = 0;
+ c->revocationThreshold = 0;
+ c->cap = Capability();
+ std::sort(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),_RemoteCredentialSorter<_RemoteCapability>());
+ return c;
+}
+
+bool Membership::_revokeCom(const Revocation &rev)
+{
+ if (rev.threshold() > _comRevocationThreshold) {
+ _comRevocationThreshold = rev.threshold();
+ return true;
+ }
+ return false;
+}
+
+bool Membership::_revokeCap(const Revocation &rev,const uint64_t now)
+{
+ _RemoteCapability *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)rev.credentialId(),_RemoteCredentialSorter<_RemoteCapability>());
+ _RemoteCapability *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteCapability *)0;
+ if (!have) have = _newCapability(rev.credentialId());
+ if (rev.threshold() > have->revocationThreshold) {
+ have->lastReceived = now;
+ have->revocationThreshold = rev.threshold();
+ return true;
+ }
+ return false;
+}
+
+bool Membership::_revokeTag(const Revocation &rev,const uint64_t now)
+{
+ _RemoteTag *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)rev.credentialId(),_RemoteCredentialSorter<_RemoteTag>());
+ _RemoteTag *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)rev.credentialId())) ? *htmp : (_RemoteTag *)0;
+ if (!have) have = _newTag(rev.credentialId());
+ if (rev.threshold() > have->revocationThreshold) {
+ have->lastReceived = now;
+ have->revocationThreshold = rev.threshold();
+ return true;
+ }
+ return false;
+}
+
} // namespace ZeroTier
diff --git a/node/Membership.hpp b/node/Membership.hpp
index 421e3ee8..c54aec9b 100644
--- a/node/Membership.hpp
+++ b/node/Membership.hpp
@@ -29,6 +29,8 @@
#include "Revocation.hpp"
#include "NetworkConfig.hpp"
+#define ZT_MEMBERSHIP_CRED_ID_UNUSED 0xffffffffffffffffULL
+
namespace ZeroTier {
class RuntimeEnvironment;
@@ -48,7 +50,7 @@ private:
// Tags and related state
struct _RemoteTag
{
- _RemoteTag() : id(0xffffffffffffffffULL),lastReceived(0),revocationThreshold(0) {}
+ _RemoteTag() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),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)
@@ -62,7 +64,7 @@ private:
// Credentials and related state
struct _RemoteCapability
{
- _RemoteCapability() : id(0xffffffffffffffffULL),lastReceived(0),revocationThreshold(0) {}
+ _RemoteCapability() : id(ZT_MEMBERSHIP_CRED_ID_UNUSED),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)
@@ -114,7 +116,7 @@ public:
inline const Capability *next()
{
for(;;) {
- if ((_i != &(_m->_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*_i)->id != 0xffffffffffffffffULL)) {
+ if ((_i != &(_m->_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
const Capability *tmp = &((*_i)->cap);
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
++_i;
@@ -147,7 +149,7 @@ public:
inline const Tag *next()
{
for(;;) {
- if ((_i != &(_m->_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*_i)->id != 0xffffffffffffffffULL)) {
+ if ((_i != &(_m->_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*_i)->id != ZT_MEMBERSHIP_CRED_ID_UNUSED)) {
const Tag *tmp = &((*_i)->tag);
if (_m->_isCredentialTimestampValid(*_c,*tmp,**_i)) {
++_i;
@@ -242,7 +244,18 @@ public:
*/
AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap);
+ /**
+ * Validate and add a credential if signature is okay and it's otherwise good
+ */
+ AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev);
+
private:
+ _RemoteTag *_newTag(const uint64_t id);
+ _RemoteCapability *_newCapability(const uint64_t id);
+ bool _revokeCom(const Revocation &rev);
+ bool _revokeCap(const Revocation &rev,const uint64_t now);
+ bool _revokeTag(const Revocation &rev,const uint64_t now);
+
template<typename C,typename CS>
inline bool _isCredentialTimestampValid(const NetworkConfig &nconf,const C &cred,const CS &state) const
{
diff --git a/node/Network.cpp b/node/Network.cpp
index 0fab6a27..487766a7 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 {
@@ -1116,8 +1116,41 @@ Membership::AddCredentialResult Network::addCredential(const CertificateOfMember
return result;
}
-Membership::AddCredentialResult Network::addCredential(const Revocation &rev)
+Membership::AddCredentialResult Network::addCredential(const Address &sentFrom,const Revocation &rev)
{
+ if (rev.networkId() != _id)
+ return Membership::ADD_REJECTED;
+
+ Mutex::Lock _l(_lock);
+ Membership &m = _membership(rev.target());
+
+ const Membership::AddCredentialResult result = m.addCredential(RR,_config,rev);
+
+ if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
+ /* Fast propagation is done by using a very aggressive rumor mill
+ * propagation algorithm. When we see a Revocation that we haven't
+ * seen before we blast it to every known member. This leads to
+ * a huge number of redundant messages, but eventually everybody
+ * will get it. This helps revocation speed and also helps in cases
+ * where the controller is under attack. It need only get one
+ * revocation out and the rest is history. */
+ Address *a = (Address *)0;
+ Membership *m = (Membership *)0;
+ Hashtable<Address,Membership>::Iterator i(_memberships);
+ while (i.next(a,m)) {
+ if ((*a != sentFrom)&&(*a != rev.signer())) {
+ Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
+ outp.append((uint8_t)0x00); // no COM
+ outp.append((uint16_t)0); // no capabilities
+ outp.append((uint16_t)0); // no tags
+ outp.append((uint16_t)1); // one revocation!
+ rev.serialize(outp);
+ RR->sw->send(outp,true);
+ }
+ }
+ }
+
+ return result;
}
void Network::destroy()
diff --git a/node/Network.hpp b/node/Network.hpp
index a151fb88..6a1ac801 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -299,7 +299,7 @@ public:
/**
* Validate a credential and learn it if it passes certificate and other checks
*/
- Membership::AddCredentialResult addCredential(const Revocation &rev);
+ Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
/**
* Force push credentials (COM, etc.) to a peer now
diff --git a/node/Packet.cpp b/node/Packet.cpp
index 9ab68968..3b8e1387 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -40,7 +40,7 @@ const char *Packet::verbString(Verb v)
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
case VERB_NETWORK_CREDENTIALS: return "NETWORK_CREDENTIALS";
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
- case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
+ case VERB_NETWORK_CONFIG: return "NETWORK_CONFIG_REFRESH";
case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 03b9b113..e76cb96c 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -674,8 +674,8 @@ public:
* superset of VERB_FRAME. They're used for bridging or when we
* want to attach a certificate since FRAME does not support that.
*
- * If the ACK flag (0x08) is set, an OK(EXT_FRAME) is sent with
- * no payload to acknowledge receipt of the frame.
+ * OK payload (if ACK flag is set):
+ * <[8] 64-bit network ID>
*/
VERB_EXT_FRAME = 0x07,
@@ -738,9 +738,14 @@ public:
* <[8] 64-bit timestamp of netconf we currently have>
*
* This message requests network configuration from a node capable of
- * providing it. If the optional revision is included, a response is
- * only generated if there is a newer network configuration available.
+ * providing it.
+ *
+ * Respones to this are always whole configs intended for the recipient.
+ * For patches and other updates a NETWORK_CONFIG is sent instead.
*
+ * It would be valid and correct as of 1.2.0 to use NETWORK_CONFIG always,
+ * but OK(NTEWORK_CONFIG_REQUEST) should be sent for compatibility.
+ *
* OK response payload:
* <[8] 64-bit network ID>
* <[2] 16-bit length of network configuration dictionary chunk>
@@ -754,9 +759,10 @@ public:
VERB_NETWORK_CONFIG_REQUEST = 0x0b,
/**
- * Network configuration push:
+ * Network configuration data push:
* <[8] 64-bit network ID>
- * <[8] 64-bit value used to group chunks in this push>
+ * <[8] 64-bit config update ID (token to identify this update)>
+ * <[1] flags>
* <[2] 16-bit length of network configuration dictionary chunk>
* <[...] network configuration dictionary (may be incomplete)>
* <[4] 32-bit total length of assembled dictionary>
@@ -766,8 +772,16 @@ public:
* 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.
+ *
+ * Unlike OK(NETWORK_CONFIG_REQUEST) this can be sent by peers other than
+ * network controllers. In that case the certificate inside the Dictionary
+ * is used for verification purposes.
+ *
+ * Flags:
+ * 0x01 - Patch, not whole config
+ * 0x02 - Use fast P2P propagation
*/
- VERB_NETWORK_CONFIG_REFRESH = 0x0c,
+ VERB_NETWORK_CONFIG = 0x0c,
/**
* Request endpoints for multicast distribution:
diff --git a/node/Revocation.hpp b/node/Revocation.hpp
index 58757465..18916985 100644
--- a/node/Revocation.hpp
+++ b/node/Revocation.hpp
@@ -49,7 +49,7 @@ class Revocation
public:
enum CredentialType
{
- CREDENTIAL_TYPE_NIL = 0,
+ CREDENTIAL_TYPE_ALL = 0,
CREDENTIAL_TYPE_COM = 1,
CREDENTIAL_TYPE_CAPABILITY = 2,
CREDENTIAL_TYPE_TAG = 3