mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-14 19:16:54 +02:00
More work on tags and capabilities.
This commit is contained in:
parent
7e6e56e2bc
commit
f057bb63cd
12 changed files with 397 additions and 54 deletions
52
node/Capability.cpp
Normal file
52
node/Capability.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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 "Capability.hpp"
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "Identity.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
|
||||||
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
int Capability::verify(const RuntimeEnvironment *RR) const
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Buffer<(sizeof(Capability) * 2)> tmp;
|
||||||
|
this->serialize(tmp,true);
|
||||||
|
for(unsigned int c=0;c<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++c) {
|
||||||
|
if (!_custody[c].to)
|
||||||
|
return ((c == 0) ? -1 : 0);
|
||||||
|
if (!_custody[c].from)
|
||||||
|
return -1;
|
||||||
|
const Identity id(RR->topology->getIdentity(_custody[c].from));
|
||||||
|
if (id) {
|
||||||
|
if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
RR->sw->requestWhois(_custody[c].from);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch ( ... ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZeroTier
|
|
@ -130,11 +130,11 @@ public:
|
||||||
inline bool sign(const Identity &from,const Address &to)
|
inline bool sign(const Identity &from,const Address &to)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
Buffer<(sizeof(Capability) * 2)> tmp;
|
|
||||||
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
|
for(unsigned int i=0;((i<_maxCustodyChainLength)&&(i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH));++i) {
|
||||||
if (!(_custody[i].to)) {
|
if (!(_custody[i].to)) {
|
||||||
_custody[i].to = to;
|
_custody[i].to = to;
|
||||||
_custody[i].from = from.address();
|
_custody[i].from = from.address();
|
||||||
|
Buffer<(sizeof(Capability) * 2)> tmp;
|
||||||
this->serialize(tmp,true);
|
this->serialize(tmp,true);
|
||||||
_custody[i].signature = from.sign(tmp.data(),tmp.size());
|
_custody[i].signature = from.sign(tmp.data(),tmp.size());
|
||||||
return true;
|
return true;
|
||||||
|
@ -145,22 +145,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify this capability's chain of custody
|
* Verify this capability's chain of custody and signatures
|
||||||
*
|
|
||||||
* This returns a tri-state result. A return value of zero indicates that
|
|
||||||
* the chain of custody is valid and all signatures are okay. A positive
|
|
||||||
* return value means at least one WHOIS was issued for a missing signing
|
|
||||||
* identity and we should retry later. A negative return value means that
|
|
||||||
* this chain or one of its signature is BAD and this capability should
|
|
||||||
* be discarded.
|
|
||||||
*
|
|
||||||
* Note that the entire chain is checked regardless of verifyInChain.
|
|
||||||
*
|
*
|
||||||
* @param RR Runtime environment to provide for peer lookup, etc.
|
* @param RR Runtime environment to provide for peer lookup, etc.
|
||||||
* @param verifyInChain Also check to ensure that this capability was at some point properly issued to this peer (if non-null)
|
|
||||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
|
||||||
*/
|
*/
|
||||||
int verify(const RuntimeEnvironment *RR,const Address &verifyInChain) const;
|
int verify(const RuntimeEnvironment *RR) const;
|
||||||
|
|
||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
|
static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)
|
||||||
|
@ -403,9 +393,31 @@ public:
|
||||||
return (p - startAt);
|
return (p - startAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check to see if a given address is a 'to' address in the custody chain
|
||||||
|
*
|
||||||
|
* This does not actually do certificate checking. That must be done with verify().
|
||||||
|
*
|
||||||
|
* @param a Address to check
|
||||||
|
* @return True if address is present
|
||||||
|
*/
|
||||||
|
inline bool wasIssuedTo(const Address &a) const
|
||||||
|
{
|
||||||
|
for(unsigned int i=0;i<ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH;++i) {
|
||||||
|
if (!_custody[i].to)
|
||||||
|
break;
|
||||||
|
else if (_custody[i].to == a)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Provides natural sort order by ID
|
// Provides natural sort order by ID
|
||||||
inline bool operator<(const Capability &c) const { return (_id < c._id); }
|
inline bool operator<(const Capability &c) const { return (_id < c._id); }
|
||||||
|
|
||||||
|
inline bool operator==(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) == 0); }
|
||||||
|
inline bool operator!=(const Capability &c) const { return (memcmp(this,&c,sizeof(Capability)) != 0); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _nwid;
|
uint64_t _nwid;
|
||||||
uint64_t _expiration;
|
uint64_t _expiration;
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -182,7 +185,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
|
||||||
|
|
||||||
bool CertificateOfMembership::sign(const Identity &with)
|
bool CertificateOfMembership::sign(const Identity &with)
|
||||||
{
|
{
|
||||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||||
unsigned int ptr = 0;
|
unsigned int ptr = 0;
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||||
|
@ -193,38 +196,32 @@ bool CertificateOfMembership::sign(const Identity &with)
|
||||||
try {
|
try {
|
||||||
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
||||||
_signedBy = with.address();
|
_signedBy = with.address();
|
||||||
delete [] buf;
|
|
||||||
return true;
|
return true;
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
_signedBy.zero();
|
_signedBy.zero();
|
||||||
delete [] buf;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CertificateOfMembership::verify(const Identity &id) const
|
int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
|
||||||
{
|
{
|
||||||
if (!_signedBy)
|
if ((!_signedBy)||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
|
||||||
return false;
|
return -1;
|
||||||
if (id.address() != _signedBy)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||||
|
if (!id) {
|
||||||
|
RR->sw->requestWhois(_signedBy);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||||
unsigned int ptr = 0;
|
unsigned int ptr = 0;
|
||||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||||
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
buf[ptr++] = Utils::hton(_qualifiers[i].maxDelta);
|
||||||
}
|
}
|
||||||
|
return (id.verify(buf,ptr * sizeof(uint64_t),_signature) ? 0 : -1);
|
||||||
bool valid = false;
|
|
||||||
try {
|
|
||||||
valid = id.verify(buf,ptr * sizeof(uint64_t),_signature);
|
|
||||||
delete [] buf;
|
|
||||||
} catch ( ... ) {
|
|
||||||
delete [] buf;
|
|
||||||
}
|
|
||||||
return valid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ZeroTier
|
} // namespace ZeroTier
|
||||||
|
|
|
@ -46,10 +46,12 @@
|
||||||
/**
|
/**
|
||||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||||
*/
|
*/
|
||||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 256
|
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
class RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Certificate of network membership
|
* Certificate of network membership
|
||||||
*
|
*
|
||||||
|
@ -275,12 +277,12 @@ public:
|
||||||
bool sign(const Identity &with);
|
bool sign(const Identity &with);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verify certificate against an identity
|
* Verify this COM and its signature
|
||||||
*
|
*
|
||||||
* @param id Identity to verify against
|
* @param RR Runtime environment for looking up peers
|
||||||
* @return True if certificate is signed by this identity and verification was successful
|
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||||
*/
|
*/
|
||||||
bool verify(const Identity &id) const;
|
int verify(const RuntimeEnvironment *RR) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return True if signed
|
* @return True if signed
|
||||||
|
|
|
@ -443,11 +443,11 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
||||||
|
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
|
|
||||||
if ((flags & 0x01) != 0) {
|
if ((flags & 0x01) != 0) { // deprecated but still used by older peers
|
||||||
// OK(MULTICAST_FRAME) includes certificate of membership update
|
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
|
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
|
||||||
peer->validateAndSetNetworkMembershipCertificate(nwid,com);
|
LockingPtr<Membership> m = peer->membership(com.networkId(),true);
|
||||||
|
if (m) m->addCredential(RR,RR->node->now(),com);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((flags & 0x02) != 0) {
|
if ((flags & 0x02) != 0) {
|
||||||
|
@ -583,10 +583,11 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
|
||||||
const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
|
const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
|
||||||
|
|
||||||
unsigned int comLen = 0;
|
unsigned int comLen = 0;
|
||||||
if ((flags & 0x01) != 0) {
|
if ((flags & 0x01) != 0) { // deprecated but still used by old peers
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
|
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
|
||||||
peer->validateAndSetNetworkMembershipCertificate(network->id(),com);
|
LockingPtr<Membership> m = peer->membership(com.networkId(),true);
|
||||||
|
if (m) m->addCredential(RR,RR->node->now(),com);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!network->isAllowed(peer)) {
|
if (!network->isAllowed(peer)) {
|
||||||
|
@ -698,6 +699,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
|
||||||
bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
const uint64_t now = RR->node->now();
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
Capability cap;
|
Capability cap;
|
||||||
Tag tag;
|
Tag tag;
|
||||||
|
@ -705,7 +707,9 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||||
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
|
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
|
||||||
while ((p < size())&&((*this)[p])) {
|
while ((p < size())&&((*this)[p])) {
|
||||||
p += com.deserialize(*this,p);
|
p += com.deserialize(*this,p);
|
||||||
peer->validateAndSetNetworkMembershipCertificate(com.networkId(),com);
|
LockingPtr<Membership> m = peer->membership(com.networkId(),true);
|
||||||
|
if (!m) return true; // sanity check
|
||||||
|
m->addCredential(RR,now,com);
|
||||||
}
|
}
|
||||||
++p; // skip trailing 0 after COMs if present
|
++p; // skip trailing 0 after COMs if present
|
||||||
|
|
||||||
|
@ -713,10 +717,16 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
|
||||||
const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
|
const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
|
||||||
for(unsigned int i=0;i<numCapabilities;++i) {
|
for(unsigned int i=0;i<numCapabilities;++i) {
|
||||||
p += cap.deserialize(*this,p);
|
p += cap.deserialize(*this,p);
|
||||||
|
LockingPtr<Membership> m = peer->membership(cap.networkId(),true);
|
||||||
|
if (!m) return true; // sanity check
|
||||||
|
m->addCredential(RR,now,cap);
|
||||||
}
|
}
|
||||||
const unsigned int numTags = at<uint16_t>(p); p += 2;
|
const unsigned int numTags = at<uint16_t>(p); p += 2;
|
||||||
for(unsigned int i=0;i<numTags;++i) {
|
for(unsigned int i=0;i<numTags;++i) {
|
||||||
p += tag.deserialize(*this,p);
|
p += tag.deserialize(*this,p);
|
||||||
|
LockingPtr<Membership> m = peer->membership(tag.networkId(),true);
|
||||||
|
if (!m) return true; // sanity check
|
||||||
|
m->addCredential(RR,now,tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -854,10 +864,11 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
|
||||||
// Offset -- size of optional fields added to position of later fields
|
// Offset -- size of optional fields added to position of later fields
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
|
|
||||||
if ((flags & 0x01) != 0) {
|
if ((flags & 0x01) != 0) { // deprecated but still used by older peers
|
||||||
CertificateOfMembership com;
|
CertificateOfMembership com;
|
||||||
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
|
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
|
||||||
peer->validateAndSetNetworkMembershipCertificate(nwid,com);
|
LockingPtr<Membership> m = peer->membership(com.networkId(),true);
|
||||||
|
if (m) m->addCredential(RR,RR->node->now(),com);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check membership after we've read any included COM, since
|
// Check membership after we've read any included COM, since
|
||||||
|
|
99
node/LockingPtr.hpp
Normal file
99
node/LockingPtr.hpp
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/*
|
||||||
|
* 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_LOCKINGPTR_HPP
|
||||||
|
#define ZT_LOCKINGPTR_HPP
|
||||||
|
|
||||||
|
#include "Mutex.hpp"
|
||||||
|
|
||||||
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple pointer that locks and holds a mutex until destroyed
|
||||||
|
*
|
||||||
|
* Care must be taken when using this. It's not very sophisticated and does
|
||||||
|
* not handle being copied except for the simple return use case. When it is
|
||||||
|
* copied it hands off the mutex to the copy and clears it in the original,
|
||||||
|
* meaning that the mutex is unlocked when the last LockingPtr<> in a chain
|
||||||
|
* of such handoffs is destroyed. If this chain of handoffs "forks" (more than
|
||||||
|
* one copy is made) then non-determinism may ensue.
|
||||||
|
*
|
||||||
|
* This does not delete or do anything else with the pointer. It also does not
|
||||||
|
* take care of locking the lock. That must be done beforehand.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class LockingPtr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LockingPtr() :
|
||||||
|
_ptr((T *)0),
|
||||||
|
_lock((Mutex *)0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LockingPtr(T *obj,Mutex *lock) :
|
||||||
|
_ptr(obj),
|
||||||
|
_lock(lock)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LockingPtr(const LockingPtr &p) :
|
||||||
|
_ptr(p._ptr),
|
||||||
|
_lock(p._lock)
|
||||||
|
{
|
||||||
|
const_cast<LockingPtr *>(&p)->_lock = (Mutex *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~LockingPtr()
|
||||||
|
{
|
||||||
|
if (_lock)
|
||||||
|
_lock->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline LockingPtr &operator=(const LockingPtr &p)
|
||||||
|
{
|
||||||
|
_ptr = p._ptr;
|
||||||
|
_lock = p._lock;
|
||||||
|
const_cast<LockingPtr *>(&p)->_lock = (Mutex *)0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline operator bool() const throw() { return (_ptr != (T *)0); }
|
||||||
|
inline T &operator*() const throw() { return *_ptr; }
|
||||||
|
inline T *operator->() const throw() { return _ptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Raw pointer to held object
|
||||||
|
*/
|
||||||
|
inline T *ptr() const throw() { return _ptr; }
|
||||||
|
|
||||||
|
inline bool operator==(const LockingPtr &sp) const throw() { return (_ptr == sp._ptr); }
|
||||||
|
inline bool operator!=(const LockingPtr &sp) const throw() { return (_ptr != sp._ptr); }
|
||||||
|
inline bool operator>(const LockingPtr &sp) const throw() { return (_ptr > sp._ptr); }
|
||||||
|
inline bool operator<(const LockingPtr &sp) const throw() { return (_ptr < sp._ptr); }
|
||||||
|
inline bool operator>=(const LockingPtr &sp) const throw() { return (_ptr >= sp._ptr); }
|
||||||
|
inline bool operator<=(const LockingPtr &sp) const throw() { return (_ptr <= sp._ptr); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *_ptr;
|
||||||
|
Mutex *_lock;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ZeroTier
|
||||||
|
|
||||||
|
#endif
|
|
@ -32,9 +32,16 @@
|
||||||
#include "Hashtable.hpp"
|
#include "Hashtable.hpp"
|
||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
|
|
||||||
|
// Expiration time for capability and tag cache
|
||||||
|
#define ZT_MEMBERSHIP_STATE_EXPIRATION_TIME (ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA * 4)
|
||||||
|
|
||||||
|
// Expiration time for Memberships (used in Peer::clean())
|
||||||
|
#define ZT_MEMBERSHIP_EXPIRATION_TIME (ZT_MEMBERSHIP_STATE_EXPIRATION_TIME * 4)
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class Peer;
|
class Peer;
|
||||||
|
class RuntimeEnvironment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Information related to a peer's participation on a network
|
* Information related to a peer's participation on a network
|
||||||
|
@ -81,15 +88,17 @@ public:
|
||||||
* This checks last pushed times for our COM and for other credentials and
|
* This checks last pushed times for our COM and for other credentials and
|
||||||
* sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
|
* sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
|
||||||
*
|
*
|
||||||
|
* @param RR Runtime environment
|
||||||
|
* @param now Current time
|
||||||
* @param peer Peer that "owns" this membership
|
* @param peer Peer that "owns" this membership
|
||||||
* @param nconf Network configuration
|
* @param nconf Network configuration
|
||||||
* @param now Current time
|
|
||||||
* @param capIds Capability IDs that this peer might need
|
* @param capIds Capability IDs that this peer might need
|
||||||
* @param capCount Number of capability IDs
|
* @param capCount Number of capability IDs
|
||||||
* @param tagIds Tag IDs that this peer might need
|
* @param tagIds Tag IDs that this peer might need
|
||||||
* @param tagCount Number of tag IDs
|
* @param tagCount Number of tag IDs
|
||||||
|
* @return True if we pushed something
|
||||||
*/
|
*/
|
||||||
void sendCredentialsIfNeeded(const Peer &peer,const NetworkConfig &nconf,const uint64_t now,const uint32_t *capIds,const unsigned int capCount,const uint32_t *tagIds,const unsigned int tagCount) const;
|
bool sendCredentialsIfNeeded(const RuntimeEnvironment *RR,const uint64_t now,const Peer &peer,const NetworkConfig &nconf,const uint32_t *capIds,const unsigned int capCount,const uint32_t *tagIds,const unsigned int tagCount) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param nconf Network configuration
|
* @param nconf Network configuration
|
||||||
|
@ -114,25 +123,98 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean up old or stale entries
|
* 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
|
||||||
*/
|
*/
|
||||||
inline void clean(const uint64_t now)
|
inline int addCredential(const RuntimeEnvironment *RR,const uint64_t now,const CertificateOfMembership &com)
|
||||||
{
|
{
|
||||||
|
if (com.issuedTo() != RR->identity.address())
|
||||||
|
return -1;
|
||||||
|
if (_com == com)
|
||||||
|
return 0;
|
||||||
|
const int vr = com.verify(RR);
|
||||||
|
if (vr == 0)
|
||||||
|
_com = com;
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
inline int addCredential(const RuntimeEnvironment *RR,const uint64_t now,const Tag &tag)
|
||||||
|
{
|
||||||
|
if (tag.issuedTo() != RR->identity.address())
|
||||||
|
return -1;
|
||||||
|
TState *t = _tags.get(tag.networkId());
|
||||||
|
if ((t)&&(t->lastReceived != 0)&&(t->tag == tag))
|
||||||
|
return 0;
|
||||||
|
const int vr = tag.verify(RR);
|
||||||
|
if (vr == 0) {
|
||||||
|
if (!t)
|
||||||
|
t = &(_tags[tag.networkId()]);
|
||||||
|
t->lastReceived = now;
|
||||||
|
t->tag = tag;
|
||||||
|
}
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
inline int addCredential(const RuntimeEnvironment *RR,const uint64_t now,const Capability &cap)
|
||||||
|
{
|
||||||
|
if (!cap.wasIssuedTo(RR->identity.address()))
|
||||||
|
return -1;
|
||||||
|
CState *c = _caps.get(cap.networkId());
|
||||||
|
if ((c)&&(c->lastReceived != 0)&&(c->cap == cap))
|
||||||
|
return 0;
|
||||||
|
const int vr = cap.verify(RR);
|
||||||
|
if (vr == 0) {
|
||||||
|
if (!c)
|
||||||
|
c = &(_caps[cap.networkId()]);
|
||||||
|
c->lastReceived = now;
|
||||||
|
c->cap = cap;
|
||||||
|
}
|
||||||
|
return vr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean up old or stale entries
|
||||||
|
*
|
||||||
|
* @return Time of most recent activity in this Membership
|
||||||
|
*/
|
||||||
|
inline uint64_t clean(const uint64_t now)
|
||||||
|
{
|
||||||
|
uint64_t lastAct = _lastPushedCom;
|
||||||
|
|
||||||
uint32_t *i = (uint32_t *)0;
|
uint32_t *i = (uint32_t *)0;
|
||||||
CState *cs = (CState *)0;
|
CState *cs = (CState *)0;
|
||||||
Hashtable<uint32_t,CState>::Iterator csi(_caps);
|
Hashtable<uint32_t,CState>::Iterator csi(_caps);
|
||||||
while (csi.next(i,cs)) {
|
while (csi.next(i,cs)) {
|
||||||
if ((now - std::max(cs->lastPushed,cs->lastReceived)) > (ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA * 3))
|
const uint64_t la = std::max(cs->lastPushed,cs->lastReceived);
|
||||||
|
if ((now - la) > ZT_MEMBERSHIP_STATE_EXPIRATION_TIME)
|
||||||
_caps.erase(*i);
|
_caps.erase(*i);
|
||||||
|
else if (la > lastAct)
|
||||||
|
lastAct = la;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = (uint32_t *)0;
|
i = (uint32_t *)0;
|
||||||
TState *ts = (TState *)0;
|
TState *ts = (TState *)0;
|
||||||
Hashtable<uint32_t,TState>::Iterator tsi(_tags);
|
Hashtable<uint32_t,TState>::Iterator tsi(_tags);
|
||||||
while (tsi.next(i,ts)) {
|
while (tsi.next(i,ts)) {
|
||||||
if ((now - std::max(ts->lastPushed,ts->lastReceived)) > (ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA * 3))
|
const uint64_t la = std::max(ts->lastPushed,ts->lastReceived);
|
||||||
|
if ((now - la) > ZT_MEMBERSHIP_STATE_EXPIRATION_TIME)
|
||||||
_tags.erase(*i);
|
_tags.erase(*i);
|
||||||
|
else if (la > lastAct)
|
||||||
|
lastAct = la;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lastAct;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -40,8 +40,10 @@
|
||||||
#include "SharedPtr.hpp"
|
#include "SharedPtr.hpp"
|
||||||
#include "AtomicCounter.hpp"
|
#include "AtomicCounter.hpp"
|
||||||
#include "Hashtable.hpp"
|
#include "Hashtable.hpp"
|
||||||
|
#include "Membership.hpp"
|
||||||
#include "Mutex.hpp"
|
#include "Mutex.hpp"
|
||||||
#include "NonCopyable.hpp"
|
#include "NonCopyable.hpp"
|
||||||
|
#include "LockingPtr.hpp"
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
@ -384,6 +386,34 @@ public:
|
||||||
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the membership record for this network, possibly creating if missing
|
||||||
|
*
|
||||||
|
* @param networkId Network ID
|
||||||
|
* @param createIfMissing If true, create a Membership record if there isn't one
|
||||||
|
* @return Single-scope locking pointer (see LockingPtr.hpp) to Membership or NULL if not found and createIfMissing is false
|
||||||
|
*/
|
||||||
|
inline LockingPtr<Membership> membership(const uint64_t networkId,bool createIfMissing)
|
||||||
|
{
|
||||||
|
_memberships_m.lock();
|
||||||
|
try {
|
||||||
|
if (createIfMissing) {
|
||||||
|
return LockingPtr<Membership>(&(_memberships[networkId]),&_memberships_m);
|
||||||
|
} else {
|
||||||
|
Membership *m = _memberships.get(networkId);
|
||||||
|
if (m) {
|
||||||
|
return LockingPtr<Membership>(m,&_memberships_m);
|
||||||
|
} else {
|
||||||
|
_memberships_m.unlock();
|
||||||
|
return LockingPtr<Membership>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch ( ... ) {
|
||||||
|
_memberships_m.unlock();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a common set of addresses by which two peers can link, if any
|
* Find a common set of addresses by which two peers can link, if any
|
||||||
*
|
*
|
||||||
|
@ -430,6 +460,9 @@ private:
|
||||||
unsigned int _latency;
|
unsigned int _latency;
|
||||||
unsigned int _directPathPushCutoffCount;
|
unsigned int _directPathPushCutoffCount;
|
||||||
|
|
||||||
|
Hashtable<uint64_t,Membership> _memberships;
|
||||||
|
Mutex _memberships_m;
|
||||||
|
|
||||||
AtomicCounter __refCount;
|
AtomicCounter __refCount;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
45
node/Tag.cpp
Normal file
45
node/Tag.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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 "Tag.hpp"
|
||||||
|
#include "RuntimeEnvironment.hpp"
|
||||||
|
#include "Identity.hpp"
|
||||||
|
#include "Topology.hpp"
|
||||||
|
#include "Switch.hpp"
|
||||||
|
|
||||||
|
namespace ZeroTier {
|
||||||
|
|
||||||
|
int Tag::verify(const RuntimeEnvironment *RR) const
|
||||||
|
{
|
||||||
|
if (!_signedBy)
|
||||||
|
return -1;
|
||||||
|
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||||
|
if (!id) {
|
||||||
|
RR->sw->requestWhois(_signedBy);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Buffer<(sizeof(Tag) * 2)> tmp;
|
||||||
|
this->serialize(tmp,true);
|
||||||
|
return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
|
||||||
|
} catch ( ... ) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ZeroTier
|
11
node/Tag.hpp
11
node/Tag.hpp
|
@ -76,7 +76,6 @@ public:
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline uint64_t networkId() const { return _nwid; }
|
inline uint64_t networkId() const { return _nwid; }
|
||||||
inline uint64_t expiration() const { return _expiration; }
|
inline uint64_t expiration() const { return _expiration; }
|
||||||
inline uint32_t id() const { return _id; }
|
inline uint32_t id() const { return _id; }
|
||||||
|
@ -106,9 +105,9 @@ public:
|
||||||
* Check this tag's signature
|
* Check this tag's signature
|
||||||
*
|
*
|
||||||
* @param RR Runtime environment to allow identity lookup for signedBy
|
* @param RR Runtime environment to allow identity lookup for signedBy
|
||||||
* @return True if signature is present and valid
|
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
|
||||||
*/
|
*/
|
||||||
bool verify(const RuntimeEnvironment *RR);
|
int verify(const RuntimeEnvironment *RR) const;
|
||||||
|
|
||||||
template<unsigned int C>
|
template<unsigned int C>
|
||||||
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
inline void serialize(Buffer<C> &b,const bool forSign = false) const
|
||||||
|
@ -156,6 +155,12 @@ public:
|
||||||
return (p - startAt);
|
return (p - startAt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Provides natural sort order by ID
|
||||||
|
inline bool operator<(const Tag &t) const { return (_id < t._id); }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t _nwid;
|
uint64_t _nwid;
|
||||||
uint64_t _expiration;
|
uint64_t _expiration;
|
||||||
|
|
|
@ -169,7 +169,9 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
||||||
|
|
||||||
Identity Topology::getIdentity(const Address &zta)
|
Identity Topology::getIdentity(const Address &zta)
|
||||||
{
|
{
|
||||||
{
|
if (zta == RR->identity.address()) {
|
||||||
|
return RR->identity;
|
||||||
|
} else {
|
||||||
Mutex::Lock _l(_lock);
|
Mutex::Lock _l(_lock);
|
||||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||||
if (ap)
|
if (ap)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
OBJS=\
|
OBJS=\
|
||||||
node/C25519.o \
|
node/C25519.o \
|
||||||
|
node/Capability.o \
|
||||||
node/CertificateOfMembership.o \
|
node/CertificateOfMembership.o \
|
||||||
node/Cluster.o \
|
node/Cluster.o \
|
||||||
node/DeferredPackets.o \
|
node/DeferredPackets.o \
|
||||||
|
@ -7,6 +8,7 @@ OBJS=\
|
||||||
node/Identity.o \
|
node/Identity.o \
|
||||||
node/IncomingPacket.o \
|
node/IncomingPacket.o \
|
||||||
node/InetAddress.o \
|
node/InetAddress.o \
|
||||||
|
node/Membership.o \
|
||||||
node/Multicaster.o \
|
node/Multicaster.o \
|
||||||
node/Network.o \
|
node/Network.o \
|
||||||
node/NetworkConfig.o \
|
node/NetworkConfig.o \
|
||||||
|
@ -20,6 +22,7 @@ OBJS=\
|
||||||
node/SelfAwareness.o \
|
node/SelfAwareness.o \
|
||||||
node/SHA512.o \
|
node/SHA512.o \
|
||||||
node/Switch.o \
|
node/Switch.o \
|
||||||
|
node/Tag.o \
|
||||||
node/Topology.o \
|
node/Topology.o \
|
||||||
node/Utils.o \
|
node/Utils.o \
|
||||||
osdep/BackgroundResolver.o \
|
osdep/BackgroundResolver.o \
|
||||||
|
|
Loading…
Add table
Reference in a new issue