updated included zerotierone src
This commit is contained in:
@@ -20,9 +20,11 @@
|
||||
#define ZT_ATOMICCOUNTER_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
#ifndef __GNUC__
|
||||
#ifdef __WINDOWS__
|
||||
// <atomic> will replace this whole class eventually once it's ubiquitous
|
||||
#include <atomic>
|
||||
#endif
|
||||
|
||||
@@ -34,34 +36,75 @@ namespace ZeroTier {
|
||||
class AtomicCounter : NonCopyable
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Initialize counter at zero
|
||||
*/
|
||||
AtomicCounter()
|
||||
throw()
|
||||
{
|
||||
_v = 0;
|
||||
}
|
||||
|
||||
inline operator int() const
|
||||
throw()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_or_and_fetch(const_cast <volatile int *>(&_v),0);
|
||||
#else
|
||||
#ifdef __WINDOWS__
|
||||
return (int)_v;
|
||||
#else
|
||||
_l.lock();
|
||||
int v = _v;
|
||||
_l.unlock();
|
||||
return v;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int operator++()
|
||||
throw()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_add_and_fetch(&_v,1);
|
||||
#else
|
||||
#ifdef __WINDOWS__
|
||||
return ++_v;
|
||||
#else
|
||||
_l.lock();
|
||||
int v = ++_v;
|
||||
_l.unlock();
|
||||
return v;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int operator--()
|
||||
throw()
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
return __sync_sub_and_fetch(&_v,1);
|
||||
#else
|
||||
#ifdef __WINDOWS__
|
||||
return --_v;
|
||||
#else
|
||||
_l.lock();
|
||||
int v = --_v;
|
||||
_l.unlock();
|
||||
return v;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
#ifdef __GNUC__
|
||||
int _v;
|
||||
#else
|
||||
#ifdef __WINDOWS__
|
||||
std::atomic_int _v;
|
||||
#else
|
||||
int _v;
|
||||
#ifndef __GNUC__
|
||||
#warning Neither __WINDOWS__ nor __GNUC__ so AtomicCounter using Mutex
|
||||
Mutex _l;
|
||||
#endif
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
@@ -17,10 +17,6 @@
|
||||
*/
|
||||
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "Topology.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
@@ -156,9 +152,6 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
|
||||
unsigned int myidx = 0;
|
||||
unsigned int otheridx = 0;
|
||||
|
||||
if ((_qualifierCount == 0)||(other._qualifierCount == 0))
|
||||
return false;
|
||||
|
||||
while (myidx < _qualifierCount) {
|
||||
// Fail if we're at the end of other, since this means the field is
|
||||
// missing.
|
||||
@@ -189,7 +182,7 @@ bool CertificateOfMembership::agreesWith(const CertificateOfMembership &other) c
|
||||
|
||||
bool CertificateOfMembership::sign(const Identity &with)
|
||||
{
|
||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
||||
unsigned int ptr = 0;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||
@@ -200,32 +193,38 @@ bool CertificateOfMembership::sign(const Identity &with)
|
||||
try {
|
||||
_signature = with.sign(buf,ptr * sizeof(uint64_t));
|
||||
_signedBy = with.address();
|
||||
delete [] buf;
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
_signedBy.zero();
|
||||
delete [] buf;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
|
||||
bool CertificateOfMembership::verify(const Identity &id) const
|
||||
{
|
||||
if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
|
||||
return -1;
|
||||
if (!_signedBy)
|
||||
return false;
|
||||
if (id.address() != _signedBy)
|
||||
return false;
|
||||
|
||||
const Identity id(RR->topology->getIdentity(_signedBy));
|
||||
if (!id) {
|
||||
RR->sw->requestWhois(_signedBy);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint64_t buf[ZT_NETWORK_COM_MAX_QUALIFIERS * 3];
|
||||
uint64_t *const buf = new uint64_t[_qualifierCount * 3];
|
||||
unsigned int ptr = 0;
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].id);
|
||||
buf[ptr++] = Utils::hton(_qualifiers[i].value);
|
||||
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
|
||||
|
||||
@@ -34,14 +34,22 @@
|
||||
#include "Utils.hpp"
|
||||
|
||||
/**
|
||||
* Maximum number of qualifiers allowed in a COM (absolute max: 65535)
|
||||
* Default window of time for certificate agreement
|
||||
*
|
||||
* Right now we use time for 'revision' so this is the maximum time divergence
|
||||
* between two certs for them to agree. It comes out to five minutes, which
|
||||
* gives a lot of margin for error if the controller hiccups or its clock
|
||||
* drifts but causes de-authorized peers to fall off fast enough.
|
||||
*/
|
||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 8
|
||||
#define ZT_NETWORK_COM_DEFAULT_REVISION_MAX_DELTA (ZT_NETWORK_AUTOCONF_DELAY * 5)
|
||||
|
||||
/**
|
||||
* Maximum number of qualifiers in a COM
|
||||
*/
|
||||
#define ZT_NETWORK_COM_MAX_QUALIFIERS 16
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* Certificate of network membership
|
||||
*
|
||||
@@ -71,11 +79,22 @@ class RuntimeEnvironment;
|
||||
class CertificateOfMembership
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Certificate type codes, used in serialization
|
||||
*
|
||||
* Only one so far, and only one hopefully there shall be for quite some
|
||||
* time.
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
COM_UINT64_ED25519 = 1 // tuples of unsigned 64's signed with Ed25519
|
||||
};
|
||||
|
||||
/**
|
||||
* Reserved qualifier IDs
|
||||
*
|
||||
* IDs below 1024 are reserved for use as standard IDs. Others are available
|
||||
* for user-defined use.
|
||||
* IDs below 65536 should be considered reserved for future global
|
||||
* assignment here.
|
||||
*
|
||||
* Addition of new required fields requires that code in hasRequiredFields
|
||||
* be updated as well.
|
||||
@@ -83,27 +102,36 @@ public:
|
||||
enum ReservedId
|
||||
{
|
||||
/**
|
||||
* Timestamp of certificate
|
||||
* Revision number of certificate
|
||||
*
|
||||
* Certificates may differ in revision number by a designated max
|
||||
* delta. Differences wider than this cause certificates not to agree.
|
||||
*/
|
||||
COM_RESERVED_ID_TIMESTAMP = 0,
|
||||
COM_RESERVED_ID_REVISION = 0,
|
||||
|
||||
/**
|
||||
* Network ID for which certificate was issued
|
||||
*
|
||||
* maxDelta here is zero, since this must match.
|
||||
*/
|
||||
COM_RESERVED_ID_NETWORK_ID = 1,
|
||||
|
||||
/**
|
||||
* ZeroTier address to whom certificate was issued
|
||||
*
|
||||
* maxDelta will be 0xffffffffffffffff here since it's permitted to differ
|
||||
* from peers obviously.
|
||||
*/
|
||||
COM_RESERVED_ID_ISSUED_TO = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an empty certificate of membership
|
||||
* Create an empty certificate
|
||||
*/
|
||||
CertificateOfMembership()
|
||||
CertificateOfMembership() :
|
||||
_qualifierCount(0)
|
||||
{
|
||||
memset(this,0,sizeof(CertificateOfMembership));
|
||||
memset(_signature.data,0,_signature.size());
|
||||
}
|
||||
|
||||
CertificateOfMembership(const CertificateOfMembership &c)
|
||||
@@ -114,16 +142,16 @@ public:
|
||||
/**
|
||||
* Create from required fields common to all networks
|
||||
*
|
||||
* @param timestamp Timestamp of certificate
|
||||
* @param revision Revision number of certificate
|
||||
* @param timestampMaxDelta Maximum variation between timestamps on this net
|
||||
* @param nwid Network ID
|
||||
* @param issuedTo Certificate recipient
|
||||
*/
|
||||
CertificateOfMembership(uint64_t timestamp,uint64_t timestampMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||
CertificateOfMembership(uint64_t revision,uint64_t revisionMaxDelta,uint64_t nwid,const Address &issuedTo)
|
||||
{
|
||||
_qualifiers[0].id = COM_RESERVED_ID_TIMESTAMP;
|
||||
_qualifiers[0].value = timestamp;
|
||||
_qualifiers[0].maxDelta = timestampMaxDelta;
|
||||
_qualifiers[0].id = COM_RESERVED_ID_REVISION;
|
||||
_qualifiers[0].value = revision;
|
||||
_qualifiers[0].maxDelta = revisionMaxDelta;
|
||||
_qualifiers[1].id = COM_RESERVED_ID_NETWORK_ID;
|
||||
_qualifiers[1].value = nwid;
|
||||
_qualifiers[1].maxDelta = 0;
|
||||
@@ -140,6 +168,22 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
/**
|
||||
* Create from string-serialized data
|
||||
*
|
||||
* @param s String-serialized COM
|
||||
*/
|
||||
CertificateOfMembership(const char *s) { fromString(s); }
|
||||
|
||||
/**
|
||||
* Create from string-serialized data
|
||||
*
|
||||
* @param s String-serialized COM
|
||||
*/
|
||||
CertificateOfMembership(const std::string &s) { fromString(s.c_str()); }
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
/**
|
||||
* Create from binary-serialized COM in buffer
|
||||
*
|
||||
@@ -158,15 +202,45 @@ public:
|
||||
inline operator bool() const throw() { return (_qualifierCount != 0); }
|
||||
|
||||
/**
|
||||
* @return Timestamp for this cert and maximum delta for timestamp
|
||||
* Check for presence of all required fields common to all networks
|
||||
*
|
||||
* @return True if all required fields are present
|
||||
*/
|
||||
inline std::pair<uint64_t,uint64_t> timestamp() const
|
||||
inline bool hasRequiredFields() const
|
||||
{
|
||||
if (_qualifierCount < 3)
|
||||
return false;
|
||||
if (_qualifiers[0].id != COM_RESERVED_ID_REVISION)
|
||||
return false;
|
||||
if (_qualifiers[1].id != COM_RESERVED_ID_NETWORK_ID)
|
||||
return false;
|
||||
if (_qualifiers[2].id != COM_RESERVED_ID_ISSUED_TO)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Maximum delta for mandatory revision field or 0 if field missing
|
||||
*/
|
||||
inline uint64_t revisionMaxDelta() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_TIMESTAMP)
|
||||
return std::pair<uint64_t,uint64_t>(_qualifiers[i].value,_qualifiers[i].maxDelta);
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
|
||||
return _qualifiers[i].maxDelta;
|
||||
}
|
||||
return std::pair<uint64_t,uint64_t>(0ULL,0ULL);
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Revision number for this cert
|
||||
*/
|
||||
inline uint64_t revision() const
|
||||
{
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
if (_qualifiers[i].id == COM_RESERVED_ID_REVISION)
|
||||
return _qualifiers[i].value;
|
||||
}
|
||||
return 0ULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -221,6 +295,7 @@ public:
|
||||
* @param s String to deserialize
|
||||
*/
|
||||
void fromString(const char *s);
|
||||
inline void fromString(const std::string &s) { fromString(s.c_str()); }
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
/**
|
||||
@@ -247,12 +322,12 @@ public:
|
||||
bool sign(const Identity &with);
|
||||
|
||||
/**
|
||||
* Verify this COM and its signature
|
||||
* Verify certificate against an identity
|
||||
*
|
||||
* @param RR Runtime environment for looking up peers
|
||||
* @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
|
||||
* @param id Identity to verify against
|
||||
* @return True if certificate is signed by this identity and verification was successful
|
||||
*/
|
||||
int verify(const RuntimeEnvironment *RR) const;
|
||||
bool verify(const Identity &id) const;
|
||||
|
||||
/**
|
||||
* @return True if signed
|
||||
@@ -267,7 +342,7 @@ public:
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
b.append((uint8_t)1);
|
||||
b.append((unsigned char)COM_UINT64_ED25519);
|
||||
b.append((uint16_t)_qualifierCount);
|
||||
for(unsigned int i=0;i<_qualifierCount;++i) {
|
||||
b.append(_qualifiers[i].id);
|
||||
@@ -287,8 +362,8 @@ public:
|
||||
_qualifierCount = 0;
|
||||
_signedBy.zero();
|
||||
|
||||
if (b[p++] != 1)
|
||||
throw std::invalid_argument("invalid object");
|
||||
if (b[p++] != COM_UINT64_ED25519)
|
||||
throw std::invalid_argument("invalid type");
|
||||
|
||||
unsigned int numq = b.template at<uint16_t>(p); p += sizeof(uint16_t);
|
||||
uint64_t lastId = 0;
|
||||
|
||||
@@ -361,7 +361,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
||||
case CLUSTER_MESSAGE_WANT_PEER: {
|
||||
const Address zeroTierAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
|
||||
SharedPtr<Peer> peer(RR->topology->getPeerNoCache(zeroTierAddress));
|
||||
if ( (peer) && (peer->hasLocalClusterOptimalPath(RR->node->now())) ) {
|
||||
if ( (peer) && (peer->hasClusterOptimalPath(RR->node->now())) ) {
|
||||
Buffer<1024> buf;
|
||||
peer->identity().serialize(buf);
|
||||
Mutex::Lock _l2(_members[fromMemberId].lock);
|
||||
@@ -455,7 +455,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
||||
Mutex::Lock _l2(_members[fromMemberId].lock);
|
||||
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
|
||||
}
|
||||
RR->sw->send(rendezvousForLocal,true);
|
||||
RR->sw->send(rendezvousForLocal,true,0);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
@@ -466,7 +466,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
|
||||
const unsigned int len = dmsg.at<uint16_t>(ptr); ptr += 2;
|
||||
Packet outp(rcpt,RR->identity.address(),verb);
|
||||
outp.append(dmsg.field(ptr,len),len); ptr += len;
|
||||
RR->sw->send(outp,true);
|
||||
RR->sw->send(outp,true,0);
|
||||
//TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
|
||||
} break;
|
||||
}
|
||||
@@ -719,9 +719,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
|
||||
std::vector<InetAddress> best;
|
||||
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
|
||||
double bestDistance = (offload ? 2147483648.0 : currentDistance);
|
||||
#ifdef ZT_TRACE
|
||||
unsigned int bestMember = _id;
|
||||
#endif
|
||||
{
|
||||
Mutex::Lock _l(_memberIds_m);
|
||||
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
|
||||
@@ -733,9 +731,7 @@ bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddr
|
||||
const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
|
||||
if (mdist < bestDistance) {
|
||||
bestDistance = mdist;
|
||||
#ifdef ZT_TRACE
|
||||
bestMember = *mid;
|
||||
#endif
|
||||
best = m.zeroTierPhysicalEndpoints;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,16 +179,16 @@
|
||||
*/
|
||||
#define ZT_PEER_SECRET_KEY_LENGTH 32
|
||||
|
||||
/**
|
||||
* Minimum delay between timer task checks to prevent thrashing
|
||||
*/
|
||||
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
|
||||
|
||||
/**
|
||||
* How often Topology::clean() and Network::clean() and similar are called, in ms
|
||||
*/
|
||||
#define ZT_HOUSEKEEPING_PERIOD 120000
|
||||
|
||||
/**
|
||||
* Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet
|
||||
*/
|
||||
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
|
||||
|
||||
/**
|
||||
* How long to remember peer records in RAM if they haven't been used
|
||||
*/
|
||||
@@ -226,21 +226,11 @@
|
||||
*/
|
||||
#define ZT_MULTICAST_LIKE_EXPIRE 600000
|
||||
|
||||
/**
|
||||
* Period for multicast LIKE announcements
|
||||
*/
|
||||
#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000
|
||||
|
||||
/**
|
||||
* Delay between explicit MULTICAST_GATHER requests for a given multicast channel
|
||||
*/
|
||||
#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10)
|
||||
|
||||
/**
|
||||
* Expiration for credentials presented for MULTICAST_LIKE or MULTICAST_GATHER (for non-network-members)
|
||||
*/
|
||||
#define ZT_MULTICAST_CREDENTIAL_EXPIRATON ZT_MULTICAST_LIKE_EXPIRE
|
||||
|
||||
/**
|
||||
* Timeout for outgoing multicasts
|
||||
*
|
||||
@@ -249,44 +239,30 @@
|
||||
#define ZT_MULTICAST_TRANSMIT_TIMEOUT 5000
|
||||
|
||||
/**
|
||||
* Delay between checks of peer pings, etc., and also related housekeeping tasks
|
||||
* Default maximum number of peers to address with a single multicast (if unspecified in network config)
|
||||
*/
|
||||
#define ZT_PING_CHECK_INVERVAL 5000
|
||||
#define ZT_MULTICAST_DEFAULT_LIMIT 32
|
||||
|
||||
/**
|
||||
* How frequently to send heartbeats over in-use paths
|
||||
* How frequently to send a zero-byte UDP keepalive packet
|
||||
*
|
||||
* There are NATs with timeouts as short as 20 seconds, so this turns out
|
||||
* to be needed.
|
||||
*/
|
||||
#define ZT_PATH_HEARTBEAT_PERIOD 10000
|
||||
#define ZT_NAT_KEEPALIVE_DELAY 19000
|
||||
|
||||
/**
|
||||
* Paths are considered inactive if they have not received traffic in this long
|
||||
* Delay between scans of the topology active peer DB for peers that need ping
|
||||
*
|
||||
* This is also how often pings will be retried to upstream peers (relays, roots)
|
||||
* constantly until something is heard.
|
||||
*/
|
||||
#define ZT_PATH_ALIVE_TIMEOUT 25000
|
||||
#define ZT_PING_CHECK_INVERVAL 9500
|
||||
|
||||
/**
|
||||
* Minimum time between attempts to check dead paths to see if they can be re-awakened
|
||||
* Delay between ordinary case pings of direct links
|
||||
*/
|
||||
#define ZT_PATH_MIN_REACTIVATE_INTERVAL 2500
|
||||
|
||||
/**
|
||||
* Do not accept HELLOs over a given path more often than this
|
||||
*/
|
||||
#define ZT_PATH_HELLO_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* Delay between full-fledge pings of directly connected peers
|
||||
*/
|
||||
#define ZT_PEER_PING_PERIOD 60000
|
||||
|
||||
/**
|
||||
* Paths are considered expired if they have not produced a real packet in this long
|
||||
*/
|
||||
#define ZT_PEER_PATH_EXPIRATION ((ZT_PEER_PING_PERIOD * 4) + 3000)
|
||||
|
||||
/**
|
||||
* How often to retry expired paths that we're still remembering
|
||||
*/
|
||||
#define ZT_PEER_EXPIRED_PATH_TRIAL_PERIOD (ZT_PEER_PING_PERIOD * 10)
|
||||
#define ZT_PEER_DIRECT_PING_DELAY 60000
|
||||
|
||||
/**
|
||||
* Timeout for overall peer activity (measured from last receive)
|
||||
@@ -294,14 +270,19 @@
|
||||
#define ZT_PEER_ACTIVITY_TIMEOUT 500000
|
||||
|
||||
/**
|
||||
* General rate limit timeout for multiple packet types (HELLO, etc.)
|
||||
* Timeout for path activity
|
||||
*/
|
||||
#define ZT_PEER_GENERAL_INBOUND_RATE_LIMIT 500
|
||||
#define ZT_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
|
||||
|
||||
/**
|
||||
* General limit for max RTT for requests over the network
|
||||
* No answer timeout to trigger dead path detection
|
||||
*/
|
||||
#define ZT_GENERAL_RTT_LIMIT 5000
|
||||
#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2000
|
||||
|
||||
/**
|
||||
* Probation threshold after which a path becomes dead
|
||||
*/
|
||||
#define ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION 3
|
||||
|
||||
/**
|
||||
* Delay between requests for updated network autoconf information
|
||||
@@ -320,6 +301,21 @@
|
||||
*/
|
||||
#define ZT_MIN_UNITE_INTERVAL 30000
|
||||
|
||||
/**
|
||||
* Delay between initial direct NAT-t packet and more aggressive techniques
|
||||
*
|
||||
* This may also be a delay before sending the first packet if we determine
|
||||
* that we should wait for the remote to initiate rendezvous first.
|
||||
*/
|
||||
#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000
|
||||
|
||||
/**
|
||||
* How long (max) to remember network certificates of membership?
|
||||
*
|
||||
* This only applies to networks we don't belong to.
|
||||
*/
|
||||
#define ZT_PEER_NETWORK_COM_EXPIRATION 3600000
|
||||
|
||||
/**
|
||||
* Sanity limit on maximum bridge routes
|
||||
*
|
||||
@@ -334,7 +330,7 @@
|
||||
/**
|
||||
* If there is no known route, spam to up to this many active bridges
|
||||
*/
|
||||
#define ZT_MAX_BRIDGE_SPAM 32
|
||||
#define ZT_MAX_BRIDGE_SPAM 16
|
||||
|
||||
/**
|
||||
* Interval between direct path pushes in milliseconds
|
||||
@@ -361,30 +357,23 @@
|
||||
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 4
|
||||
|
||||
/**
|
||||
* Time horizon for VERB_NETWORK_CREDENTIALS cutoff
|
||||
*/
|
||||
#define ZT_PEER_CREDENTIALS_CUTOFF_TIME 60000
|
||||
|
||||
/**
|
||||
* Maximum number of VERB_NETWORK_CREDENTIALS within cutoff time
|
||||
*/
|
||||
#define ZT_PEER_CREDEITIALS_CUTOFF_LIMIT 15
|
||||
|
||||
/**
|
||||
* General rate limit for other kinds of rate-limited packets (HELLO, credential request, etc.) both inbound and outbound
|
||||
*/
|
||||
#define ZT_PEER_GENERAL_RATE_LIMIT 1000
|
||||
|
||||
/**
|
||||
* How long is a path or peer considered to have a trust relationship with us (for e.g. relay policy) since last trusted established packet?
|
||||
*/
|
||||
#define ZT_TRUST_EXPIRATION 600000
|
||||
|
||||
/**
|
||||
* Enable support for older network configurations from older (pre-1.1.6) controllers
|
||||
* Enable support for old Dictionary based network configs
|
||||
*/
|
||||
#define ZT_SUPPORT_OLD_STYLE_NETCONF 1
|
||||
|
||||
/**
|
||||
* A test pseudo-network-ID that can be joined
|
||||
*
|
||||
* Joining this network ID will result in a network with no IP addressing
|
||||
* and default parameters. No network configuration master will be consulted
|
||||
* and instead a static config will be used. This is used in built-in testnet
|
||||
* scenarios and can also be used for external testing.
|
||||
*
|
||||
* This is an impossible real network ID since 0xff is a reserved address
|
||||
* prefix.
|
||||
*/
|
||||
#define ZT_TEST_NETWORK_ID 0xffffffffffffffffULL
|
||||
|
||||
/**
|
||||
* Desired buffer size for UDP sockets (used in service and osdep but defined here)
|
||||
*/
|
||||
|
||||
@@ -20,441 +20,261 @@
|
||||
#define ZT_DICTIONARY_HPP
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
#include "Address.hpp"
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
// Three fields are added/updated by sign()
|
||||
#define ZT_DICTIONARY_SIGNATURE "~!ed25519"
|
||||
#define ZT_DICTIONARY_SIGNATURE_IDENTITY "~!sigid"
|
||||
#define ZT_DICTIONARY_SIGNATURE_TIMESTAMP "~!sigts"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class Identity;
|
||||
|
||||
/**
|
||||
* A small (in code and data) packed key=value store
|
||||
* Simple key/value dictionary with string serialization
|
||||
*
|
||||
* This stores data in the form of a compact blob that is sort of human
|
||||
* readable (depending on whether you put binary data in it) and is backward
|
||||
* compatible with older versions. Binary data is escaped such that the
|
||||
* serialized form of a Dictionary is always a valid null-terminated C string.
|
||||
* The serialization format is a flat key=value with backslash escape.
|
||||
* It does not support comments or other syntactic complexities. It is
|
||||
* human-readable if the keys and values in the dictionary are also
|
||||
* human-readable. Otherwise it might contain unprintable characters.
|
||||
*
|
||||
* Keys are restricted: no binary data, no CR/LF, and no equals (=). If a key
|
||||
* contains these characters it may not be retrievable. This is not checked.
|
||||
* Keys beginning with "~!" are reserved for signature data fields.
|
||||
*
|
||||
* Lookup is via linear search and will be slow with a lot of keys. It's
|
||||
* designed for small things.
|
||||
*
|
||||
* There is code to test and fuzz this in selftest.cpp. Fuzzing a blob of
|
||||
* pointer tricks like this is important after any modifications.
|
||||
*
|
||||
* This is used for network configurations and for saving some things on disk
|
||||
* in the ZeroTier One service code.
|
||||
*
|
||||
* @tparam C Dictionary max capacity in bytes
|
||||
* It's stored as a simple vector and can be linearly scanned or
|
||||
* binary searched. Dictionaries are only used for very small things
|
||||
* outside the core loop, so this is not a significant performance
|
||||
* issue and it reduces memory use and code footprint.
|
||||
*/
|
||||
template<unsigned int C>
|
||||
class Dictionary
|
||||
class Dictionary : public std::vector< std::pair<std::string,std::string> >
|
||||
{
|
||||
public:
|
||||
Dictionary()
|
||||
{
|
||||
_d[0] = (char)0;
|
||||
}
|
||||
|
||||
Dictionary(const char *s)
|
||||
{
|
||||
Utils::scopy(_d,sizeof(_d),s);
|
||||
}
|
||||
|
||||
Dictionary(const char *s,unsigned int len)
|
||||
{
|
||||
if (len > (C-1))
|
||||
len = C-1;
|
||||
memcpy(_d,s,len);
|
||||
_d[len] = (char)0;
|
||||
}
|
||||
|
||||
Dictionary(const Dictionary &d)
|
||||
{
|
||||
Utils::scopy(_d,sizeof(_d),d._d);
|
||||
}
|
||||
|
||||
inline Dictionary &operator=(const Dictionary &d)
|
||||
{
|
||||
Utils::scopy(_d,sizeof(_d),d._d);
|
||||
return *this;
|
||||
}
|
||||
Dictionary() {}
|
||||
|
||||
/**
|
||||
* Load a dictionary from a C-string
|
||||
*
|
||||
* @param s Dictionary in string form
|
||||
* @return False if 's' was longer than our capacity
|
||||
* @param s String-serialized dictionary
|
||||
* @param maxlen Maximum length of buffer
|
||||
*/
|
||||
inline bool load(const char *s)
|
||||
{
|
||||
return Utils::scopy(_d,sizeof(_d),s);
|
||||
}
|
||||
Dictionary(const char *s,unsigned int maxlen) { fromString(s,maxlen); }
|
||||
|
||||
/**
|
||||
* Delete all entries
|
||||
* @param s String-serialized dictionary
|
||||
*/
|
||||
inline void clear()
|
||||
{
|
||||
_d[0] = (char)0;
|
||||
}
|
||||
Dictionary(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
|
||||
|
||||
iterator find(const std::string &key);
|
||||
const_iterator find(const std::string &key) const;
|
||||
|
||||
/**
|
||||
* @return Size of dictionary in bytes not including terminating NULL
|
||||
*/
|
||||
inline unsigned int sizeBytes() const
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i])
|
||||
return i;
|
||||
}
|
||||
return C-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an entry
|
||||
*
|
||||
* Note that to get binary values, dest[] should be at least one more than
|
||||
* the maximum size of the value being retrieved. That's because even if
|
||||
* the data is binary a terminating 0 is still appended to dest[] after it.
|
||||
*
|
||||
* If the key is not found, dest[0] is set to 0 to make dest[] an empty
|
||||
* C string in that case. The dest[] array will *never* be unterminated
|
||||
* after this call.
|
||||
*
|
||||
* Security note: if 'key' is ever directly based on anything that is not
|
||||
* a hard-code or internally-generated name, it must be checked to ensure
|
||||
* that the buffer is NULL-terminated since key[] does not take a secondary
|
||||
* size parameter. In NetworkConfig all keys are hard-coded strings so this
|
||||
* isn't a problem in the core.
|
||||
* Get a key, returning a default if not present
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dest Destination buffer
|
||||
* @param destlen Size of destination buffer
|
||||
* @return -1 if not found, or actual number of bytes stored in dest[] minus trailing 0
|
||||
* @param dfl Default if not present
|
||||
* @return Value or default
|
||||
*/
|
||||
inline int get(const char *key,char *dest,unsigned int destlen) const
|
||||
inline const std::string &get(const std::string &key,const std::string &dfl) const
|
||||
{
|
||||
const char *p = _d;
|
||||
const char *const eof = p + C;
|
||||
const char *k;
|
||||
bool esc;
|
||||
int j;
|
||||
|
||||
if (!destlen) // sanity check
|
||||
return -1;
|
||||
|
||||
while (*p) {
|
||||
k = key;
|
||||
while ((*k)&&(*p)) {
|
||||
if (*p != *k)
|
||||
break;
|
||||
++k;
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((!*k)&&(*p == '=')) {
|
||||
j = 0;
|
||||
esc = false;
|
||||
++p;
|
||||
while ((*p != 0)&&(*p != '\r')&&(*p != '\n')) {
|
||||
if (esc) {
|
||||
esc = false;
|
||||
switch(*p) {
|
||||
case 'r': dest[j++] = '\r'; break;
|
||||
case 'n': dest[j++] = '\n'; break;
|
||||
case '0': dest[j++] = (char)0; break;
|
||||
case 'e': dest[j++] = '='; break;
|
||||
default: dest[j++] = *p; break;
|
||||
}
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
} else if (*p == '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
dest[j++] = *p;
|
||||
if (j == (int)destlen) {
|
||||
dest[j-1] = (char)0;
|
||||
return j-1;
|
||||
}
|
||||
}
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
dest[j] = (char)0;
|
||||
return j;
|
||||
} else {
|
||||
while ((*p)&&(*p != '\r')&&(*p != '\n')) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (*p) {
|
||||
if (++p == eof) {
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else break;
|
||||
}
|
||||
}
|
||||
|
||||
dest[0] = (char)0;
|
||||
return -1;
|
||||
const_iterator e(find(key));
|
||||
if (e == end())
|
||||
return dfl;
|
||||
return e->second;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of a key into a buffer
|
||||
*
|
||||
* @param key Key to get
|
||||
* @param dest Destination buffer
|
||||
* @return True if key was found (if false, dest will be empty)
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
* @param dfl Default boolean result if key not found or empty (default: false)
|
||||
* @return Boolean value of key
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool get(const char *key,Buffer<BC> &dest) const
|
||||
bool getBoolean(const std::string &key,bool dfl = false) const;
|
||||
|
||||
/**
|
||||
* @param key Key to get
|
||||
* @param dfl Default value if not present (default: 0)
|
||||
* @return Value converted to unsigned 64-bit int or 0 if not found
|
||||
*/
|
||||
inline uint64_t getUInt(const std::string &key,uint64_t dfl = 0) const
|
||||
{
|
||||
const int r = this->get(key,const_cast<char *>(reinterpret_cast<const char *>(dest.data())),BC);
|
||||
if (r >= 0) {
|
||||
dest.setSize((unsigned int)r);
|
||||
return true;
|
||||
} else {
|
||||
dest.clear();
|
||||
return false;
|
||||
}
|
||||
const_iterator e(find(key));
|
||||
if (e == end())
|
||||
return dfl;
|
||||
return Utils::strToU64(e->second.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a boolean value
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value if not found in dictionary
|
||||
* @return Boolean value of key or 'dfl' if not found
|
||||
* @param key Key to get
|
||||
* @param dfl Default value if not present (default: 0)
|
||||
* @return Value converted to unsigned 64-bit int or 0 if not found
|
||||
*/
|
||||
bool getB(const char *key,bool dfl = false) const
|
||||
inline uint64_t getHexUInt(const std::string &key,uint64_t dfl = 0) const
|
||||
{
|
||||
char tmp[4];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 0)
|
||||
return ((*tmp == '1')||(*tmp == 't')||(*tmp == 'T'));
|
||||
return dfl;
|
||||
const_iterator e(find(key));
|
||||
if (e == end())
|
||||
return dfl;
|
||||
return Utils::hexStrToU64(e->second.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an unsigned int64 stored as hex in the dictionary
|
||||
*
|
||||
* @param key Key to look up
|
||||
* @param dfl Default value or 0 if unspecified
|
||||
* @return Decoded hex UInt value or 'dfl' if not found
|
||||
* @param key Key to get
|
||||
* @param dfl Default value if not present (default: 0)
|
||||
* @return Value converted to signed 64-bit int or 0 if not found
|
||||
*/
|
||||
inline uint64_t getUI(const char *key,uint64_t dfl = 0) const
|
||||
inline int64_t getInt(const std::string &key,int64_t dfl = 0) const
|
||||
{
|
||||
char tmp[128];
|
||||
if (this->get(key,tmp,sizeof(tmp)) >= 1)
|
||||
return Utils::hexStrToU64(tmp);
|
||||
return dfl;
|
||||
const_iterator e(find(key));
|
||||
if (e == end())
|
||||
return dfl;
|
||||
return Utils::strTo64(e->second.c_str());
|
||||
}
|
||||
|
||||
std::string &operator[](const std::string &key);
|
||||
|
||||
/**
|
||||
* @param key Key to set
|
||||
* @param value String value
|
||||
*/
|
||||
inline void set(const std::string &key,const char *value)
|
||||
{
|
||||
(*this)[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new key=value pair
|
||||
*
|
||||
* If the key is already present this will append another, but the first
|
||||
* will always be returned by get(). This is not checked. If you want to
|
||||
* ensure a key is not present use erase() first.
|
||||
*
|
||||
* Use the vlen parameter to add binary values. Nulls will be escaped.
|
||||
*
|
||||
* @param key Key -- nulls, CR/LF, and equals (=) are illegal characters
|
||||
* @param value Value to set
|
||||
* @param vlen Length of value in bytes or -1 to treat value[] as a C-string and look for terminating 0
|
||||
* @return True if there was enough room to add this key=value pair
|
||||
* @param key Key to set
|
||||
* @param value String value
|
||||
*/
|
||||
inline bool add(const char *key,const char *value,int vlen = -1)
|
||||
inline void set(const std::string &key,const std::string &value)
|
||||
{
|
||||
for(unsigned int i=0;i<C;++i) {
|
||||
if (!_d[i]) {
|
||||
unsigned int j = i;
|
||||
|
||||
if (j > 0) {
|
||||
_d[j++] = '\n';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const char *p = key;
|
||||
while (*p) {
|
||||
_d[j++] = *(p++);
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
_d[j++] = '=';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
|
||||
p = value;
|
||||
int k = 0;
|
||||
while ( ((vlen < 0)&&(*p)) || (k < vlen) ) {
|
||||
switch(*p) {
|
||||
case 0:
|
||||
case '\r':
|
||||
case '\n':
|
||||
case '\\':
|
||||
case '=':
|
||||
_d[j++] = '\\';
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
switch(*p) {
|
||||
case 0: _d[j++] = '0'; break;
|
||||
case '\r': _d[j++] = 'r'; break;
|
||||
case '\n': _d[j++] = 'n'; break;
|
||||
case '\\': _d[j++] = '\\'; break;
|
||||
case '=': _d[j++] = 'e'; break;
|
||||
}
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_d[j++] = *p;
|
||||
if (j == C) {
|
||||
_d[i] = (char)0;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
++p;
|
||||
++k;
|
||||
}
|
||||
|
||||
_d[j] = (char)0;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
(*this)[key] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a boolean as a '1' or a '0'
|
||||
* @param key Key to set
|
||||
* @param value Boolean value
|
||||
*/
|
||||
inline bool add(const char *key,bool value)
|
||||
inline void set(const std::string &key,bool value)
|
||||
{
|
||||
return this->add(key,(value) ? "1" : "0",1);
|
||||
(*this)[key] = ((value) ? "1" : "0");
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
/**
|
||||
* @param key Key to set
|
||||
* @param value Integer value
|
||||
*/
|
||||
inline bool add(const char *key,uint64_t value)
|
||||
inline void set(const std::string &key,uint64_t value)
|
||||
{
|
||||
char tmp[32];
|
||||
char tmp[24];
|
||||
Utils::snprintf(tmp,sizeof(tmp),"%llu",(unsigned long long)value);
|
||||
(*this)[key] = tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to set
|
||||
* @param value Integer value
|
||||
*/
|
||||
inline void set(const std::string &key,int64_t value)
|
||||
{
|
||||
char tmp[24];
|
||||
Utils::snprintf(tmp,sizeof(tmp),"%lld",(long long)value);
|
||||
(*this)[key] = tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to set
|
||||
* @param value Integer value
|
||||
*/
|
||||
inline void setHex(const std::string &key,uint64_t value)
|
||||
{
|
||||
char tmp[24];
|
||||
Utils::snprintf(tmp,sizeof(tmp),"%llx",(unsigned long long)value);
|
||||
return this->add(key,tmp,-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a 64-bit integer (unsigned) as a hex value
|
||||
*/
|
||||
inline bool add(const char *key,const Address &a)
|
||||
{
|
||||
char tmp[32];
|
||||
Utils::snprintf(tmp,sizeof(tmp),"%.10llx",(unsigned long long)a.toInt());
|
||||
return this->add(key,tmp,-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binary buffer's contents as a value
|
||||
*
|
||||
* @tparam BC Buffer capacity (usually inferred)
|
||||
*/
|
||||
template<unsigned int BC>
|
||||
inline bool add(const char *key,const Buffer<BC> &value)
|
||||
{
|
||||
return this->add(key,(const char *)value.data(),(int)value.size());
|
||||
(*this)[key] = tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param key Key to check
|
||||
* @return True if key is present
|
||||
* @return True if dictionary contains key
|
||||
*/
|
||||
inline bool contains(const char *key) const
|
||||
{
|
||||
char tmp[2];
|
||||
return (this->get(key,tmp,2) >= 0);
|
||||
}
|
||||
inline bool contains(const std::string &key) const { return (find(key) != end()); }
|
||||
|
||||
/**
|
||||
* Erase a key from this dictionary
|
||||
*
|
||||
* Use this before add() to ensure that a key is replaced if it might
|
||||
* already be present.
|
||||
* @return String-serialized dictionary
|
||||
*/
|
||||
std::string toString() const;
|
||||
|
||||
/**
|
||||
* Clear and initialize from a string
|
||||
*
|
||||
* @param s String-serialized dictionary
|
||||
* @param maxlen Maximum length of string buffer
|
||||
*/
|
||||
void fromString(const char *s,unsigned int maxlen);
|
||||
inline void fromString(const std::string &s) { fromString(s.c_str(),(unsigned int)s.length()); }
|
||||
void updateFromString(const char *s,unsigned int maxlen);
|
||||
inline void update(const char *s,unsigned int maxlen) { updateFromString(s, maxlen); }
|
||||
inline void update(const std::string &s) { updateFromString(s.c_str(),(unsigned int)s.length()); }
|
||||
|
||||
/**
|
||||
* @return True if this dictionary is cryptographically signed
|
||||
*/
|
||||
inline bool hasSignature() const { return (find(ZT_DICTIONARY_SIGNATURE) != end()); }
|
||||
|
||||
/**
|
||||
* @return Signing identity in string-serialized format or empty string if none
|
||||
*/
|
||||
inline std::string signingIdentity() const { return get(ZT_DICTIONARY_SIGNATURE_IDENTITY,std::string()); }
|
||||
|
||||
/**
|
||||
* @return Signature timestamp in milliseconds since epoch or 0 if none
|
||||
*/
|
||||
uint64_t signatureTimestamp() const;
|
||||
|
||||
/**
|
||||
* @param key Key to erase
|
||||
* @return True if key was found and erased
|
||||
*/
|
||||
inline bool erase(const char *key)
|
||||
void eraseKey(const std::string &key);
|
||||
|
||||
/**
|
||||
* Remove any signature from this dictionary
|
||||
*/
|
||||
inline void removeSignature()
|
||||
{
|
||||
char d2[C];
|
||||
char *saveptr = (char *)0;
|
||||
unsigned int d2ptr = 0;
|
||||
bool found = false;
|
||||
for(char *f=Utils::stok(_d,"\r\n",&saveptr);(f);f=Utils::stok((char *)0,"\r\n",&saveptr)) {
|
||||
if (*f) {
|
||||
const char *p = f;
|
||||
const char *k = key;
|
||||
while ((*k)&&(*p)) {
|
||||
if (*k != *p)
|
||||
break;
|
||||
++k;
|
||||
++p;
|
||||
}
|
||||
if (*k) {
|
||||
p = f;
|
||||
while (*p)
|
||||
d2[d2ptr++] = *(p++);
|
||||
d2[d2ptr++] = '\n';
|
||||
} else {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
d2[d2ptr++] = (char)0;
|
||||
memcpy(_d,d2,d2ptr);
|
||||
return found;
|
||||
eraseKey(ZT_DICTIONARY_SIGNATURE);
|
||||
eraseKey(ZT_DICTIONARY_SIGNATURE_IDENTITY);
|
||||
eraseKey(ZT_DICTIONARY_SIGNATURE_TIMESTAMP);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value of C template parameter
|
||||
* Add or update signature fields with a signature of all other keys and values
|
||||
*
|
||||
* @param with Identity to sign with (must have secret key)
|
||||
* @param now Current time
|
||||
* @return True on success
|
||||
*/
|
||||
inline unsigned int capacity() const { return C; }
|
||||
bool sign(const Identity &id,uint64_t now);
|
||||
|
||||
inline const char *data() const { return _d; }
|
||||
inline char *unsafeData() { return _d; }
|
||||
/**
|
||||
* Verify signature against an identity
|
||||
*
|
||||
* @param id Identity to verify against
|
||||
* @return True if signature verification OK
|
||||
*/
|
||||
bool verify(const Identity &id) const;
|
||||
|
||||
private:
|
||||
char _d[C];
|
||||
void _mkSigBuf(std::string &buf) const;
|
||||
static void _appendEsc(const char *data,unsigned int len,std::string &to);
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
#endif
|
||||
|
||||
@@ -103,9 +103,9 @@ public:
|
||||
friend class Hashtable::Iterator;
|
||||
|
||||
/**
|
||||
* @param bc Initial capacity in buckets (default: 64, must be nonzero)
|
||||
* @param bc Initial capacity in buckets (default: 128, must be nonzero)
|
||||
*/
|
||||
Hashtable(unsigned long bc = 64) :
|
||||
Hashtable(unsigned long bc = 128) :
|
||||
_t(reinterpret_cast<_Bucket **>(::malloc(sizeof(_Bucket *) * bc))),
|
||||
_bc(bc),
|
||||
_s(0)
|
||||
@@ -362,7 +362,7 @@ private:
|
||||
template<typename O>
|
||||
static inline unsigned long _hc(const O &obj)
|
||||
{
|
||||
return (unsigned long)obj.hashCode();
|
||||
return obj.hashCode();
|
||||
}
|
||||
static inline unsigned long _hc(const uint64_t i)
|
||||
{
|
||||
|
||||
@@ -133,7 +133,7 @@ std::string Identity::toString(bool includePrivate) const
|
||||
std::string r;
|
||||
|
||||
r.append(_address.toString());
|
||||
r.append(":0:"); // 0 == ZT_OBJECT_TYPE_IDENTITY
|
||||
r.append(":0:"); // 0 == IDENTITY_TYPE_C25519
|
||||
r.append(Utils::hex(_publicKey.data,(unsigned int)_publicKey.size()));
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
r.push_back(':');
|
||||
|
||||
@@ -46,6 +46,14 @@ namespace ZeroTier {
|
||||
class Identity
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Identity types
|
||||
*/
|
||||
enum Type
|
||||
{
|
||||
IDENTITY_TYPE_C25519 = 0
|
||||
};
|
||||
|
||||
Identity() :
|
||||
_privateKey((C25519::Private *)0)
|
||||
{
|
||||
@@ -197,6 +205,11 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Identity type
|
||||
*/
|
||||
inline Type type() const throw() { return IDENTITY_TYPE_C25519; }
|
||||
|
||||
/**
|
||||
* @return This identity's address
|
||||
*/
|
||||
@@ -213,7 +226,7 @@ public:
|
||||
inline void serialize(Buffer<C> &b,bool includePrivate = false) const
|
||||
{
|
||||
_address.appendTo(b);
|
||||
b.append((uint8_t)0); // C25519/Ed25519 identity type
|
||||
b.append((unsigned char)IDENTITY_TYPE_C25519);
|
||||
b.append(_publicKey.data,(unsigned int)_publicKey.size());
|
||||
if ((_privateKey)&&(includePrivate)) {
|
||||
b.append((unsigned char)_privateKey->size());
|
||||
@@ -244,7 +257,7 @@ public:
|
||||
_address.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
p += ZT_ADDRESS_LENGTH;
|
||||
|
||||
if (b[p++] != 0)
|
||||
if (b[p++] != IDENTITY_TYPE_C25519)
|
||||
throw std::invalid_argument("unsupported identity type");
|
||||
|
||||
memcpy(_publicKey.data,b.field(p,(unsigned int)_publicKey.size()),(unsigned int)_publicKey.size());
|
||||
@@ -282,24 +295,6 @@ public:
|
||||
bool fromString(const char *str);
|
||||
inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
|
||||
|
||||
/**
|
||||
* @return C25519 public key
|
||||
*/
|
||||
inline const C25519::Public &publicKey() const { return _publicKey; }
|
||||
|
||||
/**
|
||||
* @return C25519 key pair (only returns valid pair if private key is present in this Identity object)
|
||||
*/
|
||||
inline const C25519::Pair privateKeyPair() const
|
||||
{
|
||||
C25519::Pair pair;
|
||||
pair.pub = _publicKey;
|
||||
if (_privateKey)
|
||||
pair.priv = *_privateKey;
|
||||
else memset(pair.priv.data,0,ZT_C25519_PRIVATE_KEY_LEN);
|
||||
return pair;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this identity contains something
|
||||
*/
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -22,7 +22,7 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "Packet.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Peer.hpp"
|
||||
@@ -56,40 +56,59 @@ class IncomingPacket : public Packet
|
||||
public:
|
||||
IncomingPacket() :
|
||||
Packet(),
|
||||
_receiveTime(0)
|
||||
_receiveTime(0),
|
||||
_localAddress(),
|
||||
_remoteAddress()
|
||||
{
|
||||
}
|
||||
|
||||
IncomingPacket(const IncomingPacket &p)
|
||||
{
|
||||
// All fields including InetAddress are memcpy'able
|
||||
memcpy(this,&p,sizeof(IncomingPacket));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new packet-in-decode
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param path Path over which packet arrived
|
||||
* @param localAddress Local interface address
|
||||
* @param remoteAddress Address from which packet came
|
||||
* @param now Current time
|
||||
* @throws std::out_of_range Range error processing packet
|
||||
*/
|
||||
IncomingPacket(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now) :
|
||||
Packet(data,len),
|
||||
_receiveTime(now),
|
||||
_path(path)
|
||||
IncomingPacket(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now) :
|
||||
Packet(data,len),
|
||||
_receiveTime(now),
|
||||
_localAddress(localAddress),
|
||||
_remoteAddress(remoteAddress)
|
||||
{
|
||||
}
|
||||
|
||||
inline IncomingPacket &operator=(const IncomingPacket &p)
|
||||
{
|
||||
// All fields including InetAddress are memcpy'able
|
||||
memcpy(this,&p,sizeof(IncomingPacket));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init packet-in-decode in place
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param path Path over which packet arrived
|
||||
* @param localAddress Local interface address
|
||||
* @param remoteAddress Address from which packet came
|
||||
* @param now Current time
|
||||
* @throws std::out_of_range Range error processing packet
|
||||
*/
|
||||
inline void init(const void *data,unsigned int len,const SharedPtr<Path> &path,uint64_t now)
|
||||
inline void init(const void *data,unsigned int len,const InetAddress &localAddress,const InetAddress &remoteAddress,uint64_t now)
|
||||
{
|
||||
copyFrom(data,len);
|
||||
_receiveTime = now;
|
||||
_path = path;
|
||||
_localAddress = localAddress;
|
||||
_remoteAddress = remoteAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -99,12 +118,21 @@ public:
|
||||
* about whether the packet was valid. A rejection is 'complete.'
|
||||
*
|
||||
* Once true is returned, this must not be called again. The packet's state
|
||||
* may no longer be valid.
|
||||
* may no longer be valid. The only exception is deferred decoding. In this
|
||||
* case true is returned to indicate to the normal decode path that it is
|
||||
* finished with the packet. The packet will have added itself to the
|
||||
* deferred queue and will expect tryDecode() to be called one more time
|
||||
* with deferred set to true.
|
||||
*
|
||||
* Deferred decoding is performed by DeferredPackets.cpp and should not be
|
||||
* done elsewhere. Under deferred decoding packets only get one shot and
|
||||
* so the return value of tryDecode() is ignored.
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param deferred If true, this is a deferred decode and the return is ignored
|
||||
* @return True if decoding and processing is complete, false if caller should try again
|
||||
*/
|
||||
bool tryDecode(const RuntimeEnvironment *RR);
|
||||
bool tryDecode(const RuntimeEnvironment *RR,bool deferred);
|
||||
|
||||
/**
|
||||
* @return Time of packet receipt / start of decode
|
||||
@@ -136,7 +164,7 @@ private:
|
||||
// These are called internally to handle packet contents once it has
|
||||
// been authenticated, decrypted, decompressed, and classified.
|
||||
bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated);
|
||||
bool _doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer); // can be called with NULL peer, while all others cannot
|
||||
bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
@@ -144,9 +172,9 @@ private:
|
||||
bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doNETWORK_CONFIG_REFRESH(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);
|
||||
@@ -154,10 +182,12 @@ private:
|
||||
bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
|
||||
|
||||
void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid);
|
||||
// Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to communicate
|
||||
void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
|
||||
|
||||
uint64_t _receiveTime;
|
||||
SharedPtr<Path> _path;
|
||||
InetAddress _localAddress;
|
||||
InetAddress _remoteAddress;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -113,7 +113,7 @@ void InetAddress::set(const std::string &ip,unsigned int port)
|
||||
sin6->sin6_port = Utils::hton((uint16_t)port);
|
||||
if (inet_pton(AF_INET6,ip.c_str(),(void *)&(sin6->sin6_addr.s6_addr)) <= 0)
|
||||
memset(this,0,sizeof(InetAddress));
|
||||
} else if (ip.find('.') != std::string::npos) {
|
||||
} else {
|
||||
struct sockaddr_in *sin = reinterpret_cast<struct sockaddr_in *>(this);
|
||||
ss_family = AF_INET;
|
||||
sin->sin_port = Utils::hton((uint16_t)port);
|
||||
@@ -279,8 +279,6 @@ bool InetAddress::containsAddress(const InetAddress &addr) const
|
||||
switch(ss_family) {
|
||||
case AF_INET: {
|
||||
const unsigned int bits = netmaskBits();
|
||||
if (bits == 0)
|
||||
return true;
|
||||
return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
|
||||
}
|
||||
case AF_INET6: {
|
||||
@@ -395,6 +393,7 @@ bool InetAddress::operator<(const InetAddress &a) const
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
|
||||
throw()
|
||||
{
|
||||
struct sockaddr_in6 sin6;
|
||||
sin6.sin6_family = AF_INET6;
|
||||
@@ -419,6 +418,7 @@ InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac)
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
throw()
|
||||
{
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
@@ -443,25 +443,4 @@ InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
return r;
|
||||
}
|
||||
|
||||
InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
{
|
||||
nwid ^= (nwid >> 32);
|
||||
InetAddress r;
|
||||
struct sockaddr_in6 *const sin6 = reinterpret_cast<struct sockaddr_in6 *>(&r);
|
||||
sin6->sin6_family = AF_INET6;
|
||||
sin6->sin6_addr.s6_addr[0] = 0xfc;
|
||||
sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24);
|
||||
sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16);
|
||||
sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8);
|
||||
sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
|
||||
sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32);
|
||||
sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24);
|
||||
sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16);
|
||||
sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8);
|
||||
sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
|
||||
sin6->sin6_addr.s6_addr[15] = 0x01;
|
||||
sin6->sin6_port = Utils::hton((uint16_t)40);
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -231,6 +231,7 @@ struct InetAddress : public sockaddr_storage
|
||||
* @param port Port, 0 to 65535
|
||||
*/
|
||||
inline void setPort(unsigned int port)
|
||||
throw()
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
@@ -242,25 +243,6 @@ struct InetAddress : public sockaddr_storage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if this network/netmask route describes a default route (e.g. 0.0.0.0/0)
|
||||
*/
|
||||
inline bool isDefaultRoute() const
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
return ( (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const struct sockaddr_in *>(this)->sin_port == 0) );
|
||||
case AF_INET6:
|
||||
const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(int i=0;i<16;++i) {
|
||||
if (ipb[i])
|
||||
return false;
|
||||
}
|
||||
return (reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port == 0);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ASCII IP/port format representation
|
||||
*/
|
||||
@@ -300,19 +282,6 @@ struct InetAddress : public sockaddr_storage
|
||||
*/
|
||||
inline unsigned int netmaskBits() const throw() { return port(); }
|
||||
|
||||
/**
|
||||
* @return True if netmask bits is valid for the address type
|
||||
*/
|
||||
inline bool netmaskBitsValid() const
|
||||
{
|
||||
const unsigned int n = port();
|
||||
switch(ss_family) {
|
||||
case AF_INET: return (n <= 32);
|
||||
case AF_INET6: return (n <= 128);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias for port()
|
||||
*
|
||||
@@ -369,6 +338,7 @@ struct InetAddress : public sockaddr_storage
|
||||
* @return pointer to raw address bytes or NULL if not available
|
||||
*/
|
||||
inline const void *rawIpData() const
|
||||
throw()
|
||||
{
|
||||
switch(ss_family) {
|
||||
case AF_INET: return (const void *)&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr);
|
||||
@@ -377,25 +347,6 @@ struct InetAddress : public sockaddr_storage
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return InetAddress containing only the IP portion of this address and a zero port, or NULL if not IPv4 or IPv6
|
||||
*/
|
||||
inline InetAddress ipOnly() const
|
||||
{
|
||||
InetAddress r;
|
||||
switch(ss_family) {
|
||||
case AF_INET:
|
||||
r.ss_family = AF_INET;
|
||||
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
r.ss_family = AF_INET6;
|
||||
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
|
||||
break;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an IP-only comparison or, if that is impossible, a memcmp()
|
||||
*
|
||||
@@ -414,25 +365,6 @@ struct InetAddress : public sockaddr_storage
|
||||
return false;
|
||||
}
|
||||
|
||||
inline unsigned long hashCode() const
|
||||
{
|
||||
if (ss_family == AF_INET) {
|
||||
return ((unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr + (unsigned long)reinterpret_cast<const struct sockaddr_in *>(this)->sin_port);
|
||||
} else if (ss_family == AF_INET6) {
|
||||
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
|
||||
for(long i=0;i<16;++i)
|
||||
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||
return tmp;
|
||||
} else {
|
||||
unsigned long tmp = reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_port;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(this);
|
||||
for(long i=0;i<sizeof(InetAddress);++i)
|
||||
reinterpret_cast<uint8_t *>(&tmp)[i % sizeof(tmp)] ^= a[i];
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to null/zero
|
||||
*/
|
||||
@@ -521,7 +453,8 @@ struct InetAddress : public sockaddr_storage
|
||||
* @param mac MAC address seed
|
||||
* @return IPv6 link-local address
|
||||
*/
|
||||
static InetAddress makeIpv6LinkLocal(const MAC &mac);
|
||||
static InetAddress makeIpv6LinkLocal(const MAC &mac)
|
||||
throw();
|
||||
|
||||
/**
|
||||
* Compute private IPv6 unicast address from network ID and ZeroTier address
|
||||
@@ -564,12 +497,8 @@ struct InetAddress : public sockaddr_storage
|
||||
* @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
|
||||
* @return IPv6 private unicast address with /88 netmask
|
||||
*/
|
||||
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
|
||||
/**
|
||||
* Compute a private IPv6 "6plane" unicast address from network ID and ZeroTier address
|
||||
*/
|
||||
static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress);
|
||||
static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
|
||||
throw();
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -60,6 +60,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
MulticastGroup(const char *s)
|
||||
{
|
||||
fromString(s);
|
||||
}
|
||||
|
||||
MulticastGroup(const std::string &s)
|
||||
{
|
||||
fromString(s.c_str());
|
||||
}
|
||||
|
||||
/**
|
||||
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
|
||||
*
|
||||
@@ -96,6 +106,22 @@ public:
|
||||
return std::string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a human-readable multicast group
|
||||
*
|
||||
* @param s Multicast group in hex MAC/ADI format
|
||||
*/
|
||||
inline void fromString(const char *s)
|
||||
{
|
||||
char hex[17];
|
||||
unsigned int hexlen = 0;
|
||||
while ((*s)&&(*s != '/')&&(hexlen < (sizeof(hex) - 1)))
|
||||
hex[hexlen++] = *s;
|
||||
hex[hexlen] = (char)0;
|
||||
_mac.fromString(hex);
|
||||
_adi = (*s == '/') ? (uint32_t)Utils::hexStrToULong(s + 1) : (uint32_t)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Multicast address
|
||||
*/
|
||||
|
||||
@@ -34,8 +34,8 @@ namespace ZeroTier {
|
||||
|
||||
Multicaster::Multicaster(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_groups(256),
|
||||
_gatherAuth(256)
|
||||
_groups(1024),
|
||||
_groups_m()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -152,10 +152,10 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
|
||||
}
|
||||
|
||||
void Multicaster::send(
|
||||
const CertificateOfMembership *com,
|
||||
unsigned int limit,
|
||||
uint64_t now,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const std::vector<Address> &alwaysSendTo,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
@@ -194,7 +194,7 @@ void Multicaster::send(
|
||||
RR,
|
||||
now,
|
||||
nwid,
|
||||
disableCompression,
|
||||
com,
|
||||
limit,
|
||||
1, // we'll still gather a little from peers to keep multicast list fresh
|
||||
src,
|
||||
@@ -226,38 +226,33 @@ void Multicaster::send(
|
||||
|
||||
if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
|
||||
gs.lastExplicitGather = now;
|
||||
SharedPtr<Peer> explicitGatherPeers[2];
|
||||
explicitGatherPeers[0] = RR->topology->getBestRoot();
|
||||
explicitGatherPeers[1] = RR->topology->getPeer(Network::controllerFor(nwid));
|
||||
for(unsigned int k=0;k<2;++k) {
|
||||
const SharedPtr<Peer> &p = explicitGatherPeers[k];
|
||||
if (!p)
|
||||
continue;
|
||||
//TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
|
||||
|
||||
Address explicitGatherPeers[16];
|
||||
unsigned int numExplicitGatherPeers = 0;
|
||||
SharedPtr<Peer> bestRoot(RR->topology->getBestRoot());
|
||||
if (bestRoot)
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = Network::controllerFor(nwid);
|
||||
SharedPtr<Network> network(RR->node->network(nwid));
|
||||
if (network) {
|
||||
std::vector<Address> anchors(network->config().anchors());
|
||||
for(std::vector<Address>::const_iterator a(anchors.begin());a!=anchors.end();++a) {
|
||||
if (*a != RR->identity.address()) {
|
||||
explicitGatherPeers[numExplicitGatherPeers++] = *a;
|
||||
if (numExplicitGatherPeers == 16)
|
||||
break;
|
||||
}
|
||||
const CertificateOfMembership *com = (CertificateOfMembership *)0;
|
||||
{
|
||||
SharedPtr<Network> nw(RR->node->network(nwid));
|
||||
if ((nw)&&(nw->hasConfig())&&(nw->config().com)&&(nw->config().isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
|
||||
com = &(nw->config().com);
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned int k=0;k<numExplicitGatherPeers;++k) {
|
||||
const CertificateOfMembership *com = (network) ? ((network->config().com) ? &(network->config().com) : (const CertificateOfMembership *)0) : (const CertificateOfMembership *)0;
|
||||
Packet outp(explicitGatherPeers[k],RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
||||
Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
|
||||
outp.append(nwid);
|
||||
outp.append((uint8_t)((com) ? 0x01 : 0x00));
|
||||
outp.append((uint8_t)(com ? 0x01 : 0x00));
|
||||
mg.mac().appendTo(outp);
|
||||
outp.append((uint32_t)mg.adi());
|
||||
outp.append((uint32_t)gatherLimit);
|
||||
if (com)
|
||||
com->serialize(outp);
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
RR->sw->send(outp,true);
|
||||
RR->sw->send(outp,true,0);
|
||||
}
|
||||
gatherLimit = 0;
|
||||
}
|
||||
|
||||
gs.txQueue.push_back(OutboundMulticast());
|
||||
@@ -267,7 +262,7 @@ void Multicaster::send(
|
||||
RR,
|
||||
now,
|
||||
nwid,
|
||||
disableCompression,
|
||||
com,
|
||||
limit,
|
||||
gatherLimit,
|
||||
src,
|
||||
@@ -304,62 +299,42 @@ void Multicaster::send(
|
||||
|
||||
void Multicaster::clean(uint64_t now)
|
||||
{
|
||||
{
|
||||
Mutex::Lock _l(_groups_m);
|
||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||
while (mm.next(k,s)) {
|
||||
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
||||
if ((tx->expired(now))||(tx->atLimit()))
|
||||
s->txQueue.erase(tx++);
|
||||
else ++tx;
|
||||
}
|
||||
Mutex::Lock _l(_groups_m);
|
||||
|
||||
unsigned long count = 0;
|
||||
{
|
||||
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
|
||||
std::vector<MulticastGroupMember>::iterator writer(reader);
|
||||
while (reader != s->members.end()) {
|
||||
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
|
||||
*writer = *reader;
|
||||
++writer;
|
||||
++count;
|
||||
}
|
||||
++reader;
|
||||
Multicaster::Key *k = (Multicaster::Key *)0;
|
||||
MulticastGroupStatus *s = (MulticastGroupStatus *)0;
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus>::Iterator mm(_groups);
|
||||
while (mm.next(k,s)) {
|
||||
for(std::list<OutboundMulticast>::iterator tx(s->txQueue.begin());tx!=s->txQueue.end();) {
|
||||
if ((tx->expired(now))||(tx->atLimit()))
|
||||
s->txQueue.erase(tx++);
|
||||
else ++tx;
|
||||
}
|
||||
|
||||
unsigned long count = 0;
|
||||
{
|
||||
std::vector<MulticastGroupMember>::iterator reader(s->members.begin());
|
||||
std::vector<MulticastGroupMember>::iterator writer(reader);
|
||||
while (reader != s->members.end()) {
|
||||
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
|
||||
*writer = *reader;
|
||||
++writer;
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count) {
|
||||
s->members.resize(count);
|
||||
} else if (s->txQueue.empty()) {
|
||||
_groups.erase(*k);
|
||||
} else {
|
||||
s->members.clear();
|
||||
++reader;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_gatherAuth_m);
|
||||
_GatherAuthKey *k = (_GatherAuthKey *)0;
|
||||
uint64_t *ts = (uint64_t *)ts;
|
||||
Hashtable<_GatherAuthKey,uint64_t>::Iterator i(_gatherAuth);
|
||||
while (i.next(k,ts)) {
|
||||
if ((now - *ts) >= ZT_MULTICAST_CREDENTIAL_EXPIRATON)
|
||||
_gatherAuth.erase(*k);
|
||||
if (count) {
|
||||
s->members.resize(count);
|
||||
} else if (s->txQueue.empty()) {
|
||||
_groups.erase(*k);
|
||||
} else {
|
||||
s->members.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
|
||||
{
|
||||
if ((alreadyValidated)||(com.verify(RR) == 0)) {
|
||||
Mutex::Lock _l(_gatherAuth_m);
|
||||
_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
|
||||
}
|
||||
}
|
||||
|
||||
void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
|
||||
{
|
||||
// assumes _groups_m is locked
|
||||
|
||||
@@ -150,10 +150,10 @@ public:
|
||||
/**
|
||||
* Send a multicast
|
||||
*
|
||||
* @param com Certificate of membership to include or NULL for none
|
||||
* @param limit Multicast limit
|
||||
* @param now Current time
|
||||
* @param nwid Network ID
|
||||
* @param disableCompression Disable packet payload compression?
|
||||
* @param alwaysSendTo Send to these peers first and even if not included in subscriber list
|
||||
* @param mg Multicast group
|
||||
* @param src Source Ethernet MAC address or NULL to skip in packet and compute from ZT address (non-bridged mode)
|
||||
@@ -162,10 +162,10 @@ public:
|
||||
* @param len Length of packet data
|
||||
*/
|
||||
void send(
|
||||
const CertificateOfMembership *com,
|
||||
unsigned int limit,
|
||||
uint64_t now,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const std::vector<Address> &alwaysSendTo,
|
||||
const MulticastGroup &mg,
|
||||
const MAC &src,
|
||||
@@ -181,52 +181,12 @@ public:
|
||||
*/
|
||||
void clean(uint64_t now);
|
||||
|
||||
/**
|
||||
* Add an authorization credential
|
||||
*
|
||||
* The Multicaster keeps its own track of when valid credentials of network
|
||||
* membership are presented. This allows it to control MULTICAST_LIKE
|
||||
* GATHER authorization for networks this node does not belong to.
|
||||
*
|
||||
* @param com Certificate of membership
|
||||
* @param alreadyValidated If true, COM has already been checked and found to be valid and signed
|
||||
*/
|
||||
void addCredential(const CertificateOfMembership &com,bool alreadyValidated);
|
||||
|
||||
/**
|
||||
* Check authorization for GATHER and LIKE for non-network-members
|
||||
*
|
||||
* @param a Address of peer
|
||||
* @param nwid Network ID
|
||||
* @param now Current time
|
||||
* @return True if GATHER and LIKE should be allowed
|
||||
*/
|
||||
bool cacheAuthorized(const Address &a,const uint64_t nwid,const uint64_t now) const
|
||||
{
|
||||
Mutex::Lock _l(_gatherAuth_m);
|
||||
const uint64_t *p = _gatherAuth.get(_GatherAuthKey(nwid,a));
|
||||
return ((p)&&((now - *p) < ZT_MULTICAST_CREDENTIAL_EXPIRATON));
|
||||
}
|
||||
|
||||
private:
|
||||
void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
Hashtable<Multicaster::Key,MulticastGroupStatus> _groups;
|
||||
Mutex _groups_m;
|
||||
|
||||
struct _GatherAuthKey
|
||||
{
|
||||
_GatherAuthKey() : member(0),networkId(0) {}
|
||||
_GatherAuthKey(const uint64_t nwid,const Address &a) : member(a.toInt()),networkId(nwid) {}
|
||||
inline unsigned long hashCode() const { return (member ^ networkId); }
|
||||
inline bool operator==(const _GatherAuthKey &k) const { return ((member == k.member)&&(networkId == k.networkId)); }
|
||||
uint64_t member;
|
||||
uint64_t networkId;
|
||||
};
|
||||
Hashtable< _GatherAuthKey,uint64_t > _gatherAuth;
|
||||
Mutex _gatherAuth_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -40,17 +40,14 @@
|
||||
#include "MAC.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "Multicaster.hpp"
|
||||
#include "Membership.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
|
||||
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
|
||||
#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Peer;
|
||||
class _MulticastAnnounceAll;
|
||||
|
||||
/**
|
||||
* A virtual LAN
|
||||
@@ -58,6 +55,7 @@ class Peer;
|
||||
class Network : NonCopyable
|
||||
{
|
||||
friend class SharedPtr<Network>;
|
||||
friend class _MulticastAnnounceAll; // internal function object
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -65,11 +63,6 @@ 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
|
||||
*
|
||||
@@ -84,78 +77,43 @@ 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, 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 (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
|
||||
* @param macDest Ethernet layer destination address
|
||||
* @param frameData Ethernet frame data
|
||||
* @param frameLen Ethernet frame payload length
|
||||
* @param etherType 16-bit ethernet type ID
|
||||
* @param vlanId 16-bit VLAN ID
|
||||
* @return True if packet should be sent, false if dropped or redirected
|
||||
* @return Network ID
|
||||
*/
|
||||
bool filterOutgoingPacket(
|
||||
const bool noTee,
|
||||
const Address &ztSource,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId);
|
||||
inline uint64_t id() const throw() { return _id; }
|
||||
|
||||
/**
|
||||
* Apply filters to an incoming packet
|
||||
*
|
||||
* This applies filters from our network config and, if that doesn't match,
|
||||
* the peer's capabilities in ascending order of capability ID. If there is
|
||||
* a match certain actions may be taken such as sending a copy of the packet
|
||||
* to a TEE or REDIRECT target.
|
||||
*
|
||||
* @param sourcePeer Source Peer
|
||||
* @param ztDest Destination ZeroTier address
|
||||
* @param macSource Ethernet layer source address
|
||||
* @param macDest Ethernet layer destination address
|
||||
* @param frameData Ethernet frame data
|
||||
* @param frameLen Ethernet frame payload length
|
||||
* @param etherType 16-bit ethernet type ID
|
||||
* @param vlanId 16-bit VLAN ID
|
||||
* @return 0 == drop, 1 == accept, 2 == accept even if bridged
|
||||
* @return Address of network's controller (most significant 40 bits of ID)
|
||||
*/
|
||||
int filterIncomingPacket(
|
||||
const SharedPtr<Peer> &sourcePeer,
|
||||
const Address &ztDest,
|
||||
const MAC &macSource,
|
||||
const MAC &macDest,
|
||||
const uint8_t *frameData,
|
||||
const unsigned int frameLen,
|
||||
const unsigned int etherType,
|
||||
const unsigned int vlanId);
|
||||
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 check groups we've learned via bridging
|
||||
* @param includeBridgedGroups If true, also include any groups we've learned via bridging
|
||||
* @return True if this network endpoint / peer is a member
|
||||
*/
|
||||
bool subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBridgedGroups) const;
|
||||
@@ -175,17 +133,30 @@ public:
|
||||
void multicastUnsubscribe(const MulticastGroup &mg);
|
||||
|
||||
/**
|
||||
* Handle an inbound network config chunk
|
||||
* Announce multicast groups to a peer if that peer is authorized on this network
|
||||
*
|
||||
* This is called from IncomingPacket to handle incoming network config
|
||||
* chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
|
||||
* each chunk and once assembled applies the configuration.
|
||||
*
|
||||
* @param chunk Packet containing chunk
|
||||
* @param ptr Index of chunk and related fields in packet
|
||||
* @return Update ID if update was fully assembled and accepted or 0 otherwise
|
||||
* @param peer Peer to try to announce multicast groups to
|
||||
* @return True if peer was authorized and groups were announced
|
||||
*/
|
||||
uint64_t handleConfigChunk(const Packet &chunk,unsigned int ptr);
|
||||
bool tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer);
|
||||
|
||||
/**
|
||||
* 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 confBytes Network configuration in old-style Dictionary or new-style serialized format
|
||||
* @param confLen Length of network configuration in bytes
|
||||
* @param saveToDisk IF true (default), write config to disk
|
||||
* @return 0 -- rejected, 1 -- accepted but not new, 2 -- accepted new config
|
||||
*/
|
||||
int setConfiguration(const void *confBytes,unsigned int confLen,bool saveToDisk);
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
|
||||
@@ -197,7 +168,7 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this
|
||||
* Set netconf failure to 'not found' -- called by PacketDecider when controller reports this
|
||||
*/
|
||||
inline void setNotFound()
|
||||
{
|
||||
@@ -211,24 +182,73 @@ public:
|
||||
void requestConfiguration();
|
||||
|
||||
/**
|
||||
* Determine whether this peer is permitted to communicate on this network
|
||||
* @param peer Peer to check
|
||||
* @return True if peer is allowed to communicate on this network
|
||||
*/
|
||||
bool gate(const SharedPtr<Peer> &peer);
|
||||
inline bool isAllowed(const SharedPtr<Peer> &peer) const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
return _isAllowed(peer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do periodic cleanup and housekeeping tasks
|
||||
* Perform cleanup and possibly save state
|
||||
*/
|
||||
void clean();
|
||||
|
||||
/**
|
||||
* Push state to members such as multicast group memberships and latest COM (if needed)
|
||||
* @return Time of last updated configuration or 0 if none
|
||||
*/
|
||||
inline void sendUpdatesToMembers()
|
||||
inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
|
||||
|
||||
/**
|
||||
* @return Status of this network
|
||||
*/
|
||||
inline ZT_VirtualNetworkStatus status() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_sendUpdatesToMembers((const MulticastGroup *)0);
|
||||
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
|
||||
*
|
||||
* This returns a const reference to the network config in place, which is safe
|
||||
* to concurrently access but *may* change during access. Normally this isn't a
|
||||
* problem, but if it is use configCopy().
|
||||
*
|
||||
* @return Network configuration (may be a null config if we don't have one yet)
|
||||
*/
|
||||
inline const NetworkConfig &config() const { return _config; }
|
||||
|
||||
/**
|
||||
* @return A thread-safe copy of our NetworkConfig instead of a const reference
|
||||
*/
|
||||
inline NetworkConfig configCopy() const
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
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 throw() { return _mac; }
|
||||
|
||||
/**
|
||||
* Find the node on this network that has this MAC behind it (if any)
|
||||
*
|
||||
@@ -239,7 +259,9 @@ public:
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const Address *const br = _remoteBridgeRoutes.get(mac);
|
||||
return ((br) ? *br : Address());
|
||||
if (br)
|
||||
return *br;
|
||||
return Address();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -259,48 +281,14 @@ public:
|
||||
void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
* @return True if traffic on this network's tap is enabled
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(const CertificateOfMembership &com);
|
||||
inline bool enabled() const throw() { return _enabled; }
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
* @param enabled Should traffic be allowed on this network?
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(const Capability &cap)
|
||||
{
|
||||
if (cap.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(cap.issuedTo()).addCredential(RR,_config,cap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
inline Membership::AddCredentialResult addCredential(const Tag &tag)
|
||||
{
|
||||
if (tag.networkId() != _id)
|
||||
return Membership::ADD_REJECTED;
|
||||
Mutex::Lock _l(_lock);
|
||||
return _membership(tag.issuedTo()).addCredential(RR,_config,tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a credential and learn it if it passes certificate and other checks
|
||||
*/
|
||||
Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
|
||||
|
||||
/**
|
||||
* Force push credentials (COM, etc.) to a peer now
|
||||
*
|
||||
* @param to Destination peer address
|
||||
* @param now Current time
|
||||
*/
|
||||
inline void pushCredentialsNow(const Address &to,const uint64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
_membership(to).pushCredentials(RR,now,to,_config,-1,true);
|
||||
}
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* Destroy this network
|
||||
@@ -312,57 +300,40 @@ public:
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @return Pointer to user PTR (modifiable user ptr used in API)
|
||||
*/
|
||||
inline void **userPtr() throw() { return &_uPtr; }
|
||||
|
||||
inline bool operator==(const Network &n) const throw() { return (_id == n._id); }
|
||||
inline bool operator!=(const Network &n) const throw() { return (_id != n._id); }
|
||||
inline bool operator<(const Network &n) const throw() { return (_id < n._id); }
|
||||
inline bool operator>(const Network &n) const throw() { return (_id > n._id); }
|
||||
inline bool operator<=(const Network &n) const throw() { return (_id <= n._id); }
|
||||
inline bool operator>=(const Network &n) const throw() { return (_id >= n._id); }
|
||||
|
||||
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);
|
||||
void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
|
||||
void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
|
||||
bool _isAllowed(const SharedPtr<Peer> &peer) const;
|
||||
void _announceMulticastGroups();
|
||||
void _announceMulticastGroupsTo(const SharedPtr<Peer> &peer,const std::vector<MulticastGroup> &allMulticastGroups) const;
|
||||
std::vector<MulticastGroup> _allMulticastGroups() const;
|
||||
Membership &_membership(const Address &a);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
const RuntimeEnvironment *RR;
|
||||
void *_uPtr;
|
||||
const uint64_t _id;
|
||||
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||
uint64_t _id;
|
||||
MAC _mac; // local MAC address
|
||||
bool _portInitialized;
|
||||
volatile bool _enabled;
|
||||
volatile bool _portInitialized;
|
||||
|
||||
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
||||
Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
|
||||
Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
|
||||
|
||||
NetworkConfig _config;
|
||||
uint64_t _lastConfigUpdate;
|
||||
volatile uint64_t _lastConfigUpdate;
|
||||
|
||||
struct _IncomingConfigChunk
|
||||
{
|
||||
uint64_t ts;
|
||||
uint64_t updateId;
|
||||
uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
|
||||
unsigned long haveChunks;
|
||||
unsigned long haveBytes;
|
||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
|
||||
};
|
||||
_IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
|
||||
|
||||
bool _destroyed;
|
||||
volatile bool _destroyed;
|
||||
|
||||
enum {
|
||||
NETCONF_FAILURE_NONE,
|
||||
@@ -370,9 +341,7 @@ private:
|
||||
NETCONF_FAILURE_NOT_FOUND,
|
||||
NETCONF_FAILURE_INIT_FAILED
|
||||
} _netconfFailure;
|
||||
int _portError; // return value from port config callback
|
||||
|
||||
Hashtable<Address,Membership> _memberships;
|
||||
volatile int _portError; // return value from port config callback
|
||||
|
||||
Mutex _lock;
|
||||
|
||||
|
||||
@@ -18,328 +18,163 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const
|
||||
{
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
|
||||
try {
|
||||
d.clear();
|
||||
|
||||
// Try to put the more human-readable fields first
|
||||
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_VERSION,(uint64_t)ZT_NETWORKCONFIG_VERSION)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,this->networkId)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,this->timestamp)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,this->credentialTimeMaxDelta)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_REVISION,this->revision)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,this->issuedTo)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,this->flags)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,(uint64_t)this->multicastLimit)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)this->type)) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name)) return false;
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
if (includeLegacy) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD,this->allowPassiveBridging())) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD,this->enableBroadcast())) return false;
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,this->isPrivate())) return false;
|
||||
|
||||
std::string v4s;
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
if (this->staticIps[i].ss_family == AF_INET) {
|
||||
if (v4s.length() > 0)
|
||||
v4s.push_back(',');
|
||||
v4s.append(this->staticIps[i].toString());
|
||||
}
|
||||
}
|
||||
if (v4s.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,v4s.c_str())) return false;
|
||||
}
|
||||
std::string v6s;
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
if (this->staticIps[i].ss_family == AF_INET6) {
|
||||
if (v6s.length() > 0)
|
||||
v6s.push_back(',');
|
||||
v6s.append(this->staticIps[i].toString());
|
||||
}
|
||||
}
|
||||
if (v6s.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,v6s.c_str())) return false;
|
||||
}
|
||||
void NetworkConfig::fromDictionary(const char *ds,unsigned int dslen)
|
||||
{
|
||||
static const std::string zero("0");
|
||||
static const std::string one("1");
|
||||
|
||||
std::string ets;
|
||||
unsigned int et = 0;
|
||||
ZT_VirtualNetworkRuleType lastrt = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
|
||||
if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
|
||||
et = rules[i].v.etherType;
|
||||
} else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
|
||||
if (((int)lastrt < 32)||(lastrt == ZT_NETWORK_RULE_MATCH_ETHERTYPE)) {
|
||||
if (ets.length() > 0)
|
||||
ets.push_back(',');
|
||||
char tmp2[16];
|
||||
Utils::snprintf(tmp2,sizeof(tmp2),"%x",et);
|
||||
ets.append(tmp2);
|
||||
Dictionary d(ds,dslen);
|
||||
|
||||
memset(this,0,sizeof(NetworkConfig));
|
||||
|
||||
// NOTE: d.get(name) throws if not found, d.get(name,default) returns default
|
||||
|
||||
networkId = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,"0").c_str());
|
||||
if (!networkId)
|
||||
throw std::invalid_argument("configuration contains zero network ID");
|
||||
|
||||
timestamp = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,"0").c_str());
|
||||
revision = Utils::hexStrToU64(d.get(ZT_NETWORKCONFIG_DICT_KEY_REVISION,"1").c_str()); // older controllers don't send this, so default to 1
|
||||
issuedTo = Address(d.get(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,"0"));
|
||||
|
||||
multicastLimit = Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,zero).c_str());
|
||||
if (multicastLimit == 0) multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
|
||||
|
||||
flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING,zero).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING : 0);
|
||||
flags |= ((Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST,one).c_str()) != 0) ? ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST : 0);
|
||||
|
||||
this->type = (Utils::hexStrToUInt(d.get(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE,one).c_str()) != 0) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
|
||||
|
||||
std::string nametmp(d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,""));
|
||||
for(unsigned long i=0;((i<ZT_MAX_NETWORK_SHORT_NAME_LENGTH)&&(i<nametmp.length()));++i)
|
||||
name[i] = (char)nametmp[i];
|
||||
// we zeroed the entire structure above and _name is ZT_MAX_NETWORK_SHORT_NAME_LENGTH+1, so it will always null-terminate
|
||||
|
||||
std::vector<std::string> activeBridgesSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES,"").c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator a(activeBridgesSplit.begin());a!=activeBridgesSplit.end();++a) {
|
||||
if (a->length() == ZT_ADDRESS_LENGTH_HEX) { // ignore empty or garbage fields
|
||||
Address tmp(*a);
|
||||
if (!tmp.isReserved()) {
|
||||
uint64_t specialist = tmp.toInt();
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & 0xffffffffffULL) == specialist) {
|
||||
specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
|
||||
specialist = 0;
|
||||
break;
|
||||
}
|
||||
et = 0;
|
||||
}
|
||||
lastrt = rt;
|
||||
}
|
||||
if (ets.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,ets.c_str())) return false;
|
||||
}
|
||||
|
||||
if (this->com) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,this->com.toString().c_str())) return false;
|
||||
}
|
||||
|
||||
std::string ab;
|
||||
for(unsigned int i=0;i<this->specialistCount;++i) {
|
||||
if ((this->specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
|
||||
if (ab.length() > 0)
|
||||
ab.push_back(',');
|
||||
ab.append(Address(this->specialists[i]).toString().c_str());
|
||||
}
|
||||
}
|
||||
if (ab.length() > 0) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,ab.c_str())) return false;
|
||||
if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
|
||||
specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string ipAddrs(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC,std::string()));
|
||||
{
|
||||
std::string v6s(d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC,std::string()));
|
||||
if (v6s.length()) {
|
||||
if (ipAddrs.length())
|
||||
ipAddrs.push_back(',');
|
||||
ipAddrs.append(v6s);
|
||||
}
|
||||
}
|
||||
std::vector<std::string> ipAddrsSplit(Utils::split(ipAddrs.c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator ipstr(ipAddrsSplit.begin());ipstr!=ipAddrsSplit.end();++ipstr) {
|
||||
InetAddress addr(*ipstr);
|
||||
switch(addr.ss_family) {
|
||||
case AF_INET:
|
||||
if ((!addr.netmaskBits())||(addr.netmaskBits() > 32))
|
||||
continue;
|
||||
break;
|
||||
case AF_INET6:
|
||||
if ((!addr.netmaskBits())||(addr.netmaskBits() > 128))
|
||||
continue;
|
||||
break;
|
||||
default: // ignore unrecognized address types or junk/empty fields
|
||||
continue;
|
||||
}
|
||||
if (!addr.isNetwork()) {
|
||||
if ((staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)&&(std::find(&(staticIps[0]),&(staticIps[staticIpCount]),addr) == &(staticIps[staticIpCount])))
|
||||
staticIps[staticIpCount++] = addr;
|
||||
}
|
||||
}
|
||||
std::sort(&(staticIps[0]),&(staticIps[staticIpCount]));
|
||||
|
||||
/* Old versions don't support gateways anyway, so ignore this in old netconfs
|
||||
std::vector<std::string> gatewaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS,"").c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator gwstr(gatewaysSplit.begin());gwstr!=gatewaysSplit.end();++gwstr) {
|
||||
InetAddress gw(*gwstr);
|
||||
if ((gw)&&(_gatewayCount < ZT_MAX_NETWORK_GATEWAYS)&&(std::find(&(_gateways[0]),&(_gateways[_gatewayCount]),gw) == &(_gateways[_gatewayCount])))
|
||||
_gateways[_gatewayCount++] = gw;
|
||||
}
|
||||
std::sort(&(_gateways[0]),&(_gateways[_gatewayCount]));
|
||||
*/
|
||||
|
||||
std::vector<std::string> relaysSplit(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_RELAYS,"").c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator r(relaysSplit.begin());r!=relaysSplit.end();++r) {
|
||||
if (r->length() >= ZT_ADDRESS_LENGTH_HEX) {
|
||||
Address zt(r->substr(0,ZT_ADDRESS_LENGTH_HEX).c_str());
|
||||
InetAddress phy[2];
|
||||
unsigned int phyCount = 0;
|
||||
const std::size_t semi(r->find(';'));
|
||||
if ((semi > ZT_ADDRESS_LENGTH_HEX)&&(semi < (r->length() - 2))) {
|
||||
std::vector<std::string> phySplit(Utils::split(r->substr(semi+1).c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator p(phySplit.begin());((p!=phySplit.end())&&(phyCount < 2));++p) {
|
||||
phy[phyCount] = InetAddress(*p);
|
||||
if (phy[phyCount])
|
||||
++phyCount;
|
||||
else phy[phyCount].zero();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t specialist = zt.toInt();
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & 0xffffffffffULL) == specialist) {
|
||||
specialists[i] |= ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
|
||||
specialist = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ((specialist)&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS))
|
||||
specialists[specialistCount++] = specialist | ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY;
|
||||
|
||||
if ((phy[0])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
|
||||
pinned[pinnedCount].zt = zt;
|
||||
pinned[pinnedCount].phy = phy[0];
|
||||
++pinnedCount;
|
||||
}
|
||||
if ((phy[1])&&(pinnedCount < ZT_MAX_NETWORK_PINNED)) {
|
||||
pinned[pinnedCount].zt = zt;
|
||||
pinned[pinnedCount].phy = phy[0];
|
||||
++pinnedCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::string> ets(Utils::split(d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES,"").c_str(),",","",""));
|
||||
for(std::vector<std::string>::const_iterator et(ets.begin());et!=ets.end();++et) {
|
||||
unsigned int et2 = Utils::hexStrToUInt(et->c_str()) & 0xffff;
|
||||
if ((ruleCount + 1) < ZT_MAX_NETWORK_RULES) {
|
||||
if (et2) {
|
||||
rules[ruleCount].t = ZT_NETWORK_RULE_MATCH_ETHERTYPE;
|
||||
rules[ruleCount].v.etherType = (uint16_t)et2;
|
||||
++ruleCount;
|
||||
}
|
||||
rules[ruleCount++].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
}
|
||||
}
|
||||
|
||||
this->com.fromString(d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP,std::string()));
|
||||
}
|
||||
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
// Then add binary blobs
|
||||
|
||||
if (this->com) {
|
||||
tmp->clear();
|
||||
this->com.serialize(*tmp);
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp)) return false;
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->capabilityCount;++i)
|
||||
this->capabilities[i].serialize(*tmp);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) return false;
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->tagCount;++i)
|
||||
this->tags[i].serialize(*tmp);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) return false;
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->specialistCount;++i)
|
||||
tmp->append((uint64_t)this->specialists[i]);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) return false;
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->routeCount;++i) {
|
||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].target))->serialize(*tmp);
|
||||
reinterpret_cast<const InetAddress *>(&(this->routes[i].via))->serialize(*tmp);
|
||||
tmp->append((uint16_t)this->routes[i].flags);
|
||||
tmp->append((uint16_t)this->routes[i].metric);
|
||||
}
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) return false;
|
||||
}
|
||||
|
||||
tmp->clear();
|
||||
for(unsigned int i=0;i<this->staticIpCount;++i)
|
||||
this->staticIps[i].serialize(*tmp);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) return false;
|
||||
}
|
||||
|
||||
if (this->ruleCount) {
|
||||
tmp->clear();
|
||||
Capability::serializeRules(*tmp,rules,ruleCount);
|
||||
if (tmp->size()) {
|
||||
if (!d.add(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) return false;
|
||||
}
|
||||
}
|
||||
|
||||
delete tmp;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
throw;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
|
||||
{
|
||||
Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||
|
||||
try {
|
||||
memset(this,0,sizeof(NetworkConfig));
|
||||
|
||||
// Fields that are always present, new or old
|
||||
this->networkId = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID,0);
|
||||
if (!this->networkId) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
this->timestamp = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TIMESTAMP,0);
|
||||
this->credentialTimeMaxDelta = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA,0);
|
||||
this->revision = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_REVISION,0);
|
||||
this->issuedTo = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO,0);
|
||||
if (!this->issuedTo) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
this->multicastLimit = (unsigned int)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT,0);
|
||||
d.get(ZT_NETWORKCONFIG_DICT_KEY_NAME,this->name,sizeof(this->name));
|
||||
|
||||
if (d.getUI(ZT_NETWORKCONFIG_DICT_KEY_VERSION,0) < 6) {
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
char tmp2[1024];
|
||||
|
||||
// Decode legacy fields if version is old
|
||||
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD))
|
||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING;
|
||||
if (d.getB(ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD))
|
||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
|
||||
this->flags |= ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION; // always enable for old-style netconf
|
||||
this->type = (d.getB(ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD,true)) ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC;
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork())
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
if (this->staticIpCount >= ZT_MAX_ZT_ASSIGNED_ADDRESSES) break;
|
||||
InetAddress ip(f);
|
||||
if (!ip.isNetwork())
|
||||
this->staticIps[this->staticIpCount++] = ip;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
this->com.fromString(tmp2);
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
unsigned int et = Utils::hexStrToUInt(f) & 0xffff;
|
||||
if ((this->ruleCount + 2) > ZT_MAX_NETWORK_RULES) break;
|
||||
if (et > 0) {
|
||||
this->rules[this->ruleCount].t = (uint8_t)ZT_NETWORK_RULE_MATCH_ETHERTYPE;
|
||||
this->rules[this->ruleCount].v.etherType = (uint16_t)et;
|
||||
++this->ruleCount;
|
||||
}
|
||||
this->rules[this->ruleCount++].t = (uint8_t)ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
}
|
||||
} else {
|
||||
this->rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
this->ruleCount = 1;
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD,tmp2,sizeof(tmp2)) > 0) {
|
||||
char *saveptr = (char *)0;
|
||||
for(char *f=Utils::stok(tmp2,",",&saveptr);(f);f=Utils::stok((char *)0,",",&saveptr)) {
|
||||
this->addSpecialist(Address(f),ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
|
||||
}
|
||||
}
|
||||
#else
|
||||
delete tmp;
|
||||
return false;
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
} else {
|
||||
// Otherwise we can use the new fields
|
||||
this->flags = d.getUI(ZT_NETWORKCONFIG_DICT_KEY_FLAGS,0);
|
||||
this->type = (ZT_VirtualNetworkType)d.getUI(ZT_NETWORKCONFIG_DICT_KEY_TYPE,(uint64_t)ZT_NETWORK_TYPE_PRIVATE);
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_COM,*tmp))
|
||||
this->com.deserialize(*tmp,0);
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES,*tmp)) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
Capability cap;
|
||||
p += cap.deserialize(*tmp,p);
|
||||
this->capabilities[this->capabilityCount++] = cap;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->capabilities[0]),&(this->capabilities[this->capabilityCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_TAGS,*tmp)) {
|
||||
try {
|
||||
unsigned int p = 0;
|
||||
while (p < tmp->size()) {
|
||||
Tag tag;
|
||||
p += tag.deserialize(*tmp,p);
|
||||
this->tags[this->tagCount++] = tag;
|
||||
}
|
||||
} catch ( ... ) {}
|
||||
std::sort(&(this->tags[0]),&(this->tags[this->tagCount]));
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while (((p + 8) <= tmp->size())&&(specialistCount < ZT_MAX_NETWORK_SPECIALISTS)) {
|
||||
this->specialists[this->specialistCount++] = tmp->at<uint64_t>(p);
|
||||
p += 8;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_ROUTES,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(routeCount < ZT_MAX_NETWORK_ROUTES)) {
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].target))->deserialize(*tmp,p);
|
||||
p += reinterpret_cast<InetAddress *>(&(this->routes[this->routeCount].via))->deserialize(*tmp,p);
|
||||
this->routes[this->routeCount].flags = tmp->at<uint16_t>(p); p += 2;
|
||||
this->routes[this->routeCount].metric = tmp->at<uint16_t>(p); p += 2;
|
||||
++this->routeCount;
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS,*tmp)) {
|
||||
unsigned int p = 0;
|
||||
while ((p < tmp->size())&&(staticIpCount < ZT_MAX_ZT_ASSIGNED_ADDRESSES)) {
|
||||
p += this->staticIps[this->staticIpCount++].deserialize(*tmp,p);
|
||||
}
|
||||
}
|
||||
|
||||
if (d.get(ZT_NETWORKCONFIG_DICT_KEY_RULES,*tmp)) {
|
||||
this->ruleCount = 0;
|
||||
unsigned int p = 0;
|
||||
Capability::deserializeRules(*tmp,p,this->rules,this->ruleCount,ZT_MAX_NETWORK_RULES);
|
||||
}
|
||||
}
|
||||
|
||||
//printf("~~~\n%s\n~~~\n",d.data());
|
||||
//dump();
|
||||
//printf("~~~\n");
|
||||
|
||||
delete tmp;
|
||||
return true;
|
||||
} catch ( ... ) {
|
||||
delete tmp;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -35,51 +35,26 @@
|
||||
#include "MulticastGroup.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Capability.hpp"
|
||||
#include "Tag.hpp"
|
||||
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
#include "Dictionary.hpp"
|
||||
#include "Identity.hpp"
|
||||
|
||||
/**
|
||||
* Default maximum time delta for COMs, tags, and capabilities
|
||||
*
|
||||
* The current value is two hours, providing ample time for a controller to
|
||||
* experience fail-over, etc.
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MAX_MAX_DELTA 7200000ULL
|
||||
|
||||
/**
|
||||
* Default minimum credential TTL and maxDelta for COM timestamps
|
||||
*
|
||||
* This is just slightly over three minutes and provides three retries for
|
||||
* all currently online members to refresh.
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_DEFAULT_CREDENTIAL_TIME_MIN_MAX_DELTA 185000ULL
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Flag: allow passive bridging (experimental)
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0000000000000001ULL
|
||||
#define ZT_NETWORKCONFIG_FLAG_ALLOW_PASSIVE_BRIDGING 0x0001
|
||||
|
||||
/**
|
||||
* Flag: enable broadcast
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0000000000000002ULL
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST 0x0002
|
||||
|
||||
/**
|
||||
* Flag: enable IPv6 NDP emulation for certain V6 address patterns
|
||||
* Device is a network preferred relay
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION 0x0000000000000004ULL
|
||||
|
||||
/**
|
||||
* Flag: result of unrecognized MATCH entries in a rules table: match if set, no-match if clear
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_RULES_RESULT_OF_UNSUPPORTED_MATCH 0x0000000000000008ULL
|
||||
|
||||
/**
|
||||
* Flag: disable frame compression
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION 0x0000000000000010ULL
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY 0x0000010000000000ULL
|
||||
|
||||
/**
|
||||
* Device is an active bridge
|
||||
@@ -87,62 +62,24 @@
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE 0x0000020000000000ULL
|
||||
|
||||
/**
|
||||
* Anchors are stable devices on this network that can cache multicast info, etc.
|
||||
* An anchor is a device that is willing to be one and has been online/stable for a long time on this network
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR 0x0000040000000000ULL
|
||||
|
||||
/**
|
||||
* Device can send CIRCUIT_TESTs for this network
|
||||
*/
|
||||
#define ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER 0x0000080000000000ULL
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
// Dictionary capacity needed for max size network config
|
||||
#define ZT_NETWORKCONFIG_DICT_CAPACITY (4096 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS))
|
||||
|
||||
// Dictionary capacity needed for max size network meta-data
|
||||
#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024
|
||||
|
||||
// Network config version
|
||||
#define ZT_NETWORKCONFIG_VERSION 7
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
// Fields for meta-data sent with network config requests
|
||||
|
||||
// Network config version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION "v"
|
||||
// Protocol version (see Packet.hpp)
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_PROTOCOL_VERSION "pv"
|
||||
// Software vendor
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_VENDOR "vend"
|
||||
// Software major version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MAJOR_VERSION "majv"
|
||||
// Software minor version
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_MINOR_VERSION "minv"
|
||||
// Software revision
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_REVISION "revv"
|
||||
// Rules engine revision
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_RULES_ENGINE_REV "revr"
|
||||
// Maximum number of rules per network this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_RULES "mr"
|
||||
// Maximum number of capabilities this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_CAPABILITIES "mc"
|
||||
// Maximum number of rules per capability this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_CAPABILITY_RULES "mcr"
|
||||
// Maximum number of tags this node can accept
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_MAX_NETWORK_TAGS "mt"
|
||||
// Network join authorization token (if any)
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_AUTH "a"
|
||||
// Network configuration meta-data flags
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_FLAGS "f"
|
||||
// Relay policy for this node
|
||||
#define ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_NODE_RELAY_POLICY "rp"
|
||||
|
||||
// These dictionary keys are short so they don't take up much room.
|
||||
// By convention we use upper case for binary blobs, but it doesn't really matter.
|
||||
// These dictionary keys are short so they don't take up much room in
|
||||
// netconf response packets.
|
||||
|
||||
// network config version
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_VERSION "v"
|
||||
// integer(hex)[,integer(hex),...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES "et"
|
||||
// network ID
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NETWORK_ID "nwid"
|
||||
// integer(hex)
|
||||
@@ -151,57 +88,34 @@ namespace ZeroTier {
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_REVISION "r"
|
||||
// address of member
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ISSUED_TO "id"
|
||||
// flags(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_FLAGS "f"
|
||||
// integer(hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_MULTICAST_LIMIT "ml"
|
||||
// network type (hex)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TYPE "t"
|
||||
// 0/1
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE "p"
|
||||
// text
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_NAME "n"
|
||||
// credential time max delta in ms
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CREDENTIAL_TIME_MAX_DELTA "ctmd"
|
||||
// binary serialized certificate of membership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_COM "C"
|
||||
// specialists (binary array of uint64_t)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SPECIALISTS "S"
|
||||
// routes (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ROUTES "RT"
|
||||
// static IPs (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_STATIC_IPS "I"
|
||||
// rules (binary blob)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RULES "R"
|
||||
// capabilities (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CAPABILITIES "CAP"
|
||||
// tags (binary blobs)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_TAGS "TAG"
|
||||
// curve25519 signature
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE "C25519"
|
||||
|
||||
// Legacy fields -- these are obsoleted but are included when older clients query
|
||||
|
||||
// boolean (now a flag)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING_OLD "pb"
|
||||
// boolean (now a flag)
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST_OLD "eb"
|
||||
// text
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_DESC "d"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC_OLD "v4s"
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV4_STATIC "v4s"
|
||||
// IP/bits[,IP/bits,...]
|
||||
// Note that IPs that end in all zeroes are routes with no assignment in them.
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC_OLD "v6s"
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_IPV6_STATIC "v6s"
|
||||
// serialized CertificateOfMembership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP "com"
|
||||
// 0/1
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_PRIVATE_OLD "p"
|
||||
// integer(hex)[,integer(hex),...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOWED_ETHERNET_TYPES_OLD "et"
|
||||
// string-serialized CertificateOfMembership
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_CERTIFICATE_OF_MEMBERSHIP_OLD "com"
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ENABLE_BROADCAST "eb"
|
||||
// 0/1
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ALLOW_PASSIVE_BRIDGING "pb"
|
||||
// node[,node,...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES_OLD "ab"
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_ACTIVE_BRIDGES "ab"
|
||||
// node;IP/port[,node;IP/port]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS_OLD "rl"
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_RELAYS "rl"
|
||||
// IP/metric[,IP/metric,...]
|
||||
#define ZT_NETWORKCONFIG_DICT_KEY_GATEWAYS "gw"
|
||||
|
||||
// End legacy fields
|
||||
#endif // ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
|
||||
/**
|
||||
* Network configuration received from network controller nodes
|
||||
@@ -212,6 +126,58 @@ namespace ZeroTier {
|
||||
class NetworkConfig
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Network preferred relay with optional physical endpoint addresses
|
||||
*
|
||||
* This is used by the convenience relays() method.
|
||||
*/
|
||||
struct Relay
|
||||
{
|
||||
Address address;
|
||||
InetAddress phy4,phy6;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an instance of a NetworkConfig for the test network ID
|
||||
*
|
||||
* The test network ID is defined as ZT_TEST_NETWORK_ID. This is a
|
||||
* "fake" network with no real controller and default options.
|
||||
*
|
||||
* @param self This node's ZT address
|
||||
* @return Configuration for test network ID
|
||||
*/
|
||||
static inline NetworkConfig createTestNetworkConfig(const Address &self)
|
||||
{
|
||||
NetworkConfig nc;
|
||||
|
||||
nc.networkId = ZT_TEST_NETWORK_ID;
|
||||
nc.timestamp = 1;
|
||||
nc.revision = 1;
|
||||
nc.issuedTo = self;
|
||||
nc.multicastLimit = ZT_MULTICAST_DEFAULT_LIMIT;
|
||||
nc.flags = ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST;
|
||||
nc.type = ZT_NETWORK_TYPE_PUBLIC;
|
||||
|
||||
nc.rules[0].t = ZT_NETWORK_RULE_ACTION_ACCEPT;
|
||||
nc.ruleCount = 1;
|
||||
|
||||
Utils::snprintf(nc.name,sizeof(nc.name),"ZT_TEST_NETWORK");
|
||||
|
||||
// Make up a V4 IP from 'self' in the 10.0.0.0/8 range -- no
|
||||
// guarantee of uniqueness but collisions are unlikely.
|
||||
uint32_t ip = (uint32_t)((self.toInt() & 0x00ffffff) | 0x0a000000); // 10.x.x.x
|
||||
if ((ip & 0x000000ff) == 0x000000ff) ip ^= 0x00000001; // but not ending in .255
|
||||
if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0
|
||||
nc.staticIps[0] = InetAddress(Utils::hton(ip),8);
|
||||
|
||||
// Assign an RFC4193-compliant IPv6 address -- will never collide
|
||||
nc.staticIps[1] = InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt());
|
||||
|
||||
nc.staticIpCount = 2;
|
||||
|
||||
return nc;
|
||||
}
|
||||
|
||||
NetworkConfig()
|
||||
{
|
||||
memset(this,0,sizeof(NetworkConfig));
|
||||
@@ -229,21 +195,24 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Write this network config to a dictionary for transport
|
||||
*
|
||||
* @param d Dictionary
|
||||
* @param includeLegacy If true, include legacy fields for old node versions
|
||||
* @return True if dictionary was successfully created, false if e.g. overflow
|
||||
* @param etherType Ethernet frame type to check
|
||||
* @return True if allowed on this network
|
||||
*/
|
||||
bool toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,bool includeLegacy) const;
|
||||
|
||||
/**
|
||||
* Read this network config from a dictionary
|
||||
*
|
||||
* @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
|
||||
* @return True if dictionary was valid and network config successfully initialized
|
||||
*/
|
||||
bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
|
||||
inline bool permitsEtherType(unsigned int etherType) const
|
||||
{
|
||||
unsigned int et = 0;
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
ZT_VirtualNetworkRuleType rt = (ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f);
|
||||
if (rt == ZT_NETWORK_RULE_MATCH_ETHERTYPE) {
|
||||
et = rules[i].v.etherType;
|
||||
} else if (rt == ZT_NETWORK_RULE_ACTION_ACCEPT) {
|
||||
if ((!et)||(et == etherType))
|
||||
return true;
|
||||
et = 0;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if passive bridging is allowed (experimental)
|
||||
@@ -255,16 +224,6 @@ public:
|
||||
*/
|
||||
inline bool enableBroadcast() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_BROADCAST) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if IPv6 NDP emulation should be allowed for certain "magic" IPv6 address patterns
|
||||
*/
|
||||
inline bool ndpEmulation() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_ENABLE_IPV6_NDP_EMULATION) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if frames should not be compressed
|
||||
*/
|
||||
inline bool disableCompression() const throw() { return ((this->flags & ZT_NETWORKCONFIG_FLAG_DISABLE_COMPRESSION) != 0); }
|
||||
|
||||
/**
|
||||
* @return Network type is public (no access control)
|
||||
*/
|
||||
@@ -302,16 +261,40 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @param a Address to check
|
||||
* @return True if address is an anchor
|
||||
* Get pinned physical address for a given ZeroTier address, if any
|
||||
*
|
||||
* @param zt ZeroTier address
|
||||
* @param af Address family (e.g. AF_INET) or 0 for the first we find of any type
|
||||
* @return Physical address, if any
|
||||
*/
|
||||
inline bool isAnchor(const Address &a) const
|
||||
inline InetAddress findPinnedAddress(const Address &zt,unsigned int af) const
|
||||
{
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
|
||||
return true;
|
||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
||||
if (pinned[i].zt == zt) {
|
||||
if ((af == 0)||((unsigned int)pinned[i].phy.ss_family == af))
|
||||
return pinned[i].phy;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return InetAddress();
|
||||
}
|
||||
|
||||
/**
|
||||
* This gets network preferred relays with their static physical address if one is defined
|
||||
*
|
||||
* @return Network-preferred relays for this network (if none, only roots will be used)
|
||||
*/
|
||||
inline std::vector<Relay> relays() const
|
||||
{
|
||||
std::vector<Relay> r;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
|
||||
r.push_back(Relay());
|
||||
r.back().address = specialists[i];
|
||||
r.back().phy4 = findPinnedAddress(r.back().address,AF_INET);
|
||||
r.back().phy6 = findPinnedAddress(r.back().address,AF_INET6);
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -330,15 +313,30 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @param byPeer Address to check
|
||||
* @return True if this peer is allowed to do circuit tests on this network (controller is always true)
|
||||
* Iterate through relays efficiently
|
||||
*
|
||||
* @param ptr Value-result parameter -- start by initializing with zero, then call until return is null
|
||||
* @return Address of relay or NULL if no more
|
||||
*/
|
||||
inline bool circuitTestingAllowed(const Address &byPeer) const
|
||||
Address nextRelay(unsigned int &ptr) const
|
||||
{
|
||||
while (ptr < specialistCount) {
|
||||
if ((specialists[ptr] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0) {
|
||||
return Address(specialists[ptr]);
|
||||
}
|
||||
++ptr;
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zt ZeroTier address
|
||||
* @return True if this address is a relay
|
||||
*/
|
||||
bool isRelay(const Address &zt) const
|
||||
{
|
||||
if (byPeer.toInt() == ((networkId >> 24) & 0xffffffffffULL))
|
||||
return true;
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((byPeer == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_CIRCUIT_TESTER) != 0))
|
||||
if ((zt == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_NETWORK_PREFERRED_RELAY) != 0))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -352,56 +350,270 @@ public:
|
||||
inline bool operator==(const NetworkConfig &nc) const { return (memcmp(this,&nc,sizeof(NetworkConfig)) == 0); }
|
||||
inline bool operator!=(const NetworkConfig &nc) const { return (!(*this == nc)); }
|
||||
|
||||
/**
|
||||
* Add a specialist or mask flags if already present
|
||||
*
|
||||
* This masks the existing flags if the specialist is already here or adds
|
||||
* it otherwise.
|
||||
*
|
||||
* @param a Address of specialist
|
||||
* @param f Flags (OR of specialist role/type flags)
|
||||
* @return True if successfully masked or added
|
||||
*/
|
||||
inline bool addSpecialist(const Address &a,const uint64_t f)
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
const uint64_t aint = a.toInt();
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
if ((specialists[i] & 0xffffffffffULL) == aint) {
|
||||
specialists[i] |= f;
|
||||
return true;
|
||||
b.append((uint16_t)1); // version
|
||||
|
||||
b.append((uint64_t)networkId);
|
||||
b.append((uint64_t)timestamp);
|
||||
b.append((uint64_t)revision);
|
||||
issuedTo.appendTo(b);
|
||||
b.append((uint32_t)multicastLimit);
|
||||
b.append((uint32_t)flags);
|
||||
b.append((uint8_t)type);
|
||||
|
||||
unsigned int nl = (unsigned int)strlen(name);
|
||||
if (nl > 255) nl = 255; // sanity check
|
||||
b.append((uint8_t)nl);
|
||||
b.append((const void *)name,nl);
|
||||
|
||||
b.append((uint16_t)specialistCount);
|
||||
for(unsigned int i=0;i<specialistCount;++i)
|
||||
b.append((uint64_t)specialists[i]);
|
||||
|
||||
b.append((uint16_t)routeCount);
|
||||
for(unsigned int i=0;i<routeCount;++i) {
|
||||
reinterpret_cast<const InetAddress *>(&(routes[i].target))->serialize(b);
|
||||
reinterpret_cast<const InetAddress *>(&(routes[i].via))->serialize(b);
|
||||
}
|
||||
|
||||
b.append((uint16_t)staticIpCount);
|
||||
for(unsigned int i=0;i<staticIpCount;++i)
|
||||
staticIps[i].serialize(b);
|
||||
|
||||
b.append((uint16_t)pinnedCount);
|
||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
||||
pinned[i].zt.appendTo(b);
|
||||
pinned[i].phy.serialize(b);
|
||||
}
|
||||
|
||||
b.append((uint16_t)ruleCount);
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
b.append((uint8_t)rules[i].t);
|
||||
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
|
||||
//case ZT_NETWORK_RULE_ACTION_DROP:
|
||||
//case ZT_NETWORK_RULE_ACTION_ACCEPT:
|
||||
default:
|
||||
b.append((uint8_t)0);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS:
|
||||
b.append((uint8_t)5);
|
||||
Address(rules[i].v.zt).appendTo(b);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
||||
b.append((uint8_t)2);
|
||||
b.append((uint16_t)rules[i].v.vlanId);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.vlanPcp);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.vlanDei);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
||||
b.append((uint8_t)2);
|
||||
b.append((uint16_t)rules[i].v.etherType);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
||||
b.append((uint8_t)6);
|
||||
b.append(rules[i].v.mac,6);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
||||
b.append((uint8_t)5);
|
||||
b.append(&(rules[i].v.ipv4.ip),4);
|
||||
b.append((uint8_t)rules[i].v.ipv4.mask);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
||||
b.append((uint8_t)17);
|
||||
b.append(rules[i].v.ipv6.ip,16);
|
||||
b.append((uint8_t)rules[i].v.ipv6.mask);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.ipTos);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
||||
b.append((uint8_t)1);
|
||||
b.append((uint8_t)rules[i].v.ipProtocol);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
||||
b.append((uint8_t)4);
|
||||
b.append((uint16_t)rules[i].v.port[0]);
|
||||
b.append((uint16_t)rules[i].v.port[1]);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
||||
b.append((uint8_t)8);
|
||||
b.append((uint64_t)rules[i].v.characteristics);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
||||
b.append((uint8_t)4);
|
||||
b.append((uint16_t)rules[i].v.frameSize[0]);
|
||||
b.append((uint16_t)rules[i].v.frameSize[1]);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
|
||||
b.append((uint8_t)8);
|
||||
b.append((uint32_t)rules[i].v.tcpseq[0]);
|
||||
b.append((uint32_t)rules[i].v.tcpseq[1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (specialistCount < ZT_MAX_NETWORK_SPECIALISTS) {
|
||||
specialists[specialistCount++] = f | aint;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
this->com.serialize(b);
|
||||
|
||||
b.append((uint16_t)0); // extended bytes, currently 0 since unused
|
||||
}
|
||||
|
||||
const Capability *capability(const uint32_t id) const
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
for(unsigned int i=0;i<capabilityCount;++i) {
|
||||
if (capabilities[i].id() == id)
|
||||
return &(capabilities[i]);
|
||||
memset(this,0,sizeof(NetworkConfig));
|
||||
|
||||
unsigned int p = startAt;
|
||||
|
||||
if (b.template at<uint16_t>(p) != 1)
|
||||
throw std::invalid_argument("unrecognized version");
|
||||
p += 2;
|
||||
|
||||
networkId = b.template at<uint64_t>(p); p += 8;
|
||||
timestamp = b.template at<uint64_t>(p); p += 8;
|
||||
revision = b.template at<uint64_t>(p); p += 8;
|
||||
issuedTo.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
|
||||
multicastLimit = (unsigned int)b.template at<uint32_t>(p); p += 4;
|
||||
flags = (unsigned int)b.template at<uint32_t>(p); p += 4;
|
||||
type = (ZT_VirtualNetworkType)b[p++];
|
||||
|
||||
unsigned int nl = (unsigned int)b[p++];
|
||||
memcpy(this->name,b.field(p,nl),std::min(nl,(unsigned int)ZT_MAX_NETWORK_SHORT_NAME_LENGTH));
|
||||
p += nl;
|
||||
// _name will always be null terminated since field size is ZT_MAX_NETWORK_SHORT_NAME_LENGTH + 1
|
||||
|
||||
specialistCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
|
||||
if (specialistCount > ZT_MAX_NETWORK_SPECIALISTS)
|
||||
throw std::invalid_argument("overflow (specialists)");
|
||||
for(unsigned int i=0;i<specialistCount;++i) {
|
||||
specialists[i] = b.template at<uint64_t>(p); p += 8;
|
||||
}
|
||||
return (Capability *)0;
|
||||
|
||||
routeCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
|
||||
if (routeCount > ZT_MAX_NETWORK_ROUTES)
|
||||
throw std::invalid_argument("overflow (routes)");
|
||||
for(unsigned int i=0;i<routeCount;++i) {
|
||||
p += reinterpret_cast<InetAddress *>(&(routes[i].target))->deserialize(b,p);
|
||||
p += reinterpret_cast<InetAddress *>(&(routes[i].via))->deserialize(b,p);
|
||||
}
|
||||
|
||||
staticIpCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
|
||||
if (staticIpCount > ZT_MAX_ZT_ASSIGNED_ADDRESSES)
|
||||
throw std::invalid_argument("overflow (static IPs)");
|
||||
for(unsigned int i=0;i<staticIpCount;++i) {
|
||||
p += staticIps[i].deserialize(b,p);
|
||||
}
|
||||
|
||||
pinnedCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
|
||||
if (pinnedCount > ZT_MAX_NETWORK_PINNED)
|
||||
throw std::invalid_argument("overflow (static addresses)");
|
||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
||||
pinned[i].zt.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
|
||||
p += pinned[i].phy.deserialize(b,p);
|
||||
}
|
||||
|
||||
ruleCount = (unsigned int)b.template at<uint16_t>(p); p += 2;
|
||||
if (ruleCount > ZT_MAX_NETWORK_RULES)
|
||||
throw std::invalid_argument("overflow (rules)");
|
||||
for(unsigned int i=0;i<ruleCount;++i) {
|
||||
rules[i].t = (uint8_t)b[p++];
|
||||
unsigned int rlen = (unsigned int)b[p++];
|
||||
switch((ZT_VirtualNetworkRuleType)(rules[i].t & 0x7f)) {
|
||||
//case ZT_NETWORK_RULE_ACTION_DROP:
|
||||
//case ZT_NETWORK_RULE_ACTION_ACCEPT:
|
||||
default:
|
||||
break;
|
||||
case ZT_NETWORK_RULE_ACTION_TEE:
|
||||
case ZT_NETWORK_RULE_ACTION_REDIRECT:
|
||||
case ZT_NETWORK_RULE_MATCH_SOURCE_ZEROTIER_ADDRESS:
|
||||
case ZT_NETWORK_RULE_MATCH_DEST_ZEROTIER_ADDRESS: {
|
||||
Address tmp;
|
||||
tmp.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
|
||||
rules[i].v.zt = tmp.toInt();
|
||||
} break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_ID:
|
||||
rules[i].v.vlanId = b.template at<uint16_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_PCP:
|
||||
rules[i].v.vlanPcp = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_VLAN_DEI:
|
||||
rules[i].v.vlanDei = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_ETHERTYPE:
|
||||
rules[i].v.etherType = b.template at<uint16_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_MAC_DEST:
|
||||
memcpy(rules[i].v.mac,b.field(p,6),6);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV4_DEST:
|
||||
memcpy(&(rules[i].v.ipv4.ip),b.field(p,4),4);
|
||||
rules[i].v.ipv4.mask = (uint8_t)b[p+4];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_SOURCE:
|
||||
case ZT_NETWORK_RULE_MATCH_IPV6_DEST:
|
||||
memcpy(rules[i].v.ipv6.ip,b.field(p,16),16);
|
||||
rules[i].v.ipv6.mask = (uint8_t)b[p+16];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_TOS:
|
||||
rules[i].v.ipTos = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_PROTOCOL:
|
||||
rules[i].v.ipProtocol = (uint8_t)b[p];
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_IP_SOURCE_PORT_RANGE:
|
||||
case ZT_NETWORK_RULE_MATCH_IP_DEST_PORT_RANGE:
|
||||
rules[i].v.port[0] = b.template at<uint16_t>(p);
|
||||
rules[i].v.port[1] = b.template at<uint16_t>(p+2);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_CHARACTERISTICS:
|
||||
rules[i].v.characteristics = b.template at<uint64_t>(p);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_FRAME_SIZE_RANGE:
|
||||
rules[i].v.frameSize[0] = b.template at<uint16_t>(p);
|
||||
rules[i].v.frameSize[1] = b.template at<uint16_t>(p+2);
|
||||
break;
|
||||
case ZT_NETWORK_RULE_MATCH_TCP_RELATIVE_SEQUENCE_NUMBER_RANGE:
|
||||
rules[i].v.tcpseq[0] = b.template at<uint32_t>(p);
|
||||
rules[i].v.tcpseq[1] = b.template at<uint32_t>(p + 4);
|
||||
break;
|
||||
}
|
||||
p += rlen;
|
||||
}
|
||||
|
||||
p += this->com.deserialize(b,p);
|
||||
|
||||
p += b.template at<uint16_t>(p) + 2;
|
||||
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
const Tag *tag(const uint32_t id) const
|
||||
{
|
||||
for(unsigned int i=0;i<tagCount;++i) {
|
||||
if (tags[i].id() == id)
|
||||
return &(tags[i]);
|
||||
}
|
||||
return (Tag *)0;
|
||||
}
|
||||
#ifdef ZT_SUPPORT_OLD_STYLE_NETCONF
|
||||
void fromDictionary(const char *ds,unsigned int dslen);
|
||||
#endif
|
||||
|
||||
/*
|
||||
inline void dump() const
|
||||
{
|
||||
printf("networkId==%.16llx\n",networkId);
|
||||
printf("timestamp==%llu\n",timestamp);
|
||||
printf("credentialTimeMaxDelta==%llu\n",credentialTimeMaxDelta);
|
||||
printf("revision==%llu\n",revision);
|
||||
printf("issuedTo==%.10llx\n",issuedTo.toInt());
|
||||
printf("multicastLimit==%u\n",multicastLimit);
|
||||
@@ -411,14 +623,17 @@ public:
|
||||
printf(" specialists[%u]==%.16llx\n",i,specialists[i]);
|
||||
printf("routeCount==%u\n",routeCount);
|
||||
for(unsigned int i=0;i<routeCount;++i) {
|
||||
printf(" routes[i].target==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].target))->toString().c_str());
|
||||
printf(" routes[i].via==%s\n",reinterpret_cast<const InetAddress *>(&(routes[i].via))->toIpString().c_str());
|
||||
printf(" routes[i].flags==%.4x\n",(unsigned int)routes[i].flags);
|
||||
printf(" routes[i].metric==%u\n",(unsigned int)routes[i].metric);
|
||||
printf(" routes[i].target==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(routes[i].target))->toString().c_str());
|
||||
printf(" routes[i].via==%s\n",reinterpret_cast<const struct sockaddr_storage *>(&(routes[i].via))->toString().c_str());
|
||||
}
|
||||
printf("staticIpCount==%u\n",staticIpCount);
|
||||
for(unsigned int i=0;i<staticIpCount;++i)
|
||||
printf(" staticIps[i]==%s\n",staticIps[i].toString().c_str());
|
||||
printf("pinnedCount==%u\n",pinnedCount);
|
||||
for(unsigned int i=0;i<pinnedCount;++i) {
|
||||
printf(" pinned[i].zt==%s\n",pinned[i].zt->toString().c_str());
|
||||
printf(" pinned[i].phy==%s\n",pinned[i].zt->toString().c_str());
|
||||
}
|
||||
printf("ruleCount==%u\n",ruleCount);
|
||||
printf("name==%s\n",name);
|
||||
printf("com==%s\n",com.toString().c_str());
|
||||
@@ -435,11 +650,6 @@ public:
|
||||
*/
|
||||
uint64_t timestamp;
|
||||
|
||||
/**
|
||||
* Max difference between timestamp and tag/capability timestamp
|
||||
*/
|
||||
uint64_t credentialTimeMaxDelta;
|
||||
|
||||
/**
|
||||
* Controller-side revision counter for this configuration
|
||||
*/
|
||||
@@ -450,16 +660,16 @@ public:
|
||||
*/
|
||||
Address issuedTo;
|
||||
|
||||
/**
|
||||
* Flags (64-bit)
|
||||
*/
|
||||
uint64_t flags;
|
||||
|
||||
/**
|
||||
* Maximum number of recipients per multicast (not including active bridges)
|
||||
*/
|
||||
unsigned int multicastLimit;
|
||||
|
||||
/**
|
||||
* Flags (32-bit)
|
||||
*/
|
||||
unsigned int flags;
|
||||
|
||||
/**
|
||||
* Number of specialists
|
||||
*/
|
||||
@@ -485,16 +695,6 @@ public:
|
||||
*/
|
||||
unsigned int ruleCount;
|
||||
|
||||
/**
|
||||
* Number of capabilities
|
||||
*/
|
||||
unsigned int capabilityCount;
|
||||
|
||||
/**
|
||||
* Number of tags
|
||||
*/
|
||||
unsigned int tagCount;
|
||||
|
||||
/**
|
||||
* Specialist devices
|
||||
*
|
||||
@@ -514,20 +714,21 @@ public:
|
||||
InetAddress staticIps[ZT_MAX_ZT_ASSIGNED_ADDRESSES];
|
||||
|
||||
/**
|
||||
* Base network rules
|
||||
* Pinned devices with physical address hints
|
||||
*
|
||||
* These can be used to specify a physical address where a given device
|
||||
* can be reached. It's usually used with network relays (specialists).
|
||||
*/
|
||||
struct {
|
||||
Address zt;
|
||||
InetAddress phy;
|
||||
} pinned[ZT_MAX_NETWORK_PINNED];
|
||||
|
||||
/**
|
||||
* Rules table
|
||||
*/
|
||||
ZT_VirtualNetworkRule rules[ZT_MAX_NETWORK_RULES];
|
||||
|
||||
/**
|
||||
* Capabilities for this node on this network, in ascending order of capability ID
|
||||
*/
|
||||
Capability capabilities[ZT_MAX_NETWORK_CAPABILITIES];
|
||||
|
||||
/**
|
||||
* Tags for this node on this network, in ascending order of tag ID
|
||||
*/
|
||||
Tag tags[ZT_MAX_NETWORK_TAGS];
|
||||
|
||||
/**
|
||||
* Network type (currently just public or private)
|
||||
*/
|
||||
|
||||
@@ -22,15 +22,15 @@
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "Dictionary.hpp"
|
||||
#include "NetworkConfig.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "NetworkConfigRequestMetaData.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class RuntimeEnvironment;
|
||||
class Identity;
|
||||
class Address;
|
||||
struct InetAddress;
|
||||
|
||||
/**
|
||||
* Interface for network controller implementations
|
||||
@@ -67,16 +67,16 @@ public:
|
||||
* @param identity Originating peer ZeroTier identity
|
||||
* @param nwid 64-bit network ID
|
||||
* @param metaData Meta-data bundled with request (if any)
|
||||
* @param nc NetworkConfig to fill with results
|
||||
* @return Returns NETCONF_QUERY_OK if result 'nc' is valid, or an error code on error
|
||||
* @param result Buffer to receive serialized network configuration data (any existing data in buffer is preserved)
|
||||
* @return Returns NETCONF_QUERY_OK if result dictionary is valid, or an error code on error
|
||||
*/
|
||||
virtual NetworkController::ResultCode doNetworkConfigRequest(
|
||||
const InetAddress &fromAddr,
|
||||
const Identity &signingId,
|
||||
const Identity &identity,
|
||||
uint64_t nwid,
|
||||
const Dictionary<ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY> &metaData,
|
||||
NetworkConfig &nc) = 0;
|
||||
const NetworkConfigRequestMetaData &metaData,
|
||||
Buffer<8194> &result) = 0;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "Identity.hpp"
|
||||
#include "SelfAwareness.hpp"
|
||||
#include "Cluster.hpp"
|
||||
#include "DeferredPackets.hpp"
|
||||
|
||||
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
|
||||
|
||||
@@ -71,14 +72,10 @@ Node::Node(
|
||||
_prngStreamPtr(0),
|
||||
_now(now),
|
||||
_lastPingCheck(0),
|
||||
_lastHousekeepingRun(0),
|
||||
_relayPolicy(ZT_RELAY_POLICY_TRUSTED)
|
||||
_lastHousekeepingRun(0)
|
||||
{
|
||||
_online = false;
|
||||
|
||||
memset(_expectingRepliesToBucketPtr,0,sizeof(_expectingRepliesToBucketPtr));
|
||||
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
|
||||
|
||||
// Use Salsa20 alone as a high-quality non-crypto PRNG
|
||||
{
|
||||
char foo[32];
|
||||
@@ -111,7 +108,9 @@ Node::Node(
|
||||
RR->mc = new Multicaster(RR);
|
||||
RR->topology = new Topology(RR);
|
||||
RR->sa = new SelfAwareness(RR);
|
||||
RR->dp = new DeferredPackets(RR);
|
||||
} catch ( ... ) {
|
||||
delete RR->dp;
|
||||
delete RR->sa;
|
||||
delete RR->topology;
|
||||
delete RR->mc;
|
||||
@@ -119,9 +118,6 @@ Node::Node(
|
||||
throw;
|
||||
}
|
||||
|
||||
if (RR->topology->amRoot())
|
||||
_relayPolicy = ZT_RELAY_POLICY_ALWAYS;
|
||||
|
||||
postEvent(ZT_EVENT_UP);
|
||||
}
|
||||
|
||||
@@ -131,11 +127,12 @@ Node::~Node()
|
||||
|
||||
_networks.clear(); // ensure that networks are destroyed before shutdow
|
||||
|
||||
RR->dpEnabled = 0;
|
||||
delete RR->dp;
|
||||
delete RR->sa;
|
||||
delete RR->topology;
|
||||
delete RR->mc;
|
||||
delete RR->sw;
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
delete RR->cluster;
|
||||
#endif
|
||||
@@ -176,10 +173,11 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
|
||||
class _PingPeersThatNeedPing
|
||||
{
|
||||
public:
|
||||
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now) :
|
||||
_PingPeersThatNeedPing(const RuntimeEnvironment *renv,uint64_t now,const std::vector<NetworkConfig::Relay> &relays) :
|
||||
lastReceiveFromUpstream(0),
|
||||
RR(renv),
|
||||
_now(now),
|
||||
_relays(relays),
|
||||
_world(RR->topology->world())
|
||||
{
|
||||
}
|
||||
@@ -210,6 +208,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
if (!upstream) {
|
||||
// If I am a root server, only ping other root servers -- roots don't ping "down"
|
||||
// since that would just be a waste of bandwidth and could potentially cause route
|
||||
// flapping in Cluster mode.
|
||||
if (RR->topology->amRoot())
|
||||
return;
|
||||
|
||||
// Check for network preferred relays, also considered 'upstream' and thus always
|
||||
// pinged to keep links up. If they have stable addresses we will try them there.
|
||||
for(std::vector<NetworkConfig::Relay>::const_iterator r(_relays.begin());r!=_relays.end();++r) {
|
||||
if (r->address == p->address()) {
|
||||
stableEndpoint4 = r->phy4;
|
||||
stableEndpoint6 = r->phy6;
|
||||
upstream = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (upstream) {
|
||||
// "Upstream" devices are roots and relays and get special treatment -- they stay alive
|
||||
// forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
|
||||
@@ -237,19 +254,20 @@ public:
|
||||
// way whatsoever. This will e.g. find network preferred relays that lack
|
||||
// stable endpoints by using root servers.
|
||||
Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||
RR->sw->send(outp,true);
|
||||
RR->sw->send(outp,true,0);
|
||||
}
|
||||
|
||||
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
|
||||
} else if (p->isActive(_now)) {
|
||||
} else if (p->activelyTransferringFrames(_now)) {
|
||||
// Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
|
||||
p->doPingAndKeepalive(_now,-1);
|
||||
p->doPingAndKeepalive(_now,0);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const RuntimeEnvironment *RR;
|
||||
uint64_t _now;
|
||||
const std::vector<NetworkConfig::Relay> &_relays;
|
||||
World _world;
|
||||
};
|
||||
|
||||
@@ -265,20 +283,27 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
|
||||
_lastPingCheck = now;
|
||||
|
||||
// Get relays and networks that need config without leaving the mutex locked
|
||||
std::vector< NetworkConfig::Relay > networkRelays;
|
||||
std::vector< SharedPtr<Network> > needConfig;
|
||||
{
|
||||
Mutex::Lock _l(_networks_m);
|
||||
for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
|
||||
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
|
||||
if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig())) {
|
||||
needConfig.push_back(n->second);
|
||||
n->second->sendUpdatesToMembers();
|
||||
}
|
||||
if (n->second->hasConfig()) {
|
||||
std::vector<NetworkConfig::Relay> r(n->second->config().relays());
|
||||
networkRelays.insert(networkRelays.end(),r.begin(),r.end());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request updated configuration for networks that need it
|
||||
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
|
||||
(*n)->requestConfiguration();
|
||||
|
||||
// Do pings and keepalives
|
||||
_PingPeersThatNeedPing pfunc(RR,now);
|
||||
_PingPeersThatNeedPing pfunc(RR,now,networkRelays);
|
||||
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
|
||||
|
||||
// Update online status, post status change as event
|
||||
@@ -324,12 +349,6 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
|
||||
return ZT_RESULT_OK;
|
||||
}
|
||||
|
||||
ZT_ResultCode Node::setRelayPolicy(enum ZT_RelayPolicy rp)
|
||||
{
|
||||
_relayPolicy = rp;
|
||||
return ZT_RESULT_OK;
|
||||
}
|
||||
|
||||
ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
|
||||
{
|
||||
Mutex::Lock _l(_networks_m);
|
||||
@@ -419,16 +438,15 @@ ZT_PeerList *Node::peers() const
|
||||
p->latency = pi->second->latency();
|
||||
p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_ROOT : ZT_PEER_ROLE_LEAF;
|
||||
|
||||
std::vector< std::pair< SharedPtr<Path>,bool > > paths(pi->second->paths(_now));
|
||||
SharedPtr<Path> bestp(pi->second->getBestPath(_now,false));
|
||||
std::vector<Path> paths(pi->second->paths());
|
||||
Path *bestPath = pi->second->getBestPath(_now);
|
||||
p->pathCount = 0;
|
||||
for(std::vector< std::pair< SharedPtr<Path>,bool > >::iterator path(paths.begin());path!=paths.end();++path) {
|
||||
memcpy(&(p->paths[p->pathCount].address),&(path->first->address()),sizeof(struct sockaddr_storage));
|
||||
p->paths[p->pathCount].lastSend = path->first->lastOut();
|
||||
p->paths[p->pathCount].lastReceive = path->first->lastIn();
|
||||
p->paths[p->pathCount].expired = path->second;
|
||||
p->paths[p->pathCount].preferred = (path->first == bestp) ? 1 : 0;
|
||||
p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->first->address());
|
||||
for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
|
||||
memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
|
||||
p->paths[p->pathCount].lastSend = path->lastSend();
|
||||
p->paths[p->pathCount].lastReceive = path->lastReceived();
|
||||
p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
|
||||
p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
|
||||
++p->pathCount;
|
||||
}
|
||||
}
|
||||
@@ -524,7 +542,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)
|
||||
for(unsigned int a=0;a<test->hops[0].breadth;++a) {
|
||||
outp.newInitializationVector();
|
||||
outp.setDestination(Address(test->hops[0].addresses[a]));
|
||||
RR->sw->send(outp,true);
|
||||
RR->sw->send(outp,true,0);
|
||||
}
|
||||
} catch ( ... ) {
|
||||
return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
|
||||
@@ -620,6 +638,18 @@ void Node::clusterStatus(ZT_ClusterStatus *cs)
|
||||
memset(cs,0,sizeof(ZT_ClusterStatus));
|
||||
}
|
||||
|
||||
void Node::backgroundThreadMain()
|
||||
{
|
||||
++RR->dpEnabled;
|
||||
for(;;) {
|
||||
try {
|
||||
if (RR->dp->process() < 0)
|
||||
break;
|
||||
} catch ( ... ) {} // sanity check -- should not throw
|
||||
}
|
||||
--RR->dpEnabled;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* Node methods used only within node/ */
|
||||
/****************************************************************************/
|
||||
@@ -715,11 +745,6 @@ void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
|
||||
(reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
|
||||
}
|
||||
|
||||
void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
|
||||
{
|
||||
RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
/****************************************************************************/
|
||||
@@ -810,15 +835,6 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
|
||||
}
|
||||
}
|
||||
|
||||
enum ZT_ResultCode ZT_Node_setRelayPolicy(ZT_Node *node,enum ZT_RelayPolicy rp)
|
||||
{
|
||||
try {
|
||||
return reinterpret_cast<ZeroTier::Node *>(node)->setRelayPolicy(rp);
|
||||
} catch ( ... ) {
|
||||
return ZT_RESULT_FATAL_ERROR_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
|
||||
{
|
||||
try {
|
||||
@@ -998,10 +1014,10 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
|
||||
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
|
||||
void ZT_Node_backgroundThreadMain(ZT_Node *node)
|
||||
{
|
||||
try {
|
||||
reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
|
||||
reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
|
||||
} catch ( ... ) {}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,10 +44,6 @@
|
||||
#define TRACE(f,...) {}
|
||||
#endif
|
||||
|
||||
// Bit mask for "expecting reply" hash
|
||||
#define ZT_EXPECTING_REPLIES_BUCKET_MASK1 255
|
||||
#define ZT_EXPECTING_REPLIES_BUCKET_MASK2 31
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
@@ -91,7 +87,6 @@ public:
|
||||
unsigned int frameLength,
|
||||
volatile uint64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
|
||||
ZT_ResultCode setRelayPolicy(enum ZT_RelayPolicy rp);
|
||||
ZT_ResultCode join(uint64_t nwid,void *uptr);
|
||||
ZT_ResultCode leave(uint64_t nwid,void **uptr);
|
||||
ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
|
||||
@@ -122,9 +117,18 @@ public:
|
||||
void clusterRemoveMember(unsigned int memberId);
|
||||
void clusterHandleIncomingMessage(const void *msg,unsigned int len);
|
||||
void clusterStatus(ZT_ClusterStatus *cs);
|
||||
void backgroundThreadMain();
|
||||
|
||||
// Internal functions ------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Convenience threadMain() for easy background thread launch
|
||||
*
|
||||
* This allows background threads to be launched with Thread::start
|
||||
* that will run against this node.
|
||||
*/
|
||||
inline void threadMain() throw() { this->backgroundThreadMain(); }
|
||||
|
||||
/**
|
||||
* @return Time as of last call to run()
|
||||
*/
|
||||
@@ -244,43 +248,26 @@ public:
|
||||
*/
|
||||
inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
|
||||
|
||||
/**
|
||||
* @return True if we appear to be online
|
||||
*/
|
||||
inline bool online() const throw() { return _online; }
|
||||
inline ZT_RelayPolicy relayPolicy() const { return _relayPolicy; }
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
void postTrace(const char *module,unsigned int line,const char *fmt,...);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @return Next 64-bit random number (not for cryptographic use)
|
||||
*/
|
||||
uint64_t prng();
|
||||
|
||||
/**
|
||||
* Post a circuit test report to any listeners for a given test ID
|
||||
*
|
||||
* @param report Report (includes test ID)
|
||||
*/
|
||||
void postCircuitTestReport(const ZT_CircuitTestReport *report);
|
||||
void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
|
||||
|
||||
/**
|
||||
* Register that we are expecting a reply to a packet ID
|
||||
*
|
||||
* @param packetId Packet ID to expect reply to
|
||||
*/
|
||||
inline void expectReplyTo(const uint64_t packetId)
|
||||
{
|
||||
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||
_expectingRepliesTo[bucket][_expectingRepliesToBucketPtr[bucket]++ & ZT_EXPECTING_REPLIES_BUCKET_MASK2] = packetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a given packet ID is something we are expecting a reply to
|
||||
*
|
||||
* @param packetId Packet ID to check
|
||||
* @return True if we're expecting a reply
|
||||
*/
|
||||
inline bool expectingReplyTo(const uint64_t packetId) const
|
||||
{
|
||||
const unsigned long bucket = (unsigned long)(packetId & ZT_EXPECTING_REPLIES_BUCKET_MASK1);
|
||||
for(unsigned long i=0;i<=ZT_EXPECTING_REPLIES_BUCKET_MASK2;++i) {
|
||||
if (_expectingRepliesTo[bucket][i] == packetId)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
inline SharedPtr<Network> _network(uint64_t nwid) const
|
||||
@@ -298,9 +285,6 @@ private:
|
||||
|
||||
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
|
||||
|
||||
uint8_t _expectingRepliesToBucketPtr[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1];
|
||||
uint64_t _expectingRepliesTo[ZT_EXPECTING_REPLIES_BUCKET_MASK1 + 1][ZT_EXPECTING_REPLIES_BUCKET_MASK2 + 1];
|
||||
|
||||
ZT_DataStoreGetFunction _dataStoreGetFunction;
|
||||
ZT_DataStorePutFunction _dataStorePutFunction;
|
||||
ZT_WirePacketSendFunction _wirePacketSendFunction;
|
||||
@@ -327,7 +311,6 @@ private:
|
||||
uint64_t _now;
|
||||
uint64_t _lastPingCheck;
|
||||
uint64_t _lastHousekeepingRun;
|
||||
ZT_RelayPolicy _relayPolicy;
|
||||
bool _online;
|
||||
};
|
||||
|
||||
|
||||
@@ -21,9 +21,8 @@
|
||||
#include "OutboundMulticast.hpp"
|
||||
#include "Switch.hpp"
|
||||
#include "Network.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Node.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Topology.hpp"
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
@@ -31,7 +30,7 @@ void OutboundMulticast::init(
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const CertificateOfMembership *com,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
@@ -40,25 +39,16 @@ void OutboundMulticast::init(
|
||||
const void *payload,
|
||||
unsigned int len)
|
||||
{
|
||||
uint8_t flags = 0;
|
||||
|
||||
_timestamp = timestamp;
|
||||
_nwid = nwid;
|
||||
if (src) {
|
||||
_macSrc = src;
|
||||
flags |= 0x04;
|
||||
} else {
|
||||
_macSrc.fromAddress(RR->identity.address(),nwid);
|
||||
}
|
||||
_macDest = dest.mac();
|
||||
_limit = limit;
|
||||
_frameLen = (len < ZT_MAX_MTU) ? len : ZT_MAX_MTU;
|
||||
_etherType = etherType;
|
||||
|
||||
uint8_t flags = 0;
|
||||
if (gatherLimit) flags |= 0x02;
|
||||
if (src) flags |= 0x04;
|
||||
|
||||
/*
|
||||
TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u",
|
||||
TRACE(">>MC %.16llx INIT %.16llx/%s limit %u gatherLimit %u from %s to %s length %u com==%d",
|
||||
(unsigned long long)this,
|
||||
nwid,
|
||||
dest.toString().c_str(),
|
||||
@@ -66,36 +56,58 @@ void OutboundMulticast::init(
|
||||
gatherLimit,
|
||||
(src) ? src.toString().c_str() : MAC(RR->identity.address(),nwid).toString().c_str(),
|
||||
dest.toString().c_str(),
|
||||
len);
|
||||
len,
|
||||
(com) ? 1 : 0);
|
||||
*/
|
||||
|
||||
_packet.setSource(RR->identity.address());
|
||||
_packet.setVerb(Packet::VERB_MULTICAST_FRAME);
|
||||
_packet.append((uint64_t)nwid);
|
||||
_packet.append(flags);
|
||||
if (gatherLimit) _packet.append((uint32_t)gatherLimit);
|
||||
if (src) src.appendTo(_packet);
|
||||
dest.mac().appendTo(_packet);
|
||||
_packet.append((uint32_t)dest.adi());
|
||||
_packet.append((uint16_t)etherType);
|
||||
_packet.append(payload,_frameLen);
|
||||
if (!disableCompression)
|
||||
_packet.compress();
|
||||
_packetNoCom.setSource(RR->identity.address());
|
||||
_packetNoCom.setVerb(Packet::VERB_MULTICAST_FRAME);
|
||||
_packetNoCom.append((uint64_t)nwid);
|
||||
_packetNoCom.append(flags);
|
||||
if (gatherLimit) _packetNoCom.append((uint32_t)gatherLimit);
|
||||
if (src) src.appendTo(_packetNoCom);
|
||||
dest.mac().appendTo(_packetNoCom);
|
||||
_packetNoCom.append((uint32_t)dest.adi());
|
||||
_packetNoCom.append((uint16_t)etherType);
|
||||
_packetNoCom.append(payload,len);
|
||||
_packetNoCom.compress();
|
||||
|
||||
memcpy(_frameData,payload,_frameLen);
|
||||
if (com) {
|
||||
_haveCom = true;
|
||||
flags |= 0x01;
|
||||
|
||||
_packetWithCom.setSource(RR->identity.address());
|
||||
_packetWithCom.setVerb(Packet::VERB_MULTICAST_FRAME);
|
||||
_packetWithCom.append((uint64_t)nwid);
|
||||
_packetWithCom.append(flags);
|
||||
com->serialize(_packetWithCom);
|
||||
if (gatherLimit) _packetWithCom.append((uint32_t)gatherLimit);
|
||||
if (src) src.appendTo(_packetWithCom);
|
||||
dest.mac().appendTo(_packetWithCom);
|
||||
_packetWithCom.append((uint32_t)dest.adi());
|
||||
_packetWithCom.append((uint16_t)etherType);
|
||||
_packetWithCom.append(payload,len);
|
||||
_packetWithCom.compress();
|
||||
} else _haveCom = false;
|
||||
}
|
||||
|
||||
void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
|
||||
{
|
||||
const SharedPtr<Network> nw(RR->node->network(_nwid));
|
||||
Address toAddr2(toAddr);
|
||||
if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
|
||||
//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
|
||||
_packet.newInitializationVector();
|
||||
_packet.setDestination(toAddr2);
|
||||
RR->node->expectReplyTo(_packet.packetId());
|
||||
RR->sw->send(_packet,true);
|
||||
if (_haveCom) {
|
||||
SharedPtr<Peer> peer(RR->topology->getPeer(toAddr));
|
||||
if ( (!peer) || (peer->needsOurNetworkMembershipCertificate(_nwid,RR->node->now(),true)) ) {
|
||||
//TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
|
||||
_packetWithCom.newInitializationVector();
|
||||
_packetWithCom.setDestination(toAddr);
|
||||
RR->sw->send(_packetWithCom,true,_nwid);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str());
|
||||
_packetNoCom.newInitializationVector();
|
||||
_packetNoCom.setDestination(toAddr);
|
||||
RR->sw->send(_packetNoCom,true,_nwid);
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -56,7 +56,7 @@ public:
|
||||
* @param RR Runtime environment
|
||||
* @param timestamp Creation time
|
||||
* @param nwid Network ID
|
||||
* @param disableCompression Disable compression of frame payload
|
||||
* @param com Certificate of membership or NULL if none available
|
||||
* @param limit Multicast limit for desired number of packets to send
|
||||
* @param gatherLimit Number to lazily/implicitly gather with this frame or 0 for none
|
||||
* @param src Source MAC address of frame or NULL to imply compute from sender ZT address
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
const RuntimeEnvironment *RR,
|
||||
uint64_t timestamp,
|
||||
uint64_t nwid,
|
||||
bool disableCompression,
|
||||
const CertificateOfMembership *com,
|
||||
unsigned int limit,
|
||||
unsigned int gatherLimit,
|
||||
const MAC &src,
|
||||
@@ -127,22 +127,17 @@ public:
|
||||
if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
|
||||
sendAndLog(RR,toAddr);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else return false;
|
||||
}
|
||||
|
||||
private:
|
||||
uint64_t _timestamp;
|
||||
uint64_t _nwid;
|
||||
MAC _macSrc;
|
||||
MAC _macDest;
|
||||
unsigned int _limit;
|
||||
unsigned int _frameLen;
|
||||
unsigned int _etherType;
|
||||
Packet _packet;
|
||||
Packet _packetNoCom;
|
||||
Packet _packetWithCom;
|
||||
std::vector<Address> _alreadySentTo;
|
||||
uint8_t _frameData[ZT_MAX_MTU];
|
||||
bool _haveCom;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace ZeroTier {
|
||||
|
||||
const unsigned char Packet::ZERO_KEY[32] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 };
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
//#ifdef ZT_TRACE
|
||||
|
||||
const char *Packet::verbString(Verb v)
|
||||
throw()
|
||||
@@ -38,16 +38,15 @@ const char *Packet::verbString(Verb v)
|
||||
case VERB_EXT_FRAME: return "EXT_FRAME";
|
||||
case VERB_ECHO: return "ECHO";
|
||||
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
|
||||
case VERB_NETWORK_CREDENTIALS: return "NETWORK_CREDENTIALS";
|
||||
case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
|
||||
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
|
||||
case VERB_NETWORK_CONFIG: return "NETWORK_CONFIG_REFRESH";
|
||||
case VERB_NETWORK_CONFIG_REFRESH: 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";
|
||||
case VERB_CIRCUIT_TEST: return "CIRCUIT_TEST";
|
||||
case VERB_CIRCUIT_TEST_REPORT: return "CIRCUIT_TEST_REPORT";
|
||||
case VERB_REQUEST_PROOF_OF_WORK: return "REQUEST_PROOF_OF_WORK";
|
||||
case VERB_USER_MESSAGE: return "USER_MESSAGE";
|
||||
}
|
||||
return "(unknown)";
|
||||
}
|
||||
@@ -69,7 +68,7 @@ const char *Packet::errorString(ErrorCode e)
|
||||
return "(unknown)";
|
||||
}
|
||||
|
||||
#endif // ZT_TRACE
|
||||
//#endif // ZT_TRACE
|
||||
|
||||
void Packet::armor(const void *key,bool encryptPayload)
|
||||
{
|
||||
|
||||
@@ -34,11 +34,7 @@
|
||||
#include "Utils.hpp"
|
||||
#include "Buffer.hpp"
|
||||
|
||||
#ifdef ZT_USE_SYSTEM_LZ4
|
||||
#include <lz4.h>
|
||||
#else
|
||||
#include "../ext/lz4/lz4.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Protocol version -- incremented only for major changes
|
||||
@@ -51,23 +47,14 @@
|
||||
* + Yet another multicast redesign
|
||||
* + New crypto completely changes key agreement cipher
|
||||
* 4 - 0.6.0 ... 1.0.6
|
||||
* + BREAKING CHANGE: New identity format based on hashcash design
|
||||
* 5 - 1.1.0 ... 1.1.5
|
||||
* + New identity format based on hashcash design
|
||||
* 5 - 1.1.0 ... CURRENT
|
||||
* + Supports circuit test, proof of work, and echo
|
||||
* + Supports in-band world (root server definition) updates
|
||||
* + Clustering! (Though this will work with protocol v4 clients.)
|
||||
* + Otherwise backward compatible with protocol v4
|
||||
* 6 - 1.1.5 ... 1.1.10
|
||||
* + Network configuration format revisions including binary values
|
||||
* 7 - 1.1.10 -- 1.2.0
|
||||
* + Introduce trusted paths for local SDN use
|
||||
* 8 - 1.2.0 -- CURRENT
|
||||
* + Multipart network configurations for large network configs
|
||||
* + Tags and Capabilities
|
||||
* + Inline push of CertificateOfMembership deprecated
|
||||
* + Certificates of representation for federation and mesh
|
||||
*/
|
||||
#define ZT_PROTO_VERSION 8
|
||||
#define ZT_PROTO_VERSION 5
|
||||
|
||||
/**
|
||||
* Minimum supported protocol version
|
||||
@@ -106,21 +93,10 @@
|
||||
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
|
||||
|
||||
/**
|
||||
* Cipher suite: NONE
|
||||
* DEPRECATED payload encrypted flag, will be removed for re-use soon.
|
||||
*
|
||||
* This differs from POLY1305/NONE in that *no* crypto is done, not even
|
||||
* authentication. This is for trusted local LAN interconnects for internal
|
||||
* SDN use within a data center.
|
||||
*
|
||||
* For this mode the MAC field becomes a trusted path ID and must match the
|
||||
* configured ID of a trusted path or the packet is discarded.
|
||||
*/
|
||||
#define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2
|
||||
|
||||
/**
|
||||
* DEPRECATED payload encrypted flag, may be re-used in the future.
|
||||
*
|
||||
* This has been replaced by the three-bit cipher suite selection field.
|
||||
* This has been replaced by the two-bit cipher suite selection field where
|
||||
* a value of 0 indicates unencrypted (but authenticated) messages.
|
||||
*/
|
||||
#define ZT_PROTO_FLAG_ENCRYPTED 0x80
|
||||
|
||||
@@ -307,7 +283,6 @@
|
||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_FLAGS + 1)
|
||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC + 6)
|
||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI + 4)
|
||||
#define ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM (ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT + 4)
|
||||
|
||||
// Note: COM, GATHER_LIMIT, and SOURCE_MAC are optional, and so are specified without size
|
||||
#define ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID (ZT_PACKET_IDX_PAYLOAD)
|
||||
@@ -355,7 +330,7 @@ namespace ZeroTier {
|
||||
* <[5] destination ZT address>
|
||||
* <[5] source ZT address>
|
||||
* <[1] flags/cipher/hops>
|
||||
* <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
|
||||
* <[8] 64-bit MAC>
|
||||
* [... -- begin encryption envelope -- ...]
|
||||
* <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
|
||||
* [... verb-specific payload ...]
|
||||
@@ -528,7 +503,7 @@ public:
|
||||
/**
|
||||
* No operation (ignored, no reply)
|
||||
*/
|
||||
VERB_NOP = 0x00,
|
||||
VERB_NOP = 0,
|
||||
|
||||
/**
|
||||
* Announcement of a node's existence:
|
||||
@@ -539,7 +514,7 @@ public:
|
||||
* <[8] timestamp (ms since epoch)>
|
||||
* <[...] binary serialized identity (see Identity)>
|
||||
* <[1] destination address type>
|
||||
* [<[...] destination address to which packet was sent>]
|
||||
* [<[...] destination address>]
|
||||
* <[8] 64-bit world ID of current world>
|
||||
* <[8] 64-bit timestamp of current world>
|
||||
*
|
||||
@@ -571,7 +546,7 @@ public:
|
||||
*
|
||||
* ERROR has no payload.
|
||||
*/
|
||||
VERB_HELLO = 0x01,
|
||||
VERB_HELLO = 1,
|
||||
|
||||
/**
|
||||
* Error response:
|
||||
@@ -580,7 +555,7 @@ public:
|
||||
* <[1] error code>
|
||||
* <[...] error-dependent payload>
|
||||
*/
|
||||
VERB_ERROR = 0x02,
|
||||
VERB_ERROR = 2,
|
||||
|
||||
/**
|
||||
* Success response:
|
||||
@@ -588,29 +563,25 @@ public:
|
||||
* <[8] in-re packet ID>
|
||||
* <[...] request-specific payload>
|
||||
*/
|
||||
VERB_OK = 0x03,
|
||||
VERB_OK = 3,
|
||||
|
||||
/**
|
||||
* Query an identity by address:
|
||||
* <[5] address to look up>
|
||||
* [<[...] additional addresses to look up>
|
||||
*
|
||||
* OK response payload:
|
||||
* <[...] binary serialized identity>
|
||||
* [<[...] additional binary serialized identities>]
|
||||
*
|
||||
* If querying a cluster, duplicate OK responses may occasionally occur.
|
||||
* These must be tolerated, which is easy since they'll have info you
|
||||
* already have.
|
||||
* These should be discarded.
|
||||
*
|
||||
* If the address is not found, no response is generated. The semantics
|
||||
* of WHOIS is similar to ARP and NDP in that persistent retrying can
|
||||
* be performed.
|
||||
* If the address is not found, no response is generated. WHOIS requests
|
||||
* will time out much like ARP requests and similar do in L2.
|
||||
*/
|
||||
VERB_WHOIS = 0x04,
|
||||
VERB_WHOIS = 4,
|
||||
|
||||
/**
|
||||
* Relay-mediated NAT traversal or firewall punching initiation:
|
||||
* Meet another node at a given protocol address:
|
||||
* <[1] flags (unused, currently 0)>
|
||||
* <[5] ZeroTier address of peer that might be found at this address>
|
||||
* <[2] 16-bit protocol address port>
|
||||
@@ -624,9 +595,18 @@ public:
|
||||
*
|
||||
* Upon receipt a peer sends HELLO to establish a direct link.
|
||||
*
|
||||
* Nodes should implement rate control, limiting the rate at which they
|
||||
* respond to these packets to prevent their use in DDOS attacks. Nodes
|
||||
* may also ignore these messages if a peer is not known or is not being
|
||||
* actively communicated with.
|
||||
*
|
||||
* Unfortunately the physical address format in this message pre-dates
|
||||
* InetAddress's serialization format. :( ZeroTier is four years old and
|
||||
* yes we've accumulated a tiny bit of cruft here and there.
|
||||
*
|
||||
* No OK or ERROR is generated.
|
||||
*/
|
||||
VERB_RENDEZVOUS = 0x05,
|
||||
VERB_RENDEZVOUS = 5,
|
||||
|
||||
/**
|
||||
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
|
||||
@@ -642,44 +622,31 @@ public:
|
||||
* ERROR may be generated if a membership certificate is needed for a
|
||||
* closed network. Payload will be network ID.
|
||||
*/
|
||||
VERB_FRAME = 0x06,
|
||||
VERB_FRAME = 6,
|
||||
|
||||
/**
|
||||
* Full Ethernet frame with MAC addressing and optional fields:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[1] flags>
|
||||
* [<[...] certificate of network membership>]
|
||||
* <[6] destination MAC or all zero for destination node>
|
||||
* <[6] source MAC or all zero for node of origin>
|
||||
* <[2] 16-bit ethertype>
|
||||
* <[...] ethernet payload>
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Certificate of network membership attached (DEPRECATED)
|
||||
* 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)
|
||||
* 0x01 - Certificate of network membership is attached
|
||||
*
|
||||
* 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 it a
|
||||
* superset of VERB_FRAME. It is used for bridged traffic,
|
||||
* redirected or observed traffic via rules, and can in theory
|
||||
* be used for multicast though MULTICAST_FRAME exists for that
|
||||
* purpose and has additional options and capabilities.
|
||||
* 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.
|
||||
*
|
||||
* OK payload (if ACK flag is set):
|
||||
* <[8] 64-bit network ID>
|
||||
* Multicast frames may not be sent as EXT_FRAME.
|
||||
*
|
||||
* ERROR may be generated if a membership certificate is needed for a
|
||||
* closed network. Payload will be network ID.
|
||||
*/
|
||||
VERB_EXT_FRAME = 0x07,
|
||||
VERB_EXT_FRAME = 7,
|
||||
|
||||
/**
|
||||
* ECHO request (a.k.a. ping):
|
||||
@@ -689,7 +656,7 @@ public:
|
||||
* is generated. Response to ECHO requests is optional and ECHO may be
|
||||
* ignored if a node detects a possible flood.
|
||||
*/
|
||||
VERB_ECHO = 0x08,
|
||||
VERB_ECHO = 8,
|
||||
|
||||
/**
|
||||
* Announce interest in multicast group(s):
|
||||
@@ -703,116 +670,77 @@ public:
|
||||
* controllers and root servers. In the current network, root servers
|
||||
* will provide the service of final multicast cache.
|
||||
*
|
||||
* VERB_NETWORK_CREDENTIALS should be pushed along with this, especially
|
||||
* if using upstream (e.g. root) nodes as multicast databases. This allows
|
||||
* GATHERs to be authenticated.
|
||||
* It is recommended that NETWORK_MEMBERSHIP_CERTIFICATE pushes be sent
|
||||
* along with MULTICAST_LIKE when pushing LIKEs to peers that do not
|
||||
* share a network membership (such as root servers), since this can be
|
||||
* used to authenticate GATHER requests and limit responses to peers
|
||||
* authorized to talk on a network. (Should be an optional field here,
|
||||
* but saving one or two packets every five minutes is not worth an
|
||||
* ugly hack or protocol rev.)
|
||||
*
|
||||
* OK/ERROR are not generated.
|
||||
*/
|
||||
VERB_MULTICAST_LIKE = 0x09,
|
||||
VERB_MULTICAST_LIKE = 9,
|
||||
|
||||
/**
|
||||
* Network credentials push:
|
||||
* Network member certificate replication/push:
|
||||
* <[...] serialized certificate of membership>
|
||||
* [<[...] additional certificates of membership>]
|
||||
* <[1] 0x00, null byte marking end of COM array>
|
||||
* <[2] 16-bit number of capabilities>
|
||||
* <[...] 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>
|
||||
* [ ... additional certificates may follow ...]
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* The use of a zero byte to terminate the COM section is for legacy
|
||||
* backward compatiblity. Newer fields are prefixed with a length.
|
||||
* 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.
|
||||
*
|
||||
* OK/ERROR are not generated.
|
||||
*/
|
||||
VERB_NETWORK_CREDENTIALS = 0x0a,
|
||||
VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
|
||||
|
||||
/**
|
||||
* Network configuration request:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[2] 16-bit length of request meta-data dictionary>
|
||||
* <[...] string-serialized request meta-data>
|
||||
* <[8] 64-bit revision of netconf we currently have>
|
||||
* <[8] 64-bit timestamp of netconf we currently have>
|
||||
* [<[8] 64-bit revision of netconf we currently have>]
|
||||
*
|
||||
* This message requests network configuration from a node capable of
|
||||
* providing it.
|
||||
* providing it. If the optional revision is included, a response is
|
||||
* only generated if there is a newer network configuration available.
|
||||
*
|
||||
* 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>
|
||||
* <[...] network configuration dictionary (may be incomplete)>
|
||||
* [ ... end of legacy single chunk response ... ]
|
||||
* <[1] 8-bit flags>
|
||||
* <[8] 64-bit config update ID (should never be 0)>
|
||||
* <[4] 32-bit total length of assembled dictionary>
|
||||
* <[4] 32-bit index of chunk>
|
||||
* [ ... end signed portion ... ]
|
||||
* <[1] 8-bit chunk signature type>
|
||||
* <[2] 16-bit length of chunk signature>
|
||||
* <[...] chunk signature>
|
||||
* <[2] 16-bit length of network configuration dictionary>
|
||||
* <[...] network configuration dictionary>
|
||||
*
|
||||
* The chunk signature signs the entire payload of the OK response.
|
||||
* Currently only one signature type is supported: ed25519 (1).
|
||||
* OK returns a Dictionary (string serialized) containing the network's
|
||||
* configuration and IP address assignment information for the querying
|
||||
* node. It also contains a membership certificate that the querying
|
||||
* node can push to other peers to demonstrate its right to speak on
|
||||
* a given network.
|
||||
*
|
||||
* Each config chunk is signed to prevent memory exhaustion or
|
||||
* traffic crowding DOS attacks against config fragment assembly.
|
||||
*
|
||||
* If the packet is from the network controller it is permitted to end
|
||||
* before the config update ID or other chunking related or signature
|
||||
* fields. This is to support older controllers that don't include
|
||||
* these fields and may be removed in the future.
|
||||
* When a new network configuration is received, another config request
|
||||
* should be sent with the new netconf's revision. This confirms receipt
|
||||
* and also causes any subsequent changes to rapidly propagate as this
|
||||
* cycle will repeat until there are no changes. This is optional but
|
||||
* recommended behavior.
|
||||
*
|
||||
* ERROR response payload:
|
||||
* <[8] 64-bit network ID>
|
||||
*
|
||||
* UNSUPPORTED_OPERATION is returned if this service is not supported,
|
||||
* and OBJ_NOT_FOUND if the queried network ID was not found.
|
||||
*/
|
||||
VERB_NETWORK_CONFIG_REQUEST = 0x0b,
|
||||
VERB_NETWORK_CONFIG_REQUEST = 11,
|
||||
|
||||
/**
|
||||
* Network configuration data push:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||
* <[...] network configuration dictionary (may be incomplete)>
|
||||
* <[1] 8-bit flags>
|
||||
* <[8] 64-bit config update ID (should never be 0)>
|
||||
* <[4] 32-bit total length of assembled dictionary>
|
||||
* <[4] 32-bit index of chunk>
|
||||
* [ ... end signed portion ... ]
|
||||
* <[1] 8-bit chunk signature type>
|
||||
* <[2] 16-bit length of chunk signature>
|
||||
* <[...] chunk signature>
|
||||
* Network configuration refresh request:
|
||||
* <[...] array of 64-bit network IDs>
|
||||
*
|
||||
* This is a direct push variant for network config updates. It otherwise
|
||||
* carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same
|
||||
* semantics.
|
||||
* This can be sent by the network controller to inform a node that it
|
||||
* should now make a NETWORK_CONFIG_REQUEST.
|
||||
*
|
||||
* The legacy mode missing the additional chunking fields is not supported
|
||||
* here.
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Use fast propagation
|
||||
*
|
||||
* An OK should be sent if the config is successfully received and
|
||||
* accepted.
|
||||
*
|
||||
* OK payload:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[8] 64-bit config update ID>
|
||||
* It does not generate an OK or ERROR message, and is treated only as
|
||||
* a hint to refresh now.
|
||||
*/
|
||||
VERB_NETWORK_CONFIG = 0x0c,
|
||||
VERB_NETWORK_CONFIG_REFRESH = 12,
|
||||
|
||||
/**
|
||||
* Request endpoints for multicast distribution:
|
||||
@@ -821,10 +749,10 @@ public:
|
||||
* <[6] MAC address of multicast group being queried>
|
||||
* <[4] 32-bit ADI for multicast group being queried>
|
||||
* <[4] 32-bit requested max number of multicast peers>
|
||||
* [<[...] network certificate of membership>]
|
||||
* [<[...] network certificate of membership>]
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - COM is attached
|
||||
* 0x01 - Network certificate of membership is attached
|
||||
*
|
||||
* This message asks a peer for additional known endpoints that have
|
||||
* LIKEd a given multicast group. It's sent when the sender wishes
|
||||
@@ -834,9 +762,6 @@ public:
|
||||
* More than one OK response can occur if the response is broken up across
|
||||
* multiple packets or if querying a clustered node.
|
||||
*
|
||||
* The COM should be included so that upstream nodes that are not
|
||||
* members of our network can validate our request.
|
||||
*
|
||||
* OK response payload:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[6] MAC address of multicast group being queried>
|
||||
@@ -848,12 +773,13 @@ public:
|
||||
*
|
||||
* ERROR is not generated; queries that return no response are dropped.
|
||||
*/
|
||||
VERB_MULTICAST_GATHER = 0x0d,
|
||||
VERB_MULTICAST_GATHER = 13,
|
||||
|
||||
/**
|
||||
* Multicast frame:
|
||||
* <[8] 64-bit network ID>
|
||||
* <[1] flags>
|
||||
* [<[...] network certificate of membership>]
|
||||
* [<[4] 32-bit implicit gather limit>]
|
||||
* [<[6] source MAC>]
|
||||
* <[6] destination MAC (multicast address)>
|
||||
@@ -862,7 +788,7 @@ public:
|
||||
* <[...] ethernet payload>
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Network certificate of membership attached (DEPRECATED)
|
||||
* 0x01 - Network certificate of membership is attached
|
||||
* 0x02 - Implicit gather limit field is present
|
||||
* 0x04 - Source MAC is specified -- otherwise it's computed from sender
|
||||
*
|
||||
@@ -877,11 +803,11 @@ public:
|
||||
* <[6] MAC address of multicast group>
|
||||
* <[4] 32-bit ADI for multicast group>
|
||||
* <[1] flags>
|
||||
* [<[...] network certficate of membership (DEPRECATED)>]
|
||||
* [<[...] network certficate of membership>]
|
||||
* [<[...] implicit gather results if flag 0x01 is set>]
|
||||
*
|
||||
* OK flags (same bits as request flags):
|
||||
* 0x01 - OK includes certificate of network membership (DEPRECATED)
|
||||
* 0x01 - OK includes certificate of network membership
|
||||
* 0x02 - OK includes implicit gather results
|
||||
*
|
||||
* ERROR response payload:
|
||||
@@ -889,9 +815,7 @@ public:
|
||||
* <[6] multicast group MAC>
|
||||
* <[4] 32-bit multicast group ADI>
|
||||
*/
|
||||
VERB_MULTICAST_FRAME = 0x0e,
|
||||
|
||||
// 0x0f is reserved for an old deprecated message
|
||||
VERB_MULTICAST_FRAME = 14,
|
||||
|
||||
/**
|
||||
* Push of potential endpoints for direct communication:
|
||||
@@ -927,7 +851,7 @@ public:
|
||||
*
|
||||
* OK and ERROR are not generated.
|
||||
*/
|
||||
VERB_PUSH_DIRECT_PATHS = 0x10,
|
||||
VERB_PUSH_DIRECT_PATHS = 16,
|
||||
|
||||
/**
|
||||
* Source-routed circuit test message:
|
||||
@@ -943,17 +867,21 @@ public:
|
||||
* [ ... end of signed portion of request ... ]
|
||||
* <[2] 16-bit length of signature of request>
|
||||
* <[...] signature of request by originator>
|
||||
* <[2] 16-bit length of additional fields>
|
||||
* [[...] additional fields]
|
||||
* <[2] 16-bit previous hop credential length (including type)>
|
||||
* [[1] previous hop credential type]
|
||||
* [[...] previous hop credential]
|
||||
* <[...] next hop(s) in path>
|
||||
*
|
||||
* Flags:
|
||||
* 0x01 - Report back to originator at all hops
|
||||
* 0x01 - Report back to originator at middle hops
|
||||
* 0x02 - Report back to originator at last hop
|
||||
*
|
||||
* Originator credential types:
|
||||
* 0x01 - 64-bit network ID for which originator is controller
|
||||
*
|
||||
* Previous hop credential types:
|
||||
* 0x01 - Certificate of network membership
|
||||
*
|
||||
* Path record format:
|
||||
* <[1] 8-bit flags (unused, must be zero)>
|
||||
* <[1] 8-bit breadth (number of next hops)>
|
||||
@@ -1002,25 +930,25 @@ public:
|
||||
* <[8] 64-bit timestamp (echoed from original>
|
||||
* <[8] 64-bit test ID (echoed from original)>
|
||||
*/
|
||||
VERB_CIRCUIT_TEST = 0x11,
|
||||
VERB_CIRCUIT_TEST = 17,
|
||||
|
||||
/**
|
||||
* Circuit test hop report:
|
||||
* <[8] 64-bit timestamp (echoed from original test)>
|
||||
* <[8] 64-bit test ID (echoed from original test)>
|
||||
* <[8] 64-bit timestamp (from original test)>
|
||||
* <[8] 64-bit test ID (from original test)>
|
||||
* <[8] 64-bit reserved field (set to 0, currently unused)>
|
||||
* <[1] 8-bit vendor ID (set to 0, currently unused)>
|
||||
* <[1] 8-bit reporter protocol version>
|
||||
* <[1] 8-bit reporter software major version>
|
||||
* <[1] 8-bit reporter software minor version>
|
||||
* <[2] 16-bit reporter software revision>
|
||||
* <[2] 16-bit reporter OS/platform or 0 if not specified>
|
||||
* <[2] 16-bit reporter architecture or 0 if not specified>
|
||||
* <[1] 8-bit reporter major version>
|
||||
* <[1] 8-bit reporter minor version>
|
||||
* <[2] 16-bit reporter revision>
|
||||
* <[2] 16-bit reporter OS/platform>
|
||||
* <[2] 16-bit reporter architecture>
|
||||
* <[2] 16-bit error code (set to 0, currently unused)>
|
||||
* <[8] 64-bit report flags>
|
||||
* <[8] 64-bit packet ID of received CIRCUIT_TEST packet>
|
||||
* <[5] upstream ZeroTier address from which CIRCUIT_TEST was received>
|
||||
* <[1] 8-bit packet hop count of received CIRCUIT_TEST>
|
||||
* <[8] 64-bit report flags (set to 0, currently unused)>
|
||||
* <[8] 64-bit source packet ID>
|
||||
* <[5] upstream ZeroTier address from which test was received>
|
||||
* <[1] 8-bit source packet hop count (ZeroTier hop count)>
|
||||
* <[...] local wire address on which packet was received>
|
||||
* <[...] remote wire address from which packet was received>
|
||||
* <[2] 16-bit length of additional fields>
|
||||
@@ -1032,9 +960,6 @@ public:
|
||||
* <[5] ZeroTier address of next hop>
|
||||
* <[...] current best direct path address, if any, 0 if none>
|
||||
*
|
||||
* Report flags:
|
||||
* 0x1 - Upstream peer in circuit test path allowed in path (e.g. network COM valid)
|
||||
*
|
||||
* Circuit test reports can be sent by hops in a circuit test to report
|
||||
* back results. They should include information about the sender as well
|
||||
* as about the paths to which next hops are being sent.
|
||||
@@ -1042,7 +967,7 @@ public:
|
||||
* If a test report is received and no circuit test was sent, it should be
|
||||
* ignored. This message generates no OK or ERROR response.
|
||||
*/
|
||||
VERB_CIRCUIT_TEST_REPORT = 0x12,
|
||||
VERB_CIRCUIT_TEST_REPORT = 18,
|
||||
|
||||
/**
|
||||
* Request proof of work:
|
||||
@@ -1085,18 +1010,7 @@ public:
|
||||
*
|
||||
* ERROR has no payload.
|
||||
*/
|
||||
VERB_REQUEST_PROOF_OF_WORK = 0x13,
|
||||
|
||||
/**
|
||||
* A message with arbitrary user-definable content:
|
||||
* <[8] 64-bit arbitrary message type ID>
|
||||
* [<[...] message payload>]
|
||||
*
|
||||
* This can be used to send arbitrary messages over VL1. It generates no
|
||||
* OK or ERROR and has no special semantics outside of whatever the user
|
||||
* (via the ZeroTier core API) chooses to give it.
|
||||
*/
|
||||
VERB_USER_MESSAGE = 0x14
|
||||
VERB_REQUEST_PROOF_OF_WORK = 19
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1105,39 +1019,39 @@ public:
|
||||
enum ErrorCode
|
||||
{
|
||||
/* No error, not actually used in transit */
|
||||
ERROR_NONE = 0x00,
|
||||
ERROR_NONE = 0,
|
||||
|
||||
/* Invalid request */
|
||||
ERROR_INVALID_REQUEST = 0x01,
|
||||
ERROR_INVALID_REQUEST = 1,
|
||||
|
||||
/* Bad/unsupported protocol version */
|
||||
ERROR_BAD_PROTOCOL_VERSION = 0x02,
|
||||
ERROR_BAD_PROTOCOL_VERSION = 2,
|
||||
|
||||
/* Unknown object queried */
|
||||
ERROR_OBJ_NOT_FOUND = 0x03,
|
||||
ERROR_OBJ_NOT_FOUND = 3,
|
||||
|
||||
/* HELLO pushed an identity whose address is already claimed */
|
||||
ERROR_IDENTITY_COLLISION = 0x04,
|
||||
ERROR_IDENTITY_COLLISION = 4,
|
||||
|
||||
/* Verb or use case not supported/enabled by this node */
|
||||
ERROR_UNSUPPORTED_OPERATION = 0x05,
|
||||
ERROR_UNSUPPORTED_OPERATION = 5,
|
||||
|
||||
/* Network membership certificate update needed */
|
||||
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 0x06,
|
||||
/* Message to private network rejected -- no unexpired certificate on file */
|
||||
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
|
||||
|
||||
/* Tried to join network, but you're not a member */
|
||||
ERROR_NETWORK_ACCESS_DENIED_ = 0x07, /* extra _ at end to avoid Windows name conflict */
|
||||
ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
|
||||
|
||||
/* Multicasts to this group are not wanted */
|
||||
ERROR_UNWANTED_MULTICAST = 0x08
|
||||
ERROR_UNWANTED_MULTICAST = 8
|
||||
};
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
//#ifdef ZT_TRACE
|
||||
static const char *verbString(Verb v)
|
||||
throw();
|
||||
static const char *errorString(ErrorCode e)
|
||||
throw();
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
template<unsigned int C2>
|
||||
Packet(const Buffer<C2> &b) :
|
||||
@@ -1297,6 +1211,7 @@ public:
|
||||
*/
|
||||
inline unsigned int cipher() const
|
||||
{
|
||||
// Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers
|
||||
return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3);
|
||||
}
|
||||
|
||||
@@ -1307,30 +1222,12 @@ public:
|
||||
{
|
||||
unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
|
||||
b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
|
||||
// Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
|
||||
// DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
|
||||
if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
|
||||
b |= ZT_PROTO_FLAG_ENCRYPTED;
|
||||
else b &= (~ZT_PROTO_FLAG_ENCRYPTED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the trusted path ID for this packet (only meaningful if cipher is trusted path)
|
||||
*
|
||||
* @return Trusted path ID (from MAC field)
|
||||
*/
|
||||
inline uint64_t trustedPathId() const { return at<uint64_t>(ZT_PACKET_IDX_MAC); }
|
||||
|
||||
/**
|
||||
* Set this packet's trusted path ID and set the cipher spec to trusted path
|
||||
*
|
||||
* @param tpid Trusted path ID
|
||||
*/
|
||||
inline void setTrusted(const uint64_t tpid)
|
||||
{
|
||||
setCipher(ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH);
|
||||
setAt(ZT_PACKET_IDX_MAC,tpid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get this packet's unique ID (the IV field interpreted as uint64_t)
|
||||
*
|
||||
@@ -1374,10 +1271,6 @@ public:
|
||||
/**
|
||||
* Verify and (if encrypted) decrypt packet
|
||||
*
|
||||
* This does not handle trusted path mode packets and will return false
|
||||
* for these. These are handled in IncomingPacket if the sending physical
|
||||
* address and MAC field match a trusted path.
|
||||
*
|
||||
* @param key 32-byte key
|
||||
* @return False if packet is invalid or failed MAC authenticity check
|
||||
*/
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace ZeroTier {
|
||||
bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
|
||||
{
|
||||
if (RR->node->putPacket(_localAddress,address(),data,len)) {
|
||||
_lastOut = now;
|
||||
sent(now);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -27,9 +27,27 @@
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "SharedPtr.hpp"
|
||||
#include "AtomicCounter.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
// Note: if you change these flags check the logic below. Some of it depends
|
||||
// on these bits being what they are.
|
||||
|
||||
/**
|
||||
* Flag indicating that this path is suboptimal
|
||||
*
|
||||
* Clusters set this flag on remote paths if GeoIP or other routing decisions
|
||||
* indicate that a peer should be handed off to another cluster member.
|
||||
*/
|
||||
#define ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL 0x0001
|
||||
|
||||
/**
|
||||
* Flag indicating that this path is optimal
|
||||
*
|
||||
* Peers set this flag on paths that are pushed by a cluster and indicated as
|
||||
* optimal. A second flag is needed since we want to prioritize cluster optimal
|
||||
* paths and de-prioritize sub-optimal paths and for new paths we don't know
|
||||
* which one they are. So we want a trinary state: optimal, suboptimal, unknown.
|
||||
*/
|
||||
#define ZT_PATH_FLAG_CLUSTER_OPTIMAL 0x0002
|
||||
|
||||
/**
|
||||
* Maximum return value of preferenceRank()
|
||||
@@ -41,100 +59,89 @@ namespace ZeroTier {
|
||||
class RuntimeEnvironment;
|
||||
|
||||
/**
|
||||
* A path across the physical network
|
||||
* Base class for paths
|
||||
*
|
||||
* The base Path class is an immutable value.
|
||||
*/
|
||||
class Path : NonCopyable
|
||||
class Path
|
||||
{
|
||||
friend class SharedPtr<Path>;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Efficient unique key for paths in a Hashtable
|
||||
*/
|
||||
class HashKey
|
||||
{
|
||||
public:
|
||||
HashKey() {}
|
||||
|
||||
HashKey(const InetAddress &l,const InetAddress &r)
|
||||
{
|
||||
// This is an ad-hoc bit packing algorithm to yield unique keys for
|
||||
// remote addresses and their local-side counterparts if defined.
|
||||
// Portability across runtimes is not needed.
|
||||
if (r.ss_family == AF_INET) {
|
||||
_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
|
||||
_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||
if (l.ss_family == AF_INET) {
|
||||
_k[2] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&l)->sin_addr.s_addr;
|
||||
_k[3] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
|
||||
} else {
|
||||
_k[2] = 0;
|
||||
_k[3] = 0;
|
||||
}
|
||||
} else if (r.ss_family == AF_INET6) {
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
|
||||
uint8_t *b = reinterpret_cast<uint8_t *>(_k);
|
||||
for(unsigned int i=0;i<16;++i) b[i] = a[i];
|
||||
_k[2] = ~((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port);
|
||||
if (l.ss_family == AF_INET6) {
|
||||
_k[2] ^= ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) << 32;
|
||||
a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&l)->sin6_addr.s6_addr);
|
||||
b += 24;
|
||||
for(unsigned int i=0;i<8;++i) b[i] = a[i];
|
||||
a += 8;
|
||||
for(unsigned int i=0;i<8;++i) b[i] ^= a[i];
|
||||
}
|
||||
} else {
|
||||
_k[0] = 0;
|
||||
_k[1] = 0;
|
||||
_k[2] = 0;
|
||||
_k[3] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2] + _k[3]); }
|
||||
|
||||
inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) && (_k[3] == k._k[3]) ); }
|
||||
inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
|
||||
|
||||
private:
|
||||
uint64_t _k[4];
|
||||
};
|
||||
|
||||
Path() :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_lastSend(0),
|
||||
_lastPing(0),
|
||||
_lastKeepalive(0),
|
||||
_lastReceived(0),
|
||||
_addr(),
|
||||
_localAddress(),
|
||||
_flags(0),
|
||||
_ipScope(InetAddress::IP_SCOPE_NONE)
|
||||
{
|
||||
}
|
||||
|
||||
Path(const InetAddress &localAddress,const InetAddress &addr) :
|
||||
_lastOut(0),
|
||||
_lastIn(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
_lastSend(0),
|
||||
_lastPing(0),
|
||||
_lastKeepalive(0),
|
||||
_lastReceived(0),
|
||||
_addr(addr),
|
||||
_localAddress(localAddress),
|
||||
_flags(0),
|
||||
_ipScope(addr.ipScope())
|
||||
{
|
||||
}
|
||||
|
||||
inline Path &operator=(const Path &p)
|
||||
{
|
||||
if (this != &p)
|
||||
memcpy(this,&p,sizeof(Path));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a packet is received from this remote path, regardless of content
|
||||
* Called when a packet is sent to this remote path
|
||||
*
|
||||
* This is called automatically by Path::send().
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void sent(uint64_t t) { _lastSend = t; }
|
||||
|
||||
/**
|
||||
* Called when we've sent a ping or echo
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void pinged(uint64_t t) { _lastPing = t; }
|
||||
|
||||
/**
|
||||
* Called when we send a NAT keepalive
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void sentKeepalive(uint64_t t) { _lastKeepalive = t; }
|
||||
|
||||
/**
|
||||
* Called when a packet is received from this remote path
|
||||
*
|
||||
* @param t Time of receive
|
||||
*/
|
||||
inline void received(const uint64_t t) { _lastIn = t; }
|
||||
inline void received(uint64_t t)
|
||||
{
|
||||
_lastReceived = t;
|
||||
_probation = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set time last trusted packet was received (done in Peer::received())
|
||||
* @param now Current time
|
||||
* @return True if this path appears active
|
||||
*/
|
||||
inline void trustedPacketReceived(const uint64_t t) { _lastTrustEstablishedPacketReceived = t; }
|
||||
inline bool active(uint64_t now) const
|
||||
{
|
||||
return ( ((now - _lastReceived) < ZT_PATH_ACTIVITY_TIMEOUT) && (_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet via this path (last out time is also updated)
|
||||
* Send a packet via this path
|
||||
*
|
||||
* @param RR Runtime environment
|
||||
* @param data Packet data
|
||||
@@ -144,43 +151,117 @@ public:
|
||||
*/
|
||||
bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
|
||||
|
||||
/**
|
||||
* Manually update last sent time
|
||||
*
|
||||
* @param t Time of send
|
||||
*/
|
||||
inline void sent(const uint64_t t) { _lastOut = t; }
|
||||
|
||||
/**
|
||||
* @return Address of local side of this path or NULL if unspecified
|
||||
*/
|
||||
inline const InetAddress &localAddress() const { return _localAddress; }
|
||||
inline const InetAddress &localAddress() const throw() { return _localAddress; }
|
||||
|
||||
/**
|
||||
* @return Time of last send to this path
|
||||
*/
|
||||
inline uint64_t lastSend() const throw() { return _lastSend; }
|
||||
|
||||
/**
|
||||
* @return Time we last pinged or dead path checked this link
|
||||
*/
|
||||
inline uint64_t lastPing() const throw() { return _lastPing; }
|
||||
|
||||
/**
|
||||
* @return Time of last keepalive
|
||||
*/
|
||||
inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
|
||||
|
||||
/**
|
||||
* @return Time of last receive from this path
|
||||
*/
|
||||
inline uint64_t lastReceived() const throw() { return _lastReceived; }
|
||||
|
||||
/**
|
||||
* @return Physical address
|
||||
*/
|
||||
inline const InetAddress &address() const { return _addr; }
|
||||
inline const InetAddress &address() const throw() { return _addr; }
|
||||
|
||||
/**
|
||||
* @return IP scope -- faster shortcut for address().ipScope()
|
||||
*/
|
||||
inline InetAddress::IpScope ipScope() const { return _ipScope; }
|
||||
inline InetAddress::IpScope ipScope() const throw() { return _ipScope; }
|
||||
|
||||
/**
|
||||
* @return True if path has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
* @param f Valuve of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL and inverse of ZT_PATH_FLAG_CLUSTER_OPTIMAL (both are changed)
|
||||
*/
|
||||
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
|
||||
/**
|
||||
* @return Preference rank, higher == better
|
||||
*/
|
||||
inline unsigned int preferenceRank() const
|
||||
inline void setClusterSuboptimal(bool f)
|
||||
{
|
||||
// This causes us to rank paths in order of IP scope rank (see InetAdddress.hpp) but
|
||||
// within each IP scope class to prefer IPv6 over IPv4.
|
||||
if (f) {
|
||||
_flags = (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_OPTIMAL;
|
||||
} else {
|
||||
_flags = (_flags | ZT_PATH_FLAG_CLUSTER_OPTIMAL) & ~ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL is set
|
||||
*/
|
||||
inline bool isClusterSuboptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) != 0); }
|
||||
|
||||
/**
|
||||
* @return True if ZT_PATH_FLAG_CLUSTER_OPTIMAL is set
|
||||
*/
|
||||
inline bool isClusterOptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) != 0); }
|
||||
|
||||
/**
|
||||
* @return Preference rank, higher == better (will be less than 255)
|
||||
*/
|
||||
inline unsigned int preferenceRank() const throw()
|
||||
{
|
||||
/* First, since the scope enum values in InetAddress.hpp are in order of
|
||||
* use preference rank, we take that. Then we multiple by two, yielding
|
||||
* a sequence like 0, 2, 4, 6, etc. Then if it's IPv6 we add one. This
|
||||
* makes IPv6 addresses of a given scope outrank IPv4 addresses of the
|
||||
* same scope -- e.g. 1 outranks 0. This makes us prefer IPv6, but not
|
||||
* if the address scope/class is of a fundamentally lower rank. */
|
||||
return ( ((unsigned int)_ipScope << 1) | (unsigned int)(_addr.ss_family == AF_INET6) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @return This path's overall quality score (higher is better)
|
||||
*/
|
||||
inline uint64_t score() const throw()
|
||||
{
|
||||
// This is a little bit convoluted because we try to be branch-free, using multiplication instead of branches for boolean flags
|
||||
|
||||
// Start with the last time this path was active, and add a fudge factor to prevent integer underflow if _lastReceived is 0
|
||||
uint64_t score = _lastReceived + (ZT_PEER_DIRECT_PING_DELAY * (ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION + 1));
|
||||
|
||||
// Increase score based on path preference rank, which is based on IP scope and address family
|
||||
score += preferenceRank() * (ZT_PEER_DIRECT_PING_DELAY / ZT_PATH_MAX_PREFERENCE_RANK);
|
||||
|
||||
// Increase score if this is known to be an optimal path to a cluster
|
||||
score += (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_OPTIMAL) * (ZT_PEER_DIRECT_PING_DELAY / 2); // /2 because CLUSTER_OPTIMAL is flag 0x0002
|
||||
|
||||
// Decrease score if this is known to be a sub-optimal path to a cluster
|
||||
score -= (uint64_t)(_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) * ZT_PEER_DIRECT_PING_DELAY;
|
||||
|
||||
// Penalize for missed ECHO tests in dead path detection
|
||||
score -= (uint64_t)((ZT_PEER_DIRECT_PING_DELAY / 2) * _probation);
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if path is considered reliable (no NAT keepalives etc. are needed)
|
||||
*/
|
||||
inline bool reliable() const throw()
|
||||
{
|
||||
if (_addr.ss_family == AF_INET)
|
||||
return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if address is non-NULL
|
||||
*/
|
||||
inline operator bool() const throw() { return (_addr); }
|
||||
|
||||
/**
|
||||
* Check whether this address is valid for a ZeroTier path
|
||||
*
|
||||
@@ -191,6 +272,7 @@ public:
|
||||
* @return True if address is good for ZeroTier path use
|
||||
*/
|
||||
static inline bool isAddressValidForPath(const InetAddress &a)
|
||||
throw()
|
||||
{
|
||||
if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
|
||||
switch(a.ipScope()) {
|
||||
@@ -222,33 +304,60 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if path appears alive
|
||||
* @return Current path probation count (for dead path detect)
|
||||
*/
|
||||
inline bool alive(const uint64_t now) const { return ((now - _lastIn) <= ZT_PATH_ALIVE_TIMEOUT); }
|
||||
inline unsigned int probation() const { return _probation; }
|
||||
|
||||
/**
|
||||
* @return True if this path needs a heartbeat
|
||||
* Increase this path's probation violation count (for dead path detect)
|
||||
*/
|
||||
inline bool needsHeartbeat(const uint64_t now) const { return ((now - _lastOut) >= ZT_PATH_HEARTBEAT_PERIOD); }
|
||||
inline void increaseProbation() { ++_probation; }
|
||||
|
||||
/**
|
||||
* @return Last time we sent something
|
||||
*/
|
||||
inline uint64_t lastOut() const { return _lastOut; }
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
b.append((uint8_t)2); // version
|
||||
b.append((uint64_t)_lastSend);
|
||||
b.append((uint64_t)_lastPing);
|
||||
b.append((uint64_t)_lastKeepalive);
|
||||
b.append((uint64_t)_lastReceived);
|
||||
_addr.serialize(b);
|
||||
_localAddress.serialize(b);
|
||||
b.append((uint16_t)_flags);
|
||||
b.append((uint16_t)_probation);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Last time we received anything
|
||||
*/
|
||||
inline uint64_t lastIn() const { return _lastIn; }
|
||||
template<unsigned int C>
|
||||
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
|
||||
{
|
||||
unsigned int p = startAt;
|
||||
if (b[p++] != 2)
|
||||
throw std::invalid_argument("invalid serialized Path");
|
||||
_lastSend = b.template at<uint64_t>(p); p += 8;
|
||||
_lastPing = b.template at<uint64_t>(p); p += 8;
|
||||
_lastKeepalive = b.template at<uint64_t>(p); p += 8;
|
||||
_lastReceived = b.template at<uint64_t>(p); p += 8;
|
||||
p += _addr.deserialize(b,p);
|
||||
p += _localAddress.deserialize(b,p);
|
||||
_flags = b.template at<uint16_t>(p); p += 2;
|
||||
_probation = b.template at<uint16_t>(p); p += 2;
|
||||
_ipScope = _addr.ipScope();
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
inline bool operator==(const Path &p) const { return ((p._addr == _addr)&&(p._localAddress == _localAddress)); }
|
||||
inline bool operator!=(const Path &p) const { return ((p._addr != _addr)||(p._localAddress != _localAddress)); }
|
||||
|
||||
private:
|
||||
uint64_t _lastOut;
|
||||
uint64_t _lastIn;
|
||||
uint64_t _lastTrustEstablishedPacketReceived;
|
||||
uint64_t _lastSend;
|
||||
uint64_t _lastPing;
|
||||
uint64_t _lastKeepalive;
|
||||
uint64_t _lastReceived;
|
||||
InetAddress _addr;
|
||||
InetAddress _localAddress;
|
||||
unsigned int _flags;
|
||||
unsigned int _probation;
|
||||
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -27,13 +27,9 @@
|
||||
#include "Cluster.hpp"
|
||||
#include "Packet.hpp"
|
||||
|
||||
#ifndef AF_MAX
|
||||
#if AF_INET > AF_INET6
|
||||
#define AF_MAX AF_INET
|
||||
#else
|
||||
#define AF_MAX AF_INET6
|
||||
#endif
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#define ZT_PEER_PATH_SORT_INTERVAL 5000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
@@ -41,20 +37,15 @@ namespace ZeroTier {
|
||||
static uint32_t _natKeepaliveBuf = 0;
|
||||
|
||||
Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
|
||||
RR(renv),
|
||||
_lastUsed(0),
|
||||
_lastReceive(0),
|
||||
_lastUnicastFrame(0),
|
||||
_lastMulticastFrame(0),
|
||||
_lastAnnouncedTo(0),
|
||||
_lastDirectPathPushSent(0),
|
||||
_lastDirectPathPushReceive(0),
|
||||
_lastCredentialRequestSent(0),
|
||||
_lastWhoisRequestReceived(0),
|
||||
_lastEchoRequestReceived(0),
|
||||
_lastComRequestReceived(0),
|
||||
_lastComRequestSent(0),
|
||||
_lastCredentialsReceived(0),
|
||||
_lastTrustEstablishedPacketReceived(0),
|
||||
RR(renv),
|
||||
_remoteClusterOptimal4(0),
|
||||
_lastPathSort(0),
|
||||
_vProto(0),
|
||||
_vMajor(0),
|
||||
_vMinor(0),
|
||||
@@ -63,31 +54,29 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
|
||||
_numPaths(0),
|
||||
_latency(0),
|
||||
_directPathPushCutoffCount(0),
|
||||
_credentialsCutoffCount(0)
|
||||
_networkComs(4),
|
||||
_lastPushedComs(4)
|
||||
{
|
||||
memset(_remoteClusterOptimal6,0,sizeof(_remoteClusterOptimal6));
|
||||
if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
|
||||
throw std::runtime_error("new peer identity key agreement failed");
|
||||
}
|
||||
|
||||
void Peer::received(
|
||||
const SharedPtr<Path> &path,
|
||||
const unsigned int hops,
|
||||
const uint64_t packetId,
|
||||
const Packet::Verb verb,
|
||||
const uint64_t inRePacketId,
|
||||
const Packet::Verb inReVerb,
|
||||
const bool trustEstablished)
|
||||
const InetAddress &localAddr,
|
||||
const InetAddress &remoteAddr,
|
||||
unsigned int hops,
|
||||
uint64_t packetId,
|
||||
Packet::Verb verb,
|
||||
uint64_t inRePacketId,
|
||||
Packet::Verb inReVerb)
|
||||
{
|
||||
const uint64_t now = RR->node->now();
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
bool suboptimalPath = false;
|
||||
if ((RR->cluster)&&(hops == 0)) {
|
||||
// Note: findBetterEndpoint() is first since we still want to check
|
||||
// for a better endpoint even if we don't actually send a redirect.
|
||||
InetAddress redirectTo;
|
||||
if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),path->address(),false)) ) {
|
||||
if ( (verb != Packet::VERB_OK) && (verb != Packet::VERB_ERROR) && (verb != Packet::VERB_RENDEZVOUS) && (verb != Packet::VERB_PUSH_DIRECT_PATHS) && (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) ) {
|
||||
if (_vProto >= 5) {
|
||||
// For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||
@@ -105,7 +94,7 @@ void Peer::received(
|
||||
}
|
||||
outp.append((uint16_t)redirectTo.port());
|
||||
outp.armor(_key,true);
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
||||
} else {
|
||||
// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
@@ -120,226 +109,94 @@ void Peer::received(
|
||||
outp.append(redirectTo.rawIpData(),16);
|
||||
}
|
||||
outp.armor(_key,true);
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
||||
}
|
||||
suboptimalPath = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const uint64_t now = RR->node->now();
|
||||
_lastReceive = now;
|
||||
if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
|
||||
_lastUnicastFrame = now;
|
||||
else if (verb == Packet::VERB_MULTICAST_FRAME)
|
||||
_lastMulticastFrame = now;
|
||||
|
||||
if (trustEstablished) {
|
||||
_lastTrustEstablishedPacketReceived = now;
|
||||
path->trustedPacketReceived(now);
|
||||
}
|
||||
|
||||
if (hops == 0) {
|
||||
bool pathIsConfirmed = false;
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].path->address() == path->address()) {
|
||||
_paths[p].lastReceive = now;
|
||||
_paths[p].path = path; // local address may have changed!
|
||||
unsigned int np = _numPaths;
|
||||
for(unsigned int p=0;p<np;++p) {
|
||||
if ((_paths[p].address() == remoteAddr)&&(_paths[p].localAddress() == localAddr)) {
|
||||
_paths[p].received(now);
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
_paths[p].localClusterSuboptimal = suboptimalPath;
|
||||
_paths[p].setClusterSuboptimal(suboptimalPath);
|
||||
#endif
|
||||
pathIsConfirmed = true;
|
||||
break;
|
||||
}
|
||||
pathIsConfirmed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(path->localAddress(),path->address())) ) {
|
||||
if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
|
||||
if (verb == Packet::VERB_OK) {
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
// Since this is a new path, figure out where to put it (possibly replacing an old/dead one)
|
||||
unsigned int slot;
|
||||
if (_numPaths < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
slot = _numPaths++;
|
||||
Path *slot = (Path *)0;
|
||||
if (np < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
slot = &(_paths[np++]);
|
||||
} else {
|
||||
// First try to replace the worst within the same address family, if possible
|
||||
int worstSlot = -1;
|
||||
uint64_t worstScore = 0xffffffffffffffffULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].path->address().ss_family == path->address().ss_family) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s < worstScore) {
|
||||
worstScore = s;
|
||||
worstSlot = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (worstSlot >= 0) {
|
||||
slot = (unsigned int)worstSlot;
|
||||
} else {
|
||||
// If we can't find one with the same family, replace the worst of any family
|
||||
slot = ZT_MAX_PEER_NETWORK_PATHS - 1;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s < worstScore) {
|
||||
worstScore = s;
|
||||
slot = p;
|
||||
uint64_t slotWorstScore = 0xffffffffffffffffULL;
|
||||
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
|
||||
if (!_paths[p].active(now)) {
|
||||
slot = &(_paths[p]);
|
||||
break;
|
||||
} else {
|
||||
const uint64_t score = _paths[p].score();
|
||||
if (score <= slotWorstScore) {
|
||||
slotWorstScore = score;
|
||||
slot = &(_paths[p]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_paths[slot].lastReceive = now;
|
||||
_paths[slot].path = path;
|
||||
if (slot) {
|
||||
*slot = Path(localAddr,remoteAddr);
|
||||
slot->received(now);
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
slot->setClusterSuboptimal(suboptimalPath);
|
||||
#endif
|
||||
_numPaths = np;
|
||||
}
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
_paths[slot].localClusterSuboptimal = suboptimalPath;
|
||||
if (RR->cluster)
|
||||
RR->cluster->broadcastHavePeer(_id);
|
||||
#endif
|
||||
|
||||
} else {
|
||||
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
|
||||
attemptToContactAt(path->localAddress(),path->address(),now);
|
||||
path->sent(now);
|
||||
}
|
||||
}
|
||||
} else if (trustEstablished) {
|
||||
// Send PUSH_DIRECT_PATHS if hops>0 (relayed) and we have a trust relationship (common network membership)
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
||||
const bool haveCluster = (RR->cluster);
|
||||
#else
|
||||
const bool haveCluster = false;
|
||||
#endif
|
||||
if ( ((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL) && (!haveCluster) ) {
|
||||
_lastDirectPathPushSent = now;
|
||||
|
||||
std::vector<InetAddress> pathsToPush;
|
||||
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
|
||||
|
||||
std::vector<InetAddress> dps(RR->node->directPaths());
|
||||
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i)
|
||||
pathsToPush.push_back(*i);
|
||||
|
||||
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
||||
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
||||
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
|
||||
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
|
||||
pathsToPush.push_back(tmp);
|
||||
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
|
||||
break;
|
||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||
outp.armor(_key,true);
|
||||
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
|
||||
} else {
|
||||
sendHELLO(localAddr,remoteAddr,now);
|
||||
}
|
||||
}
|
||||
|
||||
if (pathsToPush.size() > 0) {
|
||||
#ifdef ZT_TRACE
|
||||
std::string ps;
|
||||
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
||||
if (ps.length() > 0)
|
||||
ps.push_back(',');
|
||||
ps.append(p->toString());
|
||||
}
|
||||
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
||||
#endif
|
||||
|
||||
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
||||
while (p != pathsToPush.end()) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||
outp.addSize(2); // leave room for count
|
||||
|
||||
unsigned int count = 0;
|
||||
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
|
||||
uint8_t addressType = 4;
|
||||
switch(p->ss_family) {
|
||||
case AF_INET:
|
||||
break;
|
||||
case AF_INET6:
|
||||
addressType = 6;
|
||||
break;
|
||||
default: // we currently only push IP addresses
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
outp.append((uint8_t)0); // no flags
|
||||
outp.append((uint16_t)0); // no extensions
|
||||
outp.append(addressType);
|
||||
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
|
||||
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
|
||||
outp.append((uint16_t)p->port());
|
||||
|
||||
++count;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
||||
outp.armor(_key,true);
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::hasActivePathTo(uint64_t now,const InetAddress &addr) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( (_paths[p].path->address() == addr) && ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)||(forceEvenIfDead)) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
return _paths[bestp].path->send(RR,data,len,now);
|
||||
} else {
|
||||
return false;
|
||||
if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
|
||||
_lastAnnouncedTo = now;
|
||||
const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
|
||||
for(std::vector< SharedPtr<Network> >::const_iterator n(networks.begin());n!=networks.end();++n)
|
||||
(*n)->tryAnnounceMulticastGroupsTo(SharedPtr<Peer>(this));
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) || (includeExpired) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
return _paths[bestp].path;
|
||||
} else {
|
||||
return SharedPtr<Path>();
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
|
||||
void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
|
||||
{
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
|
||||
outp.append((unsigned char)ZT_PROTO_VERSION);
|
||||
@@ -351,103 +208,351 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
|
||||
atAddress.serialize(outp);
|
||||
outp.append((uint64_t)RR->topology->worldId());
|
||||
outp.append((uint64_t)RR->topology->worldTimestamp());
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
outp.armor(_key,false); // HELLO is sent in the clear
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||
}
|
||||
|
||||
void Peer::attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
|
||||
{
|
||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
outp.armor(_key,true);
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
|
||||
} else {
|
||||
sendHELLO(localAddr,atAddress,now);
|
||||
}
|
||||
outp.armor(_key,false); // HELLO is sent in the clear
|
||||
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
|
||||
}
|
||||
|
||||
bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
Path *p = (Path *)0;
|
||||
|
||||
int bestp = -1;
|
||||
uint64_t best = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && ((inetAddressFamily < 0)||((int)_paths[p].path->address().ss_family == inetAddressFamily)) ) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best) {
|
||||
best = s;
|
||||
bestp = (int)p;
|
||||
}
|
||||
}
|
||||
if (inetAddressFamily != 0) {
|
||||
p = _getBestPath(now,inetAddressFamily);
|
||||
} else {
|
||||
p = _getBestPath(now);
|
||||
}
|
||||
|
||||
if (bestp >= 0) {
|
||||
if ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) {
|
||||
attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now);
|
||||
_paths[bestp].path->sent(now);
|
||||
} else if (_paths[bestp].path->needsHeartbeat(now)) {
|
||||
if (p) {
|
||||
if ((now - p->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
|
||||
//TRACE("PING %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
|
||||
sendHELLO(p->localAddress(),p->address(),now);
|
||||
p->sent(now);
|
||||
p->pinged(now);
|
||||
} else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
|
||||
//TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
|
||||
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
|
||||
_paths[bestp].path->send(RR,&_natKeepaliveBuf,sizeof(_natKeepaliveBuf),now);
|
||||
RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
|
||||
p->sentKeepalive(now);
|
||||
} else {
|
||||
//TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Peer::hasActiveDirectPath(uint64_t now) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION)&&(_paths[p].path->alive(now)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
|
||||
bool Peer::pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths)
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
|
||||
attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now);
|
||||
_paths[p].path->sent(now);
|
||||
_paths[p].lastReceive = 0; // path will not be used unless it speaks again
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
|
||||
if (RR->cluster)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (!force) {
|
||||
if ((now - _lastDirectPathPushSent) < ZT_DIRECT_PATH_PUSH_INTERVAL)
|
||||
return false;
|
||||
else _lastDirectPathPushSent = now;
|
||||
}
|
||||
|
||||
std::vector<InetAddress> pathsToPush;
|
||||
|
||||
std::vector<InetAddress> dps(RR->node->directPaths());
|
||||
for(std::vector<InetAddress>::const_iterator i(dps.begin());i!=dps.end();++i) {
|
||||
if ((includePrivatePaths)||(i->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
|
||||
pathsToPush.push_back(*i);
|
||||
}
|
||||
|
||||
std::vector<InetAddress> sym(RR->sa->getSymmetricNatPredictions());
|
||||
for(unsigned long i=0,added=0;i<sym.size();++i) {
|
||||
InetAddress tmp(sym[(unsigned long)RR->node->prng() % sym.size()]);
|
||||
if (std::find(pathsToPush.begin(),pathsToPush.end(),tmp) == pathsToPush.end()) {
|
||||
pathsToPush.push_back(tmp);
|
||||
if (++added >= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pathsToPush.empty())
|
||||
return false;
|
||||
|
||||
#ifdef ZT_TRACE
|
||||
{
|
||||
std::string ps;
|
||||
for(std::vector<InetAddress>::const_iterator p(pathsToPush.begin());p!=pathsToPush.end();++p) {
|
||||
if (ps.length() > 0)
|
||||
ps.push_back(',');
|
||||
ps.append(p->toString());
|
||||
}
|
||||
TRACE("pushing %u direct paths to %s: %s",(unsigned int)pathsToPush.size(),_id.address().toString().c_str(),ps.c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<InetAddress>::const_iterator p(pathsToPush.begin());
|
||||
while (p != pathsToPush.end()) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
|
||||
outp.addSize(2); // leave room for count
|
||||
|
||||
unsigned int count = 0;
|
||||
while ((p != pathsToPush.end())&&((outp.size() + 24) < 1200)) {
|
||||
uint8_t addressType = 4;
|
||||
switch(p->ss_family) {
|
||||
case AF_INET:
|
||||
break;
|
||||
case AF_INET6:
|
||||
addressType = 6;
|
||||
break;
|
||||
default: // we currently only push IP addresses
|
||||
++p;
|
||||
continue;
|
||||
}
|
||||
|
||||
outp.append((uint8_t)0); // no flags
|
||||
outp.append((uint16_t)0); // no extensions
|
||||
outp.append(addressType);
|
||||
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
|
||||
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
|
||||
outp.append((uint16_t)p->port());
|
||||
|
||||
++count;
|
||||
++p;
|
||||
}
|
||||
|
||||
if (count) {
|
||||
outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
|
||||
outp.armor(_key,true);
|
||||
RR->node->putPacket(localAddr,toAddress,outp.data(),outp.size(),0);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
|
||||
{
|
||||
unsigned int np = _numPaths;
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 0;
|
||||
while (x < np) {
|
||||
if (_paths[x].address().ipScope() == scope) {
|
||||
// Resetting a path means sending a HELLO and then forgetting it. If we
|
||||
// get OK(HELLO) then it will be re-learned.
|
||||
sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
|
||||
} else {
|
||||
_paths[y++] = _paths[x];
|
||||
}
|
||||
++x;
|
||||
}
|
||||
_numPaths = y;
|
||||
return (y < np);
|
||||
}
|
||||
|
||||
void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
|
||||
{
|
||||
Mutex::Lock _l(_paths_m);
|
||||
|
||||
int bestp4 = -1,bestp6 = -1;
|
||||
uint64_t best4 = 0ULL,best6 = 0ULL;
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ( ((now - _paths[p].lastReceive) <= ZT_PEER_PATH_EXPIRATION) && (_paths[p].path->alive(now)) ) {
|
||||
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best4) {
|
||||
best4 = s;
|
||||
bestp4 = (int)p;
|
||||
}
|
||||
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||
const uint64_t s = _pathScore(p,now);
|
||||
if (s >= best6) {
|
||||
best6 = s;
|
||||
bestp6 = (int)p;
|
||||
uint64_t bestV4 = 0,bestV6 = 0;
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
||||
if (_paths[p].active(now)) {
|
||||
uint64_t lr = _paths[p].lastReceived();
|
||||
if (lr) {
|
||||
if (_paths[p].address().isV4()) {
|
||||
if (lr >= bestV4) {
|
||||
bestV4 = lr;
|
||||
v4 = _paths[p].address();
|
||||
}
|
||||
} else if (_paths[p].address().isV6()) {
|
||||
if (lr >= bestV6) {
|
||||
bestV6 = lr;
|
||||
v6 = _paths[p].address();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestp4 >= 0)
|
||||
v4 = _paths[bestp4].path->address();
|
||||
if (bestp6 >= 0)
|
||||
v6 = _paths[bestp6].path->address();
|
||||
bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
const _NetworkCom *ourCom = _networkComs.get(nwid);
|
||||
if (ourCom)
|
||||
return ourCom->com.agreesWith(com);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
|
||||
{
|
||||
// Sanity checks
|
||||
if ((!com)||(com.issuedTo() != _id.address()))
|
||||
return false;
|
||||
|
||||
// Return true if we already have this *exact* COM
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
_NetworkCom *ourCom = _networkComs.get(nwid);
|
||||
if ((ourCom)&&(ourCom->com == com))
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check signature, log and return if cert is invalid
|
||||
if (com.signedBy() != Network::controllerFor(nwid)) {
|
||||
TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
||||
return false; // invalid signer
|
||||
}
|
||||
|
||||
if (com.signedBy() == RR->identity.address()) {
|
||||
|
||||
// We are the controller: RR->identity.address() == controller() == cert.signedBy()
|
||||
// So, verify that we signed th cert ourself
|
||||
if (!com.verify(RR->identity)) {
|
||||
TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
||||
return false; // invalid signature
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
SharedPtr<Peer> signer(RR->topology->getPeer(com.signedBy()));
|
||||
|
||||
if (!signer) {
|
||||
// This would be rather odd, since this is our controller... could happen
|
||||
// if we get packets before we've gotten config.
|
||||
RR->sw->requestWhois(com.signedBy());
|
||||
return false; // signer unknown
|
||||
}
|
||||
|
||||
if (!com.verify(signer->identity())) {
|
||||
TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)nwid,com.signedBy().toString().c_str());
|
||||
return false; // invalid signature
|
||||
}
|
||||
}
|
||||
|
||||
// If we made it past all those checks, add or update cert in our cert info store
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
_networkComs.set(nwid,_NetworkCom(RR->node->now(),com));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime)
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
uint64_t &lastPushed = _lastPushedComs[nwid];
|
||||
const uint64_t tmp = lastPushed;
|
||||
if (updateLastPushedTime)
|
||||
lastPushed = now;
|
||||
return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 3));
|
||||
}
|
||||
|
||||
void Peer::clean(uint64_t now)
|
||||
{
|
||||
{
|
||||
unsigned int np = _numPaths;
|
||||
unsigned int x = 0;
|
||||
unsigned int y = 0;
|
||||
while (x < np) {
|
||||
if (_paths[x].active(now))
|
||||
_paths[y++] = _paths[x];
|
||||
++x;
|
||||
}
|
||||
_numPaths = y;
|
||||
}
|
||||
|
||||
{
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
{
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
_NetworkCom *v = (_NetworkCom *)0;
|
||||
Hashtable< uint64_t,_NetworkCom >::Iterator i(_networkComs);
|
||||
while (i.next(k,v)) {
|
||||
if ( (!RR->node->belongsToNetwork(*k)) && ((now - v->ts) >= ZT_PEER_NETWORK_COM_EXPIRATION) )
|
||||
_networkComs.erase(*k);
|
||||
}
|
||||
}
|
||||
{
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
uint64_t *v = (uint64_t *)0;
|
||||
Hashtable< uint64_t,uint64_t >::Iterator i(_lastPushedComs);
|
||||
while (i.next(k,v)) {
|
||||
if ((now - *v) > (ZT_NETWORK_AUTOCONF_DELAY * 2))
|
||||
_lastPushedComs.erase(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Peer::_doDeadPathDetection(Path &p,const uint64_t now)
|
||||
{
|
||||
/* Dead path detection: if we have sent something to this peer and have not
|
||||
* yet received a reply, double check this path. The majority of outbound
|
||||
* packets including Ethernet frames do generate some kind of reply either
|
||||
* immediately or at some point in the near future. This will occasionally
|
||||
* (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
|
||||
* does not generate a response is being sent such as multicast announcements
|
||||
* or frames belonging to unidirectional UDP protocols, but the cost is very
|
||||
* tiny and the benefit in reliability is very large. This takes care of many
|
||||
* failure modes including crap NATs that forget links and spurious changes
|
||||
* to physical network topology that cannot be otherwise detected.
|
||||
*
|
||||
* Each time we do this we increment a probation counter in the path. This
|
||||
* counter is reset on any packet receive over this path. If it reaches the
|
||||
* MAX_PROBATION threshold the path is considred dead. */
|
||||
|
||||
if (
|
||||
(p.lastSend() > p.lastReceived()) &&
|
||||
((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
|
||||
((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
|
||||
(!p.isClusterSuboptimal()) &&
|
||||
(!RR->topology->amRoot())
|
||||
) {
|
||||
TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
|
||||
|
||||
if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
|
||||
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
|
||||
outp.armor(_key,true);
|
||||
p.send(RR,outp.data(),outp.size(),now);
|
||||
p.pinged(now);
|
||||
} else {
|
||||
sendHELLO(p.localAddress(),p.address(),now);
|
||||
p.sent(now);
|
||||
p.pinged(now);
|
||||
}
|
||||
|
||||
p.increaseProbation();
|
||||
}
|
||||
}
|
||||
|
||||
Path *Peer::_getBestPath(const uint64_t now)
|
||||
{
|
||||
Path *bestPath = (Path *)0;
|
||||
uint64_t bestPathScore = 0;
|
||||
for(unsigned int i=0;i<_numPaths;++i) {
|
||||
const uint64_t score = _paths[i].score();
|
||||
if ((score >= bestPathScore)&&(_paths[i].active(now))) {
|
||||
bestPathScore = score;
|
||||
bestPath = &(_paths[i]);
|
||||
}
|
||||
}
|
||||
if (bestPath)
|
||||
_doDeadPathDetection(*bestPath,now);
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
|
||||
{
|
||||
Path *bestPath = (Path *)0;
|
||||
uint64_t bestPathScore = 0;
|
||||
for(unsigned int i=0;i<_numPaths;++i) {
|
||||
const uint64_t score = _paths[i].score();
|
||||
if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
|
||||
bestPathScore = score;
|
||||
bestPath = &(_paths[i]);
|
||||
}
|
||||
}
|
||||
if (bestPath)
|
||||
_doDeadPathDetection(*bestPath,now);
|
||||
return bestPath;
|
||||
}
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "RuntimeEnvironment.hpp"
|
||||
#include "CertificateOfMembership.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Address.hpp"
|
||||
#include "Utils.hpp"
|
||||
@@ -43,6 +44,10 @@
|
||||
#include "Mutex.hpp"
|
||||
#include "NonCopyable.hpp"
|
||||
|
||||
// Very rough computed estimate: (8 + 256 + 80 + (16 * 64) + (128 * 256) + (128 * 16))
|
||||
// 1048576 provides tons of headroom -- overflow would just cause peer not to be persisted
|
||||
#define ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE 1048576
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
/**
|
||||
@@ -68,6 +73,18 @@ public:
|
||||
*/
|
||||
Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
|
||||
|
||||
/**
|
||||
* @return Time peer record was last used in any way
|
||||
*/
|
||||
inline uint64_t lastUsed() const throw() { return _lastUsed; }
|
||||
|
||||
/**
|
||||
* Log a use of this peer record (done by Topology when peers are looked up)
|
||||
*
|
||||
* @param now New time of last use
|
||||
*/
|
||||
inline void use(uint64_t now) throw() { _lastUsed = now; }
|
||||
|
||||
/**
|
||||
* @return This peer's ZT address (short for identity().address())
|
||||
*/
|
||||
@@ -84,167 +101,149 @@ public:
|
||||
* This is called by the decode pipe when a packet is proven to be authentic
|
||||
* and appears to be valid.
|
||||
*
|
||||
* @param path Path over which packet was received
|
||||
* @param RR Runtime environment
|
||||
* @param localAddr Local address
|
||||
* @param remoteAddr Internet address of sender
|
||||
* @param hops ZeroTier (not IP) hops
|
||||
* @param packetId Packet ID
|
||||
* @param verb Packet verb
|
||||
* @param inRePacketId Packet ID in reply to (default: none)
|
||||
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
|
||||
* @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
|
||||
*/
|
||||
void received(
|
||||
const SharedPtr<Path> &path,
|
||||
const unsigned int hops,
|
||||
const uint64_t packetId,
|
||||
const Packet::Verb verb,
|
||||
const uint64_t inRePacketId,
|
||||
const Packet::Verb inReVerb,
|
||||
const bool trustEstablished);
|
||||
const InetAddress &localAddr,
|
||||
const InetAddress &remoteAddr,
|
||||
unsigned int hops,
|
||||
uint64_t packetId,
|
||||
Packet::Verb verb,
|
||||
uint64_t inRePacketId = 0,
|
||||
Packet::Verb inReVerb = Packet::VERB_NOP);
|
||||
|
||||
/**
|
||||
* Get the current best direct path to this peer
|
||||
*
|
||||
* @param now Current time
|
||||
* @return Best path or NULL if there are no active direct paths
|
||||
*/
|
||||
inline Path *getBestPath(uint64_t now) { return _getBestPath(now); }
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @param addr Remote address
|
||||
* @return True if we have an active path to this destination
|
||||
*/
|
||||
bool hasActivePathTo(uint64_t now,const InetAddress &addr) const;
|
||||
inline bool hasActivePathTo(uint64_t now,const InetAddress &addr) const
|
||||
{
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if ((_paths[p].active(now))&&(_paths[p].address() == addr))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which known path for an address family is optimal
|
||||
* Set all paths in the same ss_family that are not this one to cluster suboptimal
|
||||
*
|
||||
* Addresses in other families are not affected.
|
||||
*
|
||||
* @param addr Address to make exclusive
|
||||
*/
|
||||
inline void setClusterOptimal(const InetAddress &addr)
|
||||
inline void setClusterOptimalPathForAddressFamily(const InetAddress &addr)
|
||||
{
|
||||
if (addr.ss_family == AF_INET) {
|
||||
_remoteClusterOptimal4 = (uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr;
|
||||
} else if (addr.ss_family == AF_INET6) {
|
||||
memcpy(_remoteClusterOptimal6,reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr,16);
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].address().ss_family == addr.ss_family) {
|
||||
_paths[p].setClusterSuboptimal(_paths[p].address() != addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send via best direct path
|
||||
* Send via best path
|
||||
*
|
||||
* @param data Packet data
|
||||
* @param len Packet length
|
||||
* @param now Current time
|
||||
* @param forceEvenIfDead If true, send even if the path is not 'alive'
|
||||
* @return True if we actually sent something
|
||||
* @return Path used on success or NULL on failure
|
||||
*/
|
||||
bool sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead);
|
||||
|
||||
/**
|
||||
* Get the best current direct path
|
||||
*
|
||||
* @param now Current time
|
||||
* @param includeDead If true, include even expired paths
|
||||
* @return Best current path or NULL if none
|
||||
*/
|
||||
SharedPtr<Path> getBestPath(uint64_t now,bool includeExpired);
|
||||
inline Path *send(const void *data,unsigned int len,uint64_t now)
|
||||
{
|
||||
Path *const bestPath = getBestPath(now);
|
||||
if (bestPath) {
|
||||
if (bestPath->send(RR,data,len,now))
|
||||
return bestPath;
|
||||
}
|
||||
return (Path *)0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a HELLO to this peer at a specified physical address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
* This does not update any statistics. It's used to send initial HELLOs
|
||||
* for NAT traversal and path verification.
|
||||
*
|
||||
* @param localAddr Local address
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
* @param ttl Desired IP TTL (default: 0 to leave alone)
|
||||
*/
|
||||
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
|
||||
|
||||
/**
|
||||
* Send ECHO (or HELLO for older peers) to this peer at the given address
|
||||
*
|
||||
* No statistics or sent times are updated here.
|
||||
*
|
||||
* @param localAddr Local address
|
||||
* @param atAddress Destination address
|
||||
* @param now Current time
|
||||
*/
|
||||
void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
|
||||
void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
|
||||
|
||||
/**
|
||||
* Send pings or keepalives depending on configured timeouts
|
||||
*
|
||||
* @param now Current time
|
||||
* @param inetAddressFamily Keep this address family alive, or -1 for any
|
||||
* @return True if we have at least one direct path of the given family (or any if family is -1)
|
||||
* @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
|
||||
* @return True if at least one direct path seems alive
|
||||
*/
|
||||
bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
|
||||
|
||||
/**
|
||||
* Push direct paths back to self if we haven't done so in the configured timeout
|
||||
*
|
||||
* @param localAddr Local address
|
||||
* @param toAddress Remote address to send push to (usually from path)
|
||||
* @param now Current time
|
||||
* @return True if this peer has at least one active and alive direct path
|
||||
* @param force If true, push regardless of rate limit
|
||||
* @param includePrivatePaths If true, include local interface address paths (should only be done to peers with a trust relationship)
|
||||
* @return True if something was actually sent
|
||||
*/
|
||||
bool hasActiveDirectPath(uint64_t now) const;
|
||||
bool pushDirectPaths(const InetAddress &localAddr,const InetAddress &toAddress,uint64_t now,bool force,bool includePrivatePaths);
|
||||
|
||||
/**
|
||||
* Reset paths within a given IP scope and address family
|
||||
*
|
||||
* Resetting a path involves sending an ECHO to it and then deactivating
|
||||
* it until or unless it responds.
|
||||
*
|
||||
* @param scope IP scope
|
||||
* @param inetAddressFamily Family e.g. AF_INET
|
||||
* @param now Current time
|
||||
* @return All known direct paths to this peer (active or inactive)
|
||||
*/
|
||||
void resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now);
|
||||
|
||||
/**
|
||||
* Get most recently active path addresses for IPv4 and/or IPv6
|
||||
*
|
||||
* Note that v4 and v6 are not modified if they are not found, so
|
||||
* initialize these to a NULL address to be able to check.
|
||||
*
|
||||
* @param now Current time
|
||||
* @param v4 Result parameter to receive active IPv4 address, if any
|
||||
* @param v6 Result parameter to receive active IPv6 address, if any
|
||||
*/
|
||||
void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return All known direct paths to this peer and whether they are expired (true == expired)
|
||||
*/
|
||||
inline std::vector< std::pair< SharedPtr<Path>,bool > > paths(const uint64_t now) const
|
||||
inline std::vector<Path> paths() const
|
||||
{
|
||||
std::vector< std::pair< SharedPtr<Path>,bool > > pp;
|
||||
Mutex::Lock _l(_paths_m);
|
||||
std::vector<Path> pp;
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p)
|
||||
pp.push_back(std::pair< SharedPtr<Path>,bool >(_paths[p].path,(now - _paths[p].lastReceive) > ZT_PEER_PATH_EXPIRATION));
|
||||
pp.push_back(_paths[p]);
|
||||
return pp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Time of last receive of anything, whether direct or relayed
|
||||
*/
|
||||
inline uint64_t lastReceive() const { return _lastReceive; }
|
||||
|
||||
/**
|
||||
* @return True if we've heard from this peer in less than ZT_PEER_ACTIVITY_TIMEOUT
|
||||
*/
|
||||
inline bool isAlive(const uint64_t now) const { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
inline uint64_t lastReceive() const throw() { return _lastReceive; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent unicast frame received
|
||||
*/
|
||||
inline uint64_t lastUnicastFrame() const { return _lastUnicastFrame; }
|
||||
inline uint64_t lastUnicastFrame() const throw() { return _lastUnicastFrame; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent multicast frame received
|
||||
*/
|
||||
inline uint64_t lastMulticastFrame() const { return _lastMulticastFrame; }
|
||||
inline uint64_t lastMulticastFrame() const throw() { return _lastMulticastFrame; }
|
||||
|
||||
/**
|
||||
* @return Time of most recent frame of any kind (unicast or multicast)
|
||||
*/
|
||||
inline uint64_t lastFrame() const { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
|
||||
inline uint64_t lastFrame() const throw() { return std::max(_lastUnicastFrame,_lastMulticastFrame); }
|
||||
|
||||
/**
|
||||
* @return True if this peer has sent us real network traffic recently
|
||||
*/
|
||||
inline uint64_t isActive(uint64_t now) const { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
|
||||
|
||||
/**
|
||||
* @return Latency in milliseconds or 0 if unknown
|
||||
@@ -270,7 +269,7 @@ public:
|
||||
unsigned int l = _latency;
|
||||
if (!l)
|
||||
l = 0xffff;
|
||||
return (l * (((unsigned int)tsr / (ZT_PEER_PING_PERIOD + 1000)) + 1));
|
||||
return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -286,25 +285,47 @@ public:
|
||||
else _latency = std::min(l,(unsigned int)65535);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return True if this peer has at least one active direct path
|
||||
*/
|
||||
inline bool hasActiveDirectPath(uint64_t now) const
|
||||
{
|
||||
for(unsigned int p=0;p<_numPaths;++p) {
|
||||
if (_paths[p].active(now))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
/**
|
||||
* @param now Current time
|
||||
* @return True if this peer has at least one active direct path that is not cluster-suboptimal
|
||||
*/
|
||||
inline bool hasLocalClusterOptimalPath(uint64_t now) const
|
||||
inline bool hasClusterOptimalPath(uint64_t now) const
|
||||
{
|
||||
for(unsigned int p=0,np=_numPaths;p<np;++p) {
|
||||
if ( (_paths[p].path->alive(now)) && (!_paths[p].localClusterSuboptimal) )
|
||||
if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reset paths within a given scope
|
||||
*
|
||||
* @param scope IP scope of paths to reset
|
||||
* @param now Current time
|
||||
* @return True if at least one path was forgotten
|
||||
*/
|
||||
bool resetWithinScope(InetAddress::IpScope scope,uint64_t now);
|
||||
|
||||
/**
|
||||
* @return 256-bit secret symmetric encryption key
|
||||
*/
|
||||
inline const unsigned char *key() const { return _key; }
|
||||
inline const unsigned char *key() const throw() { return _key; }
|
||||
|
||||
/**
|
||||
* Set the currently known remote version of this peer's client
|
||||
@@ -322,22 +343,69 @@ public:
|
||||
_vRevision = (uint16_t)vrev;
|
||||
}
|
||||
|
||||
inline unsigned int remoteVersionProtocol() const { return _vProto; }
|
||||
inline unsigned int remoteVersionMajor() const { return _vMajor; }
|
||||
inline unsigned int remoteVersionMinor() const { return _vMinor; }
|
||||
inline unsigned int remoteVersionRevision() const { return _vRevision; }
|
||||
inline unsigned int remoteVersionProtocol() const throw() { return _vProto; }
|
||||
inline unsigned int remoteVersionMajor() const throw() { return _vMajor; }
|
||||
inline unsigned int remoteVersionMinor() const throw() { return _vMinor; }
|
||||
inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
|
||||
|
||||
inline bool remoteVersionKnown() const { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||
inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
|
||||
|
||||
/**
|
||||
* @return True if peer has received a trust established packet (e.g. common network membership) in the past ZT_TRUST_EXPIRATION ms
|
||||
* Get most recently active path addresses for IPv4 and/or IPv6
|
||||
*
|
||||
* Note that v4 and v6 are not modified if they are not found, so
|
||||
* initialize these to a NULL address to be able to check.
|
||||
*
|
||||
* @param now Current time
|
||||
* @param v4 Result parameter to receive active IPv4 address, if any
|
||||
* @param v6 Result parameter to receive active IPv6 address, if any
|
||||
*/
|
||||
inline bool trustEstablished(const uint64_t now) const { return ((now - _lastTrustEstablishedPacketReceived) < ZT_TRUST_EXPIRATION); }
|
||||
void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_PUSH_DIRECT_PATHS
|
||||
* Check network COM agreement with this peer
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param com Another certificate of membership
|
||||
* @return True if supplied COM agrees with ours, false if not or if we don't have one
|
||||
*/
|
||||
inline bool rateGatePushDirectPaths(const uint64_t now)
|
||||
bool networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfMembership &com) const;
|
||||
|
||||
/**
|
||||
* Check the validity of the COM and add/update if valid and new
|
||||
*
|
||||
* @param nwid Network ID
|
||||
* @param com Externally supplied COM
|
||||
*/
|
||||
bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
|
||||
|
||||
/**
|
||||
* @param nwid Network ID
|
||||
* @param now Current time
|
||||
* @param updateLastPushedTime If true, go ahead and update the last pushed time regardless of return value
|
||||
* @return Whether or not this peer needs another COM push from us
|
||||
*/
|
||||
bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
|
||||
|
||||
/**
|
||||
* Perform periodic cleaning operations
|
||||
*
|
||||
* @param now Current time
|
||||
*/
|
||||
void clean(uint64_t now);
|
||||
|
||||
/**
|
||||
* Update direct path push stats and return true if we should respond
|
||||
*
|
||||
* This is a circuit breaker to make VERB_PUSH_DIRECT_PATHS not particularly
|
||||
* useful as a DDOS amplification attack vector. Otherwise a malicious peer
|
||||
* could send loads of these and cause others to bombard arbitrary IPs with
|
||||
* traffic.
|
||||
*
|
||||
* @param now Current time
|
||||
* @return True if we should respond
|
||||
*/
|
||||
inline bool shouldRespondToDirectPathPush(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
|
||||
++_directPathPushCutoffCount;
|
||||
@@ -346,78 +414,6 @@ public:
|
||||
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for VERB_NETWORK_CREDENTIALS
|
||||
*/
|
||||
inline bool rateGateCredentialsReceived(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialsReceived) <= ZT_PEER_CREDENTIALS_CUTOFF_TIME)
|
||||
++_credentialsCutoffCount;
|
||||
else _credentialsCutoffCount = 0;
|
||||
_lastCredentialsReceived = now;
|
||||
return (_directPathPushCutoffCount < ZT_PEER_CREDEITIALS_CUTOFF_LIMIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for sending of ERROR_NEED_MEMBERSHIP_CERTIFICATE
|
||||
*/
|
||||
inline bool rateGateRequestCredentials(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastCredentialRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastCredentialRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound WHOIS requests
|
||||
*/
|
||||
inline bool rateGateInboundWhoisRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastWhoisRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastWhoisRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate limit gate for inbound ECHO requests
|
||||
*/
|
||||
inline bool rateGateEchoRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastEchoRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastEchoRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate gate incoming requests for network COM
|
||||
*/
|
||||
inline bool rateGateIncomingComRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastComRequestReceived) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastComRequestReceived = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rate gate outgoing requests for network COM
|
||||
*/
|
||||
inline bool rateGateOutgoingComRequest(const uint64_t now)
|
||||
{
|
||||
if ((now - _lastComRequestSent) >= ZT_PEER_GENERAL_RATE_LIMIT) {
|
||||
_lastComRequestSent = now;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a common set of addresses by which two peers can link, if any
|
||||
*
|
||||
@@ -438,67 +434,168 @@ public:
|
||||
else return std::pair<InetAddress,InetAddress>();
|
||||
}
|
||||
|
||||
private:
|
||||
inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b) const
|
||||
{
|
||||
uint64_t s = ZT_PEER_PING_PERIOD + _paths[p].lastReceive + (uint64_t)(_paths[p].path->preferenceRank() * (ZT_PEER_PING_PERIOD / ZT_PATH_MAX_PREFERENCE_RANK));
|
||||
Mutex::Lock _l(_networkComs_m);
|
||||
|
||||
if (_paths[p].path->address().ss_family == AF_INET) {
|
||||
s += (uint64_t)(ZT_PEER_PING_PERIOD * (unsigned long)(reinterpret_cast<const struct sockaddr_in *>(&(_paths[p].path->address()))->sin_addr.s_addr == _remoteClusterOptimal4));
|
||||
} else if (_paths[p].path->address().ss_family == AF_INET6) {
|
||||
uint64_t clusterWeight = ZT_PEER_PING_PERIOD;
|
||||
const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(_paths[p].path->address()))->sin6_addr.s6_addr);
|
||||
for(long i=0;i<16;++i) {
|
||||
if (a[i] != _remoteClusterOptimal6[i]) {
|
||||
clusterWeight = 0;
|
||||
break;
|
||||
}
|
||||
const unsigned int recSizePos = b.size();
|
||||
b.addSize(4); // space for uint32_t field length
|
||||
|
||||
b.append((uint16_t)1); // version of serialized Peer data
|
||||
|
||||
_id.serialize(b,false);
|
||||
|
||||
b.append((uint64_t)_lastUsed);
|
||||
b.append((uint64_t)_lastReceive);
|
||||
b.append((uint64_t)_lastUnicastFrame);
|
||||
b.append((uint64_t)_lastMulticastFrame);
|
||||
b.append((uint64_t)_lastAnnouncedTo);
|
||||
b.append((uint64_t)_lastDirectPathPushSent);
|
||||
b.append((uint64_t)_lastDirectPathPushReceive);
|
||||
b.append((uint64_t)_lastPathSort);
|
||||
b.append((uint16_t)_vProto);
|
||||
b.append((uint16_t)_vMajor);
|
||||
b.append((uint16_t)_vMinor);
|
||||
b.append((uint16_t)_vRevision);
|
||||
b.append((uint32_t)_latency);
|
||||
b.append((uint16_t)_directPathPushCutoffCount);
|
||||
|
||||
b.append((uint16_t)_numPaths);
|
||||
for(unsigned int i=0;i<_numPaths;++i)
|
||||
_paths[i].serialize(b);
|
||||
|
||||
b.append((uint32_t)_networkComs.size());
|
||||
{
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
_NetworkCom *v = (_NetworkCom *)0;
|
||||
Hashtable<uint64_t,_NetworkCom>::Iterator i(const_cast<Peer *>(this)->_networkComs);
|
||||
while (i.next(k,v)) {
|
||||
b.append((uint64_t)*k);
|
||||
b.append((uint64_t)v->ts);
|
||||
v->com.serialize(b);
|
||||
}
|
||||
s += clusterWeight;
|
||||
}
|
||||
|
||||
s += (ZT_PEER_PING_PERIOD / 2) * (uint64_t)_paths[p].path->alive(now);
|
||||
b.append((uint32_t)_lastPushedComs.size());
|
||||
{
|
||||
uint64_t *k = (uint64_t *)0;
|
||||
uint64_t *v = (uint64_t *)0;
|
||||
Hashtable<uint64_t,uint64_t>::Iterator i(const_cast<Peer *>(this)->_lastPushedComs);
|
||||
while (i.next(k,v)) {
|
||||
b.append((uint64_t)*k);
|
||||
b.append((uint64_t)*v);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
s -= ZT_PEER_PING_PERIOD * (uint64_t)_paths[p].localClusterSuboptimal;
|
||||
#endif
|
||||
|
||||
return s;
|
||||
b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
|
||||
}
|
||||
|
||||
uint8_t _key[ZT_PEER_SECRET_KEY_LENGTH];
|
||||
uint8_t _remoteClusterOptimal6[16];
|
||||
/**
|
||||
* Create a new Peer from a serialized instance
|
||||
*
|
||||
* @param renv Runtime environment
|
||||
* @param myIdentity This node's identity
|
||||
* @param b Buffer containing serialized Peer data
|
||||
* @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result)
|
||||
* @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer)
|
||||
*/
|
||||
template<unsigned int C>
|
||||
static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
|
||||
{
|
||||
const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
|
||||
if ((p + recSize) > b.size())
|
||||
return SharedPtr<Peer>(); // size invalid
|
||||
if (b.template at<uint16_t>(p) != 1)
|
||||
return SharedPtr<Peer>(); // version mismatch
|
||||
p += 2;
|
||||
|
||||
Identity npid;
|
||||
p += npid.deserialize(b,p);
|
||||
if (!npid)
|
||||
return SharedPtr<Peer>();
|
||||
|
||||
SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
|
||||
|
||||
np->_lastUsed = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastReceive = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastUnicastFrame = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
|
||||
np->_vProto = b.template at<uint16_t>(p); p += 2;
|
||||
np->_vMajor = b.template at<uint16_t>(p); p += 2;
|
||||
np->_vMinor = b.template at<uint16_t>(p); p += 2;
|
||||
np->_vRevision = b.template at<uint16_t>(p); p += 2;
|
||||
np->_latency = b.template at<uint32_t>(p); p += 4;
|
||||
np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
|
||||
|
||||
const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
|
||||
for(unsigned int i=0;i<numPaths;++i) {
|
||||
if (i < ZT_MAX_PEER_NETWORK_PATHS) {
|
||||
p += np->_paths[np->_numPaths++].deserialize(b,p);
|
||||
} else {
|
||||
// Skip any paths beyond max, but still read stream
|
||||
Path foo;
|
||||
p += foo.deserialize(b,p);
|
||||
}
|
||||
}
|
||||
|
||||
const unsigned int numNetworkComs = b.template at<uint32_t>(p); p += 4;
|
||||
for(unsigned int i=0;i<numNetworkComs;++i) {
|
||||
_NetworkCom &c = np->_networkComs[b.template at<uint64_t>(p)]; p += 8;
|
||||
c.ts = b.template at<uint64_t>(p); p += 8;
|
||||
p += c.com.deserialize(b,p);
|
||||
}
|
||||
|
||||
const unsigned int numLastPushed = b.template at<uint32_t>(p); p += 4;
|
||||
for(unsigned int i=0;i<numLastPushed;++i) {
|
||||
const uint64_t nwid = b.template at<uint64_t>(p); p += 8;
|
||||
const uint64_t ts = b.template at<uint64_t>(p); p += 8;
|
||||
np->_lastPushedComs.set(nwid,ts);
|
||||
}
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
private:
|
||||
void _doDeadPathDetection(Path &p,const uint64_t now);
|
||||
Path *_getBestPath(const uint64_t now);
|
||||
Path *_getBestPath(const uint64_t now,int inetAddressFamily);
|
||||
|
||||
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
uint64_t _lastUsed;
|
||||
uint64_t _lastReceive; // direct or indirect
|
||||
uint64_t _lastUnicastFrame;
|
||||
uint64_t _lastMulticastFrame;
|
||||
uint64_t _lastAnnouncedTo;
|
||||
uint64_t _lastDirectPathPushSent;
|
||||
uint64_t _lastDirectPathPushReceive;
|
||||
uint64_t _lastCredentialRequestSent;
|
||||
uint64_t _lastWhoisRequestReceived;
|
||||
uint64_t _lastEchoRequestReceived;
|
||||
uint64_t _lastComRequestReceived;
|
||||
uint64_t _lastComRequestSent;
|
||||
uint64_t _lastCredentialsReceived;
|
||||
uint64_t _lastTrustEstablishedPacketReceived;
|
||||
const RuntimeEnvironment *RR;
|
||||
uint32_t _remoteClusterOptimal4;
|
||||
uint64_t _lastPathSort;
|
||||
uint16_t _vProto;
|
||||
uint16_t _vMajor;
|
||||
uint16_t _vMinor;
|
||||
uint16_t _vRevision;
|
||||
Identity _id;
|
||||
struct {
|
||||
uint64_t lastReceive;
|
||||
SharedPtr<Path> path;
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
bool localClusterSuboptimal;
|
||||
#endif
|
||||
} _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||
Mutex _paths_m;
|
||||
Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
|
||||
unsigned int _numPaths;
|
||||
unsigned int _latency;
|
||||
unsigned int _directPathPushCutoffCount;
|
||||
unsigned int _credentialsCutoffCount;
|
||||
|
||||
struct _NetworkCom
|
||||
{
|
||||
_NetworkCom() {}
|
||||
_NetworkCom(uint64_t t,const CertificateOfMembership &c) : ts(t),com(c) {}
|
||||
uint64_t ts;
|
||||
CertificateOfMembership com;
|
||||
};
|
||||
Hashtable<uint64_t,_NetworkCom> _networkComs;
|
||||
Hashtable<uint64_t,uint64_t> _lastPushedComs;
|
||||
Mutex _networkComs_m;
|
||||
|
||||
AtomicCounter __refCount;
|
||||
};
|
||||
|
||||
@@ -35,6 +35,7 @@ class Multicaster;
|
||||
class NetworkController;
|
||||
class SelfAwareness;
|
||||
class Cluster;
|
||||
class DeferredPackets;
|
||||
|
||||
/**
|
||||
* Holds global state for an instance of ZeroTier::Node
|
||||
@@ -50,9 +51,11 @@ public:
|
||||
,mc((Multicaster *)0)
|
||||
,topology((Topology *)0)
|
||||
,sa((SelfAwareness *)0)
|
||||
,dp((DeferredPackets *)0)
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
,cluster((Cluster *)0)
|
||||
#endif
|
||||
,dpEnabled(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -79,9 +82,15 @@ public:
|
||||
Multicaster *mc;
|
||||
Topology *topology;
|
||||
SelfAwareness *sa;
|
||||
DeferredPackets *dp;
|
||||
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
Cluster *cluster;
|
||||
#endif
|
||||
|
||||
// This is set to >0 if background threads are waiting on deferred
|
||||
// packets, otherwise 'dp' should not be used.
|
||||
volatile int dpEnabled;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -33,29 +33,37 @@
|
||||
#include "Switch.hpp"
|
||||
|
||||
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
|
||||
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 600000
|
||||
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
|
||||
|
||||
namespace ZeroTier {
|
||||
|
||||
class _ResetWithinScope
|
||||
{
|
||||
public:
|
||||
_ResetWithinScope(uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
|
||||
_ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
|
||||
_now(now),
|
||||
_family(inetAddressFamily),
|
||||
_scope(scope) {}
|
||||
|
||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_scope,_family,_now); }
|
||||
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
|
||||
{
|
||||
if (p->resetWithinScope(_scope,_now))
|
||||
peersReset.push_back(p);
|
||||
}
|
||||
|
||||
std::vector< SharedPtr<Peer> > peersReset;
|
||||
|
||||
private:
|
||||
uint64_t _now;
|
||||
int _family;
|
||||
InetAddress::IpScope _scope;
|
||||
};
|
||||
|
||||
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_phy(128)
|
||||
_phy(32)
|
||||
{
|
||||
}
|
||||
|
||||
SelfAwareness::~SelfAwareness()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -71,11 +79,9 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
|
||||
|
||||
if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
|
||||
// Changes to external surface reported by trusted peers causes path reset in this scope
|
||||
TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
|
||||
|
||||
entry.mySurface = myPhysicalAddress;
|
||||
entry.ts = now;
|
||||
entry.trusted = trusted;
|
||||
TRACE("physical address %s for scope %u as seen from %s(%s) differs from %s, resetting paths in scope",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
|
||||
|
||||
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
|
||||
// due to multiple reports of endpoint change.
|
||||
@@ -90,14 +96,23 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
|
||||
}
|
||||
}
|
||||
|
||||
// Reset all paths within this scope and address family
|
||||
_ResetWithinScope rset(now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
|
||||
// Reset all paths within this scope
|
||||
_ResetWithinScope rset(now,(InetAddress::IpScope)scope);
|
||||
RR->topology->eachPeer<_ResetWithinScope &>(rset);
|
||||
|
||||
// Send a NOP to all peers for whom we forgot a path. This will cause direct
|
||||
// links to be re-established if possible, possibly using a root server or some
|
||||
// other relay.
|
||||
for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
|
||||
if ((*p)->activelyTransferringFrames(now)) {
|
||||
Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||
RR->sw->send(outp,true,0);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Otherwise just update DB to use to determine external surface info
|
||||
entry.mySurface = myPhysicalAddress;
|
||||
entry.ts = now;
|
||||
entry.trusted = trusted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,70 +133,49 @@ std::vector<InetAddress> SelfAwareness::getSymmetricNatPredictions()
|
||||
/* This is based on ideas and strategies found here:
|
||||
* https://tools.ietf.org/html/draft-takeda-symmetric-nat-traversal-00
|
||||
*
|
||||
* For each IP address reported by a trusted (upstream) peer, we find
|
||||
* the external port most recently reported by ANY peer for that IP.
|
||||
*
|
||||
* We only do any of this for global IPv4 addresses since private IPs
|
||||
* and IPv6 are not going to have symmetric NAT.
|
||||
*
|
||||
* SECURITY NOTE:
|
||||
*
|
||||
* We never use IPs reported by non-trusted peers, since this could lead
|
||||
* to a minor vulnerability whereby a peer could poison our cache with
|
||||
* bad external surface reports via OK(HELLO) and then possibly coax us
|
||||
* into suggesting their IP to other peers via PUSH_DIRECT_PATHS. This
|
||||
* in turn could allow them to MITM flows.
|
||||
*
|
||||
* Since flows are encrypted and authenticated they could not actually
|
||||
* read or modify traffic, but they could gather meta-data for forensics
|
||||
* purpsoes or use this as a DOS attack vector. */
|
||||
* In short: a great many symmetric NATs allocate ports sequentially.
|
||||
* This is common on enterprise and carrier grade NATs as well as consumer
|
||||
* devices. This code generates a list of "you might try this" addresses by
|
||||
* extrapolating likely port assignments from currently known external
|
||||
* global IPv4 surfaces. These can then be included in a PUSH_DIRECT_PATHS
|
||||
* message to another peer, causing it to possibly try these addresses and
|
||||
* bust our local symmetric NAT. It works often enough to be worth the
|
||||
* extra bit of code and does no harm in cases where it fails. */
|
||||
|
||||
std::map< uint32_t,std::pair<uint64_t,unsigned int> > maxPortByIp;
|
||||
InetAddress theOneTrueSurface;
|
||||
// Gather unique surfaces indexed by local received-on address and flag
|
||||
// us as behind a symmetric NAT if there is more than one.
|
||||
std::map< InetAddress,std::set<InetAddress> > surfaces;
|
||||
bool symmetric = false;
|
||||
{
|
||||
Mutex::Lock _l(_phy_m);
|
||||
|
||||
{ // First get IPs from only trusted peers, and perform basic NAT type characterization
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((e->trusted)&&(e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||
if (!theOneTrueSurface)
|
||||
theOneTrueSurface = e->mySurface;
|
||||
else if (theOneTrueSurface != e->mySurface)
|
||||
symmetric = true;
|
||||
maxPortByIp[reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr] = std::pair<uint64_t,unsigned int>(e->ts,e->mySurface.port());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{ // Then find max port per IP from a trusted peer
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||
std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator mp(maxPortByIp.find(reinterpret_cast<const struct sockaddr_in *>(&(e->mySurface))->sin_addr.s_addr));
|
||||
if ((mp != maxPortByIp.end())&&(mp->second.first < e->ts)) {
|
||||
mp->second.first = e->ts;
|
||||
mp->second.second = e->mySurface.port();
|
||||
}
|
||||
}
|
||||
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
|
||||
PhySurfaceKey *k = (PhySurfaceKey *)0;
|
||||
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
|
||||
while (i.next(k,e)) {
|
||||
if ((e->mySurface.ss_family == AF_INET)&&(e->mySurface.ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
|
||||
std::set<InetAddress> &s = surfaces[k->receivedOnLocalAddress];
|
||||
s.insert(e->mySurface);
|
||||
symmetric = symmetric||(s.size() > 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we appear to be symmetrically NATed, generate and return extrapolations
|
||||
// of those surfaces. Since PUSH_DIRECT_PATHS is sent multiple times, we
|
||||
// probabilistically generate extrapolations of anywhere from +1 to +5 to
|
||||
// increase the odds that it will work "eventually".
|
||||
if (symmetric) {
|
||||
std::vector<InetAddress> r;
|
||||
for(unsigned int k=1;k<=3;++k) {
|
||||
for(std::map< uint32_t,std::pair<uint64_t,unsigned int> >::iterator i(maxPortByIp.begin());i!=maxPortByIp.end();++i) {
|
||||
unsigned int p = i->second.second + k;
|
||||
if (p > 65535) p -= 64511;
|
||||
InetAddress pred(&(i->first),4,p);
|
||||
if (std::find(r.begin(),r.end(),pred) == r.end())
|
||||
r.push_back(pred);
|
||||
for(std::map< InetAddress,std::set<InetAddress> >::iterator si(surfaces.begin());si!=surfaces.end();++si) {
|
||||
for(std::set<InetAddress>::iterator i(si->second.begin());i!=si->second.end();++i) {
|
||||
InetAddress ipp(*i);
|
||||
unsigned int p = ipp.port() + 1 + ((unsigned int)RR->node->prng() & 3);
|
||||
if (p >= 65535)
|
||||
p -= 64510; // NATs seldom use ports <=1024 so wrap to 1025
|
||||
ipp.setPort(p);
|
||||
if ((si->second.count(ipp) == 0)&&(std::find(r.begin(),r.end(),ipp) == r.end())) {
|
||||
r.push_back(ipp);
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
|
||||
@@ -36,6 +36,7 @@ class SelfAwareness
|
||||
{
|
||||
public:
|
||||
SelfAwareness(const RuntimeEnvironment *renv);
|
||||
~SelfAwareness();
|
||||
|
||||
/**
|
||||
* Called when a trusted remote peer informs us of our external network address
|
||||
@@ -81,10 +82,9 @@ private:
|
||||
{
|
||||
InetAddress mySurface;
|
||||
uint64_t ts;
|
||||
bool trusted;
|
||||
|
||||
PhySurfaceEntry() : mySurface(),ts(0),trusted(false) {}
|
||||
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t),trusted(false) {}
|
||||
PhySurfaceEntry() : mySurface(),ts(0) {}
|
||||
PhySurfaceEntry(const InetAddress &a,const uint64_t t) : mySurface(a),ts(t) {}
|
||||
};
|
||||
|
||||
const RuntimeEnvironment *RR;
|
||||
|
||||
@@ -119,39 +119,15 @@ public:
|
||||
inline T *ptr() const throw() { return _ptr; }
|
||||
|
||||
/**
|
||||
* Set this pointer to NULL
|
||||
* Set this pointer to null
|
||||
*/
|
||||
inline void zero()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (--_ptr->__refCount <= 0)
|
||||
delete _ptr;
|
||||
_ptr = (T *)0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this pointer to NULL if this is the only pointer holding the object
|
||||
*
|
||||
* @return True if object was deleted and SharedPtr is now NULL (or was already NULL)
|
||||
*/
|
||||
inline bool reclaimIfWeak()
|
||||
{
|
||||
if (_ptr) {
|
||||
if (++_ptr->__refCount <= 2) {
|
||||
if (--_ptr->__refCount <= 1) {
|
||||
delete _ptr;
|
||||
_ptr = (T *)0;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
_ptr = (T *)0;
|
||||
}
|
||||
|
||||
inline bool operator==(const SharedPtr &sp) const throw() { return (_ptr == sp._ptr); }
|
||||
|
||||
@@ -73,9 +73,6 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
try {
|
||||
const uint64_t now = RR->node->now();
|
||||
|
||||
SharedPtr<Path> path(RR->topology->getPath(localAddr,fromAddr));
|
||||
path->received(now);
|
||||
|
||||
if (len == 13) {
|
||||
/* LEGACY: before VERB_PUSH_DIRECT_PATHS, peers used broadcast
|
||||
* announcements on the LAN to solve the 'same network problem.' We
|
||||
@@ -93,11 +90,11 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
_lastBeaconResponse = now;
|
||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
|
||||
outp.armor(peer->key(),true);
|
||||
path->send(RR,outp.data(),outp.size(),now);
|
||||
RR->node->putPacket(localAddr,fromAddr,outp.data(),outp.size());
|
||||
}
|
||||
}
|
||||
|
||||
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // SECURITY: min length check is important since we do some C-style stuff below!
|
||||
} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { // min length check is important!
|
||||
if (reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
|
||||
// Handle fragment ----------------------------------------------------
|
||||
|
||||
@@ -105,25 +102,14 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
const Address destination(fragment.destination());
|
||||
|
||||
if (destination != RR->identity.address()) {
|
||||
switch(RR->node->relayPolicy()) {
|
||||
case ZT_RELAY_POLICY_ALWAYS:
|
||||
break;
|
||||
case ZT_RELAY_POLICY_TRUSTED:
|
||||
if (!path->trustEstablished(now))
|
||||
return;
|
||||
break;
|
||||
// case ZT_RELAY_POLICY_NEVER:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
// Fragment is not for us, so try to relay it
|
||||
if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
|
||||
fragment.incrementHops();
|
||||
|
||||
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
|
||||
// It wouldn't hurt anything, just redundant and unnecessary.
|
||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||
if ((!relayTo)||(!relayTo->sendDirect(fragment.data(),fragment.size(),now,false))) {
|
||||
if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),now))) {
|
||||
#ifdef ZT_ENABLE_CLUSTER
|
||||
if (RR->cluster) {
|
||||
RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
|
||||
@@ -134,7 +120,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
// Don't know peer or no direct path -- so relay via root server
|
||||
relayTo = RR->topology->getBestRoot();
|
||||
if (relayTo)
|
||||
relayTo->sendDirect(fragment.data(),fragment.size(),now,true);
|
||||
relayTo->send(fragment.data(),fragment.size(),now);
|
||||
}
|
||||
} else {
|
||||
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
|
||||
@@ -178,7 +164,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
for(unsigned int f=1;f<totalFragments;++f)
|
||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||
|
||||
if (rq->frag0.tryDecode(RR)) {
|
||||
if (rq->frag0.tryDecode(RR,false)) {
|
||||
rq->timestamp = 0; // packet decoded, free entry
|
||||
} else {
|
||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||
@@ -214,25 +200,14 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
|
||||
|
||||
if (destination != RR->identity.address()) {
|
||||
switch(RR->node->relayPolicy()) {
|
||||
case ZT_RELAY_POLICY_ALWAYS:
|
||||
break;
|
||||
case ZT_RELAY_POLICY_TRUSTED:
|
||||
if (!path->trustEstablished(now))
|
||||
return;
|
||||
break;
|
||||
// case ZT_RELAY_POLICY_NEVER:
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Packet packet(data,len);
|
||||
|
||||
// Packet is not for us, so try to relay it
|
||||
if (packet.hops() < ZT_RELAY_MAX_HOPS) {
|
||||
packet.incrementHops();
|
||||
|
||||
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
|
||||
if ((relayTo)&&((relayTo->sendDirect(packet.data(),packet.size(),now,false)))) {
|
||||
if ((relayTo)&&((relayTo->send(packet.data(),packet.size(),now)))) {
|
||||
Mutex::Lock _l(_lastUniteAttempt_m);
|
||||
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
|
||||
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
|
||||
@@ -256,7 +231,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
#endif
|
||||
relayTo = RR->topology->getBestRoot(&source,1,true);
|
||||
if (relayTo)
|
||||
relayTo->sendDirect(packet.data(),packet.size(),now,true);
|
||||
relayTo->send(packet.data(),packet.size(),now);
|
||||
}
|
||||
} else {
|
||||
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
|
||||
@@ -273,7 +248,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
|
||||
rq->timestamp = now;
|
||||
rq->packetId = packetId;
|
||||
rq->frag0.init(data,len,path,now);
|
||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
||||
rq->totalFragments = 0;
|
||||
rq->haveFragments = 1;
|
||||
rq->complete = false;
|
||||
@@ -284,24 +259,24 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
|
||||
// We have all fragments -- assemble and process full Packet
|
||||
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
|
||||
|
||||
rq->frag0.init(data,len,path,now);
|
||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
||||
for(unsigned int f=1;f<rq->totalFragments;++f)
|
||||
rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
|
||||
|
||||
if (rq->frag0.tryDecode(RR)) {
|
||||
if (rq->frag0.tryDecode(RR,false)) {
|
||||
rq->timestamp = 0; // packet decoded, free entry
|
||||
} else {
|
||||
rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
|
||||
}
|
||||
} else {
|
||||
// Still waiting on more fragments, but keep the head
|
||||
rq->frag0.init(data,len,path,now);
|
||||
rq->frag0.init(data,len,localAddr,fromAddr,now);
|
||||
}
|
||||
} // else this is a duplicate head, ignore
|
||||
} else {
|
||||
// Packet is unfragmented, so just process it
|
||||
IncomingPacket packet(data,len,path,now);
|
||||
if (!packet.tryDecode(RR)) {
|
||||
IncomingPacket packet(data,len,localAddr,fromAddr,now);
|
||||
if (!packet.tryDecode(RR,false)) {
|
||||
Mutex::Lock _l(_rxQueue_m);
|
||||
RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
|
||||
unsigned long i = ZT_RX_QUEUE_SIZE - 1;
|
||||
@@ -338,6 +313,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||
if (to == network->mac())
|
||||
return;
|
||||
|
||||
// Check to make sure this protocol is allowed on this network
|
||||
if (!network->config().permitsEtherType(etherType)) {
|
||||
TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this packet is from someone other than the tap -- i.e. bridged in
|
||||
bool fromBridged = false;
|
||||
if (from != network->mac()) {
|
||||
@@ -349,7 +330,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||
}
|
||||
|
||||
if (to.isMulticast()) {
|
||||
MulticastGroup multicastGroup(to,0);
|
||||
// Destination is a multicast address (including broadcast)
|
||||
MulticastGroup mg(to,0);
|
||||
|
||||
if (to.isBroadcast()) {
|
||||
if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
|
||||
@@ -362,100 +344,75 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||
* them into multicasts by stuffing the IP address being queried into
|
||||
* the 32-bit ADI field. In practice this uses our multicast pub/sub
|
||||
* system to implement a kind of extended/distributed ARP table. */
|
||||
multicastGroup = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
|
||||
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
|
||||
} else if (!network->config().enableBroadcast()) {
|
||||
// Don't transmit broadcasts if this network doesn't want them
|
||||
TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
|
||||
return;
|
||||
}
|
||||
} else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
|
||||
// IPv6 NDP emulation for certain very special patterns of private IPv6 addresses -- if enabled
|
||||
if ((network->config().ndpEmulation())&&(reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
|
||||
Address v6EmbeddedAddress;
|
||||
const uint8_t *const pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
|
||||
const uint8_t *my6 = (const uint8_t *)0;
|
||||
|
||||
// ZT-RFC4193 address: fdNN:NNNN:NNNN:NNNN:NN99:93DD:DDDD:DDDD / 88 (one /128 per actual host)
|
||||
|
||||
// ZT-6PLANE address: fcXX:XXXX:XXDD:DDDD:DDDD:####:####:#### / 40 (one /80 per actual host)
|
||||
// (XX - lower 32 bits of network ID XORed with higher 32 bits)
|
||||
|
||||
// For these to work, we must have a ZT-managed address assigned in one of the
|
||||
// above formats, and the query must match its prefix.
|
||||
/* IPv6 NDP emulation on ZeroTier-RFC4193 addressed networks! This allows
|
||||
* for multicast-free operation in IPv6 networks, which both improves
|
||||
* performance and is friendlier to mobile and (especially) IoT devices.
|
||||
* In the future there may be a no-multicast build option for embedded
|
||||
* and IoT use and this will be the preferred addressing mode. Note that
|
||||
* it plays nice with our L2 emulation philosophy and even with bridging.
|
||||
* While "real" devices behind the bridge can't have ZT-RFC4193 addresses
|
||||
* themselves, they can look these addresses up with NDP and it will
|
||||
* work just fine. */
|
||||
if ((reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
|
||||
for(unsigned int sipk=0;sipk<network->config().staticIpCount;++sipk) {
|
||||
const InetAddress *const sip = &(network->config().staticIps[sipk]);
|
||||
if (sip->ss_family == AF_INET6) {
|
||||
my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
|
||||
const unsigned int sipNetmaskBits = Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port);
|
||||
if ((sipNetmaskBits == 88)&&(my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 /88 ???
|
||||
const InetAddress *sip = &(network->config().staticIps[sipk]);
|
||||
if ((sip->ss_family == AF_INET6)&&(Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port) == 88)) {
|
||||
const uint8_t *my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
|
||||
if ((my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 == fd__:____:____:____:__99:93__:____:____ / 88
|
||||
const uint8_t *pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
|
||||
unsigned int ptr = 0;
|
||||
while (ptr != 11) {
|
||||
if (pkt6[ptr] != my6[ptr])
|
||||
break;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr == 11) { // prefix match!
|
||||
v6EmbeddedAddress.setTo(pkt6 + ptr,5);
|
||||
break;
|
||||
}
|
||||
} else if (sipNetmaskBits == 40) { // ZT-6PLANE /40 ???
|
||||
const uint32_t nwid32 = (uint32_t)((network->id() ^ (network->id() >> 32)) & 0xffffffff);
|
||||
if ( (my6[0] == 0xfc) && (my6[1] == (uint8_t)((nwid32 >> 24) & 0xff)) && (my6[2] == (uint8_t)((nwid32 >> 16) & 0xff)) && (my6[3] == (uint8_t)((nwid32 >> 8) & 0xff)) && (my6[4] == (uint8_t)(nwid32 & 0xff))) {
|
||||
unsigned int ptr = 0;
|
||||
while (ptr != 5) {
|
||||
if (pkt6[ptr] != my6[ptr])
|
||||
break;
|
||||
++ptr;
|
||||
}
|
||||
if (ptr == 5) { // prefix match!
|
||||
v6EmbeddedAddress.setTo(pkt6 + ptr,5);
|
||||
break;
|
||||
if (ptr == 11) { // /88 matches an assigned address on this network
|
||||
const Address atPeer(pkt6 + ptr,5);
|
||||
if (atPeer != RR->identity.address()) {
|
||||
const MAC atPeerMac(atPeer,network->id());
|
||||
TRACE("ZT-RFC4193 NDP emulation: %.16llx: forging response for %s/%s",network->id(),atPeer.toString().c_str(),atPeerMac.toString().c_str());
|
||||
|
||||
uint8_t adv[72];
|
||||
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
|
||||
adv[4] = 0x00; adv[5] = 0x20;
|
||||
adv[6] = 0x3a; adv[7] = 0xff;
|
||||
for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
|
||||
for(int i=0;i<16;++i) adv[24 + i] = my6[i];
|
||||
adv[40] = 0x88; adv[41] = 0x00;
|
||||
adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
|
||||
adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
|
||||
for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
|
||||
adv[64] = 0x02; adv[65] = 0x01;
|
||||
adv[66] = atPeerMac[0]; adv[67] = atPeerMac[1]; adv[68] = atPeerMac[2]; adv[69] = atPeerMac[3]; adv[70] = atPeerMac[4]; adv[71] = atPeerMac[5];
|
||||
|
||||
uint16_t pseudo_[36];
|
||||
uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
|
||||
for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
|
||||
pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
|
||||
pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
|
||||
for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
|
||||
uint32_t checksum = 0;
|
||||
for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
|
||||
while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
|
||||
checksum = ~checksum;
|
||||
adv[42] = (checksum >> 8) & 0xff;
|
||||
adv[43] = checksum & 0xff;
|
||||
|
||||
RR->node->putFrame(network->id(),network->userPtr(),atPeerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
|
||||
return; // stop processing: we have handled this frame with a spoofed local reply so no need to send it anywhere
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((v6EmbeddedAddress)&&(v6EmbeddedAddress != RR->identity.address())) {
|
||||
const MAC peerMac(v6EmbeddedAddress,network->id());
|
||||
TRACE("IPv6 NDP emulation: %.16llx: forging response for %s/%s",network->id(),v6EmbeddedAddress.toString().c_str(),peerMac.toString().c_str());
|
||||
|
||||
uint8_t adv[72];
|
||||
adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
|
||||
adv[4] = 0x00; adv[5] = 0x20;
|
||||
adv[6] = 0x3a; adv[7] = 0xff;
|
||||
for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
|
||||
for(int i=0;i<16;++i) adv[24 + i] = my6[i];
|
||||
adv[40] = 0x88; adv[41] = 0x00;
|
||||
adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
|
||||
adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
|
||||
for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
|
||||
adv[64] = 0x02; adv[65] = 0x01;
|
||||
adv[66] = peerMac[0]; adv[67] = peerMac[1]; adv[68] = peerMac[2]; adv[69] = peerMac[3]; adv[70] = peerMac[4]; adv[71] = peerMac[5];
|
||||
|
||||
uint16_t pseudo_[36];
|
||||
uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
|
||||
for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
|
||||
pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
|
||||
pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
|
||||
for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
|
||||
uint32_t checksum = 0;
|
||||
for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
|
||||
while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
|
||||
checksum = ~checksum;
|
||||
adv[42] = (checksum >> 8) & 0xff;
|
||||
adv[43] = checksum & 0xff;
|
||||
|
||||
RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
|
||||
return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
|
||||
} // else no NDP emulation
|
||||
} // else no NDP emulation
|
||||
}
|
||||
|
||||
// Check this after NDP emulation, since that has to be allowed in exactly this case
|
||||
if (network->config().multicastLimit == 0) {
|
||||
TRACE("%.16llx: dropped multicast: not allowed on network",network->id());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Learn multicast groups for bridged-in hosts.
|
||||
@@ -463,70 +420,62 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||
* multicast addresses on bridge interfaces and subscribing each slave.
|
||||
* But in that case this does no harm, as the sets are just merged. */
|
||||
if (fromBridged)
|
||||
network->learnBridgedMulticastGroup(multicastGroup,RR->node->now());
|
||||
network->learnBridgedMulticastGroup(mg,RR->node->now());
|
||||
|
||||
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len);
|
||||
|
||||
// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),len);
|
||||
|
||||
RR->mc->send(
|
||||
((!network->config().isPublic())&&(network->config().com)) ? &(network->config().com) : (const CertificateOfMembership *)0,
|
||||
network->config().multicastLimit,
|
||||
RR->node->now(),
|
||||
network->id(),
|
||||
network->config().disableCompression(),
|
||||
network->config().activeBridges(),
|
||||
multicastGroup,
|
||||
mg,
|
||||
(fromBridged) ? from : MAC(),
|
||||
etherType,
|
||||
data,
|
||||
len);
|
||||
} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (to[0] == MAC::firstOctetForNetwork(network->id())) {
|
||||
// Destination is another ZeroTier peer on the same network
|
||||
|
||||
Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
|
||||
SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
|
||||
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (fromBridged) {
|
||||
const bool includeCom = ( (network->config().isPrivate()) && (network->config().com) && ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) );
|
||||
if ((fromBridged)||(includeCom)) {
|
||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((unsigned char)0x00);
|
||||
if (includeCom) {
|
||||
outp.append((unsigned char)0x01); // 0x01 -- COM included
|
||||
network->config().com.serialize(outp);
|
||||
} else {
|
||||
outp.append((unsigned char)0x00);
|
||||
}
|
||||
to.appendTo(outp);
|
||||
from.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
outp.compress();
|
||||
send(outp,true,network->id());
|
||||
} else {
|
||||
Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
outp.compress();
|
||||
send(outp,true,network->id());
|
||||
}
|
||||
|
||||
//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
|
||||
} else {
|
||||
// Destination is bridged behind a remote peer
|
||||
|
||||
// We filter with a NULL destination ZeroTier address first. Filtrations
|
||||
// for each ZT destination are also done below. This is the same rationale
|
||||
// and design as for multicast.
|
||||
if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
// Destination is bridged behind a remote peer
|
||||
|
||||
Address bridges[ZT_MAX_BRIDGE_SPAM];
|
||||
unsigned int numBridges = 0;
|
||||
@@ -561,34 +510,37 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
|
||||
}
|
||||
|
||||
for(unsigned int b=0;b<numBridges;++b) {
|
||||
if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
|
||||
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(network->id());
|
||||
outp.append((uint8_t)0x00);
|
||||
to.appendTo(outp);
|
||||
from.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
if (!network->config().disableCompression())
|
||||
outp.compress();
|
||||
send(outp,true);
|
||||
SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
|
||||
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
|
||||
outp.append(network->id());
|
||||
if ( (network->config().isPrivate()) && (network->config().com) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
|
||||
outp.append((unsigned char)0x01); // 0x01 -- COM included
|
||||
network->config().com.serialize(outp);
|
||||
} else {
|
||||
TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
|
||||
outp.append((unsigned char)0);
|
||||
}
|
||||
to.appendTo(outp);
|
||||
from.appendTo(outp);
|
||||
outp.append((uint16_t)etherType);
|
||||
outp.append(data,len);
|
||||
outp.compress();
|
||||
send(outp,true,network->id());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::send(const Packet &packet,bool encrypt)
|
||||
void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
|
||||
{
|
||||
if (packet.destination() == RR->identity.address()) {
|
||||
TRACE("BUG: caught attempt to send() to self, ignored");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_trySend(packet,encrypt)) {
|
||||
//TRACE(">> %s to %s (%u bytes, encrypt==%d, nwid==%.16llx)",Packet::verbString(packet.verb()),packet.destination().toString().c_str(),packet.size(),(int)encrypt,nwid);
|
||||
|
||||
if (!_trySend(packet,encrypt,nwid)) {
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt));
|
||||
_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt,nwid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,7 +590,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
|
||||
outp.append(cg.first.rawIpData(),4);
|
||||
}
|
||||
outp.armor(p1p->key(),true);
|
||||
p1p->sendDirect(outp.data(),outp.size(),now,true);
|
||||
p1p->send(outp.data(),outp.size(),now);
|
||||
} else {
|
||||
// Tell p2 where to find p1.
|
||||
Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
|
||||
@@ -653,7 +605,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
|
||||
outp.append(cg.second.rawIpData(),4);
|
||||
}
|
||||
outp.armor(p2p->key(),true);
|
||||
p2p->sendDirect(outp.data(),outp.size(),now,true);
|
||||
p2p->send(outp.data(),outp.size(),now);
|
||||
}
|
||||
++alt; // counts up and also flips LSB
|
||||
}
|
||||
@@ -661,6 +613,17 @@ bool Switch::unite(const Address &p1,const Address &p2)
|
||||
return true;
|
||||
}
|
||||
|
||||
void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr)
|
||||
{
|
||||
TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
|
||||
const uint64_t now = RR->node->now();
|
||||
peer->sendHELLO(localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
|
||||
{
|
||||
Mutex::Lock _l(_contactQueue_m);
|
||||
_contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr));
|
||||
}
|
||||
}
|
||||
|
||||
void Switch::requestWhois(const Address &addr)
|
||||
{
|
||||
bool inserted = false;
|
||||
@@ -691,7 +654,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
|
||||
while (i) {
|
||||
RXQueueEntry *rq = &(_rxQueue[--i]);
|
||||
if ((rq->timestamp)&&(rq->complete)) {
|
||||
if (rq->frag0.tryDecode(RR))
|
||||
if (rq->frag0.tryDecode(RR,false))
|
||||
rq->timestamp = 0;
|
||||
}
|
||||
}
|
||||
@@ -701,7 +664,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||
if (txi->dest == peer->address()) {
|
||||
if (_trySend(txi->packet,txi->encrypt))
|
||||
if (_trySend(txi->packet,txi->encrypt,txi->nwid))
|
||||
_txQueue.erase(txi++);
|
||||
else ++txi;
|
||||
} else ++txi;
|
||||
@@ -713,6 +676,42 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
||||
{
|
||||
unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
|
||||
|
||||
{ // Iterate through NAT traversal strategies for entries in contact queue
|
||||
Mutex::Lock _l(_contactQueue_m);
|
||||
for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
|
||||
if (now >= qi->fireAtTime) {
|
||||
if (!qi->peer->pushDirectPaths(qi->localAddr,qi->inaddr,now,true,false))
|
||||
qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
|
||||
_contactQueue.erase(qi++);
|
||||
continue;
|
||||
/* Old symmetric NAT buster code, obsoleted by port prediction alg in SelfAwareness but left around for now in case we revert
|
||||
if (qi->strategyIteration == 0) {
|
||||
// First strategy: send packet directly to destination
|
||||
qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
|
||||
} else if (qi->strategyIteration <= 3) {
|
||||
// Strategies 1-3: try escalating ports for symmetric NATs that remap sequentially
|
||||
InetAddress tmpaddr(qi->inaddr);
|
||||
int p = (int)qi->inaddr.port() + qi->strategyIteration;
|
||||
if (p > 65535)
|
||||
p -= 64511;
|
||||
tmpaddr.setPort((unsigned int)p);
|
||||
qi->peer->sendHELLO(qi->localAddr,tmpaddr,now);
|
||||
} else {
|
||||
// All strategies tried, expire entry
|
||||
_contactQueue.erase(qi++);
|
||||
continue;
|
||||
}
|
||||
++qi->strategyIteration;
|
||||
qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
|
||||
nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
|
||||
*/
|
||||
} else {
|
||||
nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
|
||||
}
|
||||
++qi; // if qi was erased, loop will have continued before here
|
||||
}
|
||||
}
|
||||
|
||||
{ // Retry outstanding WHOIS requests
|
||||
Mutex::Lock _l(_outstandingWhoisRequests_m);
|
||||
Hashtable< Address,WhoisRequest >::Iterator i(_outstandingWhoisRequests);
|
||||
@@ -740,7 +739,7 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
||||
{ // Time out TX queue packets that never got WHOIS lookups or other info.
|
||||
Mutex::Lock _l(_txQueue_m);
|
||||
for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
|
||||
if (_trySend(txi->packet,txi->encrypt))
|
||||
if (_trySend(txi->packet,txi->encrypt,txi->nwid))
|
||||
_txQueue.erase(txi++);
|
||||
else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
|
||||
TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
|
||||
@@ -765,41 +764,65 @@ unsigned long Switch::doTimerTasks(uint64_t now)
|
||||
|
||||
Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
|
||||
{
|
||||
SharedPtr<Peer> upstream(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
||||
if (upstream) {
|
||||
Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
||||
SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
|
||||
if (root) {
|
||||
Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
|
||||
addr.appendTo(outp);
|
||||
RR->node->expectReplyTo(outp.packetId());
|
||||
send(outp,true);
|
||||
outp.armor(root->key(),true);
|
||||
if (root->send(outp.data(),outp.size(),RR->node->now()))
|
||||
return root->address();
|
||||
}
|
||||
return Address();
|
||||
}
|
||||
|
||||
bool Switch::_trySend(const Packet &packet,bool encrypt)
|
||||
bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
|
||||
{
|
||||
const SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
|
||||
SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
|
||||
|
||||
if (peer) {
|
||||
const uint64_t now = RR->node->now();
|
||||
|
||||
// First get the best path, and if it's dead (and this is not a root)
|
||||
// we attempt to re-activate that path but this packet will flow
|
||||
// upstream. If the path comes back alive, it will be used in the future.
|
||||
// For roots we don't do the alive check since roots are not required
|
||||
// to send heartbeats "down" and because we have to at least try to
|
||||
// go somewhere.
|
||||
|
||||
SharedPtr<Path> viaPath(peer->getBestPath(now,false));
|
||||
if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
|
||||
if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL))
|
||||
peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now);
|
||||
viaPath.zero();
|
||||
SharedPtr<Network> network;
|
||||
if (nwid) {
|
||||
network = RR->node->network(nwid);
|
||||
if ((!network)||(!network->hasConfig()))
|
||||
return false; // we probably just left this network, let its packets die
|
||||
}
|
||||
|
||||
Path *viaPath = peer->getBestPath(now);
|
||||
SharedPtr<Peer> relay;
|
||||
|
||||
if (!viaPath) {
|
||||
SharedPtr<Peer> relay(RR->topology->getBestRoot());
|
||||
if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
|
||||
if (!(viaPath = peer->getBestPath(now,true)))
|
||||
return false;
|
||||
if (network) {
|
||||
unsigned int bestq = ~((unsigned int)0); // max unsigned int since quality is lower==better
|
||||
unsigned int ptr = 0;
|
||||
for(;;) {
|
||||
const Address raddr(network->config().nextRelay(ptr));
|
||||
if (raddr) {
|
||||
SharedPtr<Peer> rp(RR->topology->getPeer(raddr));
|
||||
if (rp) {
|
||||
const unsigned int q = rp->relayQuality(now);
|
||||
if (q < bestq) {
|
||||
bestq = q;
|
||||
rp.swap(relay);
|
||||
}
|
||||
}
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!relay)
|
||||
relay = RR->topology->getBestRoot();
|
||||
|
||||
if ( (!relay) || (!(viaPath = relay->getBestPath(now))) )
|
||||
return false;
|
||||
}
|
||||
// viaPath will not be null if we make it here
|
||||
|
||||
// Push possible direct paths to us if we are relaying
|
||||
if (relay) {
|
||||
peer->pushDirectPaths(viaPath->localAddress(),viaPath->address(),now,false,( (network)&&(network->isAllowed(peer)) ));
|
||||
viaPath->sent(now);
|
||||
}
|
||||
|
||||
Packet tmp(packet);
|
||||
@@ -807,12 +830,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
|
||||
unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
|
||||
tmp.setFragmented(chunkSize < tmp.size());
|
||||
|
||||
const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
|
||||
if (trustedPathId) {
|
||||
tmp.setTrusted(trustedPathId);
|
||||
} else {
|
||||
tmp.armor(peer->key(),encrypt);
|
||||
}
|
||||
tmp.armor(peer->key(),encrypt);
|
||||
|
||||
if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
|
||||
if (chunkSize < tmp.size()) {
|
||||
@@ -822,7 +840,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
|
||||
unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||
if ((fragsRemaining * (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)) < remaining)
|
||||
++fragsRemaining;
|
||||
const unsigned int totalFragments = fragsRemaining + 1;
|
||||
unsigned int totalFragments = fragsRemaining + 1;
|
||||
|
||||
for(unsigned int fno=1;fno<totalFragments;++fno) {
|
||||
chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
|
||||
|
||||
@@ -92,10 +92,15 @@ public:
|
||||
* Needless to say, the packet's source must be this node. Otherwise it
|
||||
* won't be encrypted right. (This is not used for relaying.)
|
||||
*
|
||||
* The network ID should only be specified for frames and other actual
|
||||
* network traffic. Other traffic such as controller requests and regular
|
||||
* protocol messages should specify zero.
|
||||
*
|
||||
* @param packet Packet to send
|
||||
* @param encrypt Encrypt packet payload? (always true except for HELLO)
|
||||
* @param nwid Related network ID or 0 if message is not in-network traffic
|
||||
*/
|
||||
void send(const Packet &packet,bool encrypt);
|
||||
void send(const Packet &packet,bool encrypt,uint64_t nwid);
|
||||
|
||||
/**
|
||||
* Send RENDEZVOUS to two peers to permit them to directly connect
|
||||
@@ -108,6 +113,15 @@ public:
|
||||
*/
|
||||
bool unite(const Address &p1,const Address &p2);
|
||||
|
||||
/**
|
||||
* Attempt NAT traversal to peer at a given physical address
|
||||
*
|
||||
* @param peer Peer to contact
|
||||
* @param localAddr Local interface address
|
||||
* @param atAddr Address of peer
|
||||
*/
|
||||
void rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr,const InetAddress &atAddr);
|
||||
|
||||
/**
|
||||
* Request WHOIS on a given address
|
||||
*
|
||||
@@ -137,7 +151,7 @@ public:
|
||||
|
||||
private:
|
||||
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
|
||||
bool _trySend(const Packet &packet,bool encrypt);
|
||||
bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
uint64_t _lastBeaconResponse;
|
||||
@@ -191,14 +205,16 @@ private:
|
||||
struct TXQueueEntry
|
||||
{
|
||||
TXQueueEntry() {}
|
||||
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc) :
|
||||
TXQueueEntry(Address d,uint64_t ct,const Packet &p,bool enc,uint64_t nw) :
|
||||
dest(d),
|
||||
creationTime(ct),
|
||||
nwid(nw),
|
||||
packet(p),
|
||||
encrypt(enc) {}
|
||||
|
||||
Address dest;
|
||||
uint64_t creationTime;
|
||||
uint64_t nwid;
|
||||
Packet packet; // unencrypted/unMAC'd packet -- this is done at send time
|
||||
bool encrypt;
|
||||
};
|
||||
@@ -225,6 +241,26 @@ private:
|
||||
};
|
||||
Hashtable< _LastUniteKey,uint64_t > _lastUniteAttempt; // key is always sorted in ascending order, for set-like behavior
|
||||
Mutex _lastUniteAttempt_m;
|
||||
|
||||
// Active attempts to contact remote peers, including state of multi-phase NAT traversal
|
||||
struct ContactQueueEntry
|
||||
{
|
||||
ContactQueueEntry() {}
|
||||
ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &laddr,const InetAddress &a) :
|
||||
peer(p),
|
||||
fireAtTime(ft),
|
||||
inaddr(a),
|
||||
localAddr(laddr),
|
||||
strategyIteration(0) {}
|
||||
|
||||
SharedPtr<Peer> peer;
|
||||
uint64_t fireAtTime;
|
||||
InetAddress inaddr;
|
||||
InetAddress localAddr;
|
||||
unsigned int strategyIteration;
|
||||
};
|
||||
std::list<ContactQueueEntry> _contactQueue;
|
||||
Mutex _contactQueue_m;
|
||||
};
|
||||
|
||||
} // namespace ZeroTier
|
||||
|
||||
@@ -44,10 +44,38 @@ static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x0
|
||||
|
||||
Topology::Topology(const RuntimeEnvironment *renv) :
|
||||
RR(renv),
|
||||
_trustedPathCount(0),
|
||||
_amRoot(false)
|
||||
{
|
||||
// Get cached world if present
|
||||
std::string alls(RR->node->dataStoreGet("peers.save"));
|
||||
const uint8_t *all = reinterpret_cast<const uint8_t *>(alls.data());
|
||||
RR->node->dataStoreDelete("peers.save");
|
||||
|
||||
Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *deserializeBuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
|
||||
unsigned int ptr = 0;
|
||||
while ((ptr + 4) < alls.size()) {
|
||||
try {
|
||||
const unsigned int reclen = ( // each Peer serialized record is prefixed by a record length
|
||||
((((unsigned int)all[ptr]) & 0xff) << 24) |
|
||||
((((unsigned int)all[ptr + 1]) & 0xff) << 16) |
|
||||
((((unsigned int)all[ptr + 2]) & 0xff) << 8) |
|
||||
(((unsigned int)all[ptr + 3]) & 0xff)
|
||||
);
|
||||
unsigned int pos = 0;
|
||||
deserializeBuf->copyFrom(all + ptr,reclen + 4);
|
||||
SharedPtr<Peer> p(Peer::deserializeNew(RR,RR->identity,*deserializeBuf,pos));
|
||||
ptr += pos;
|
||||
if (!p)
|
||||
break; // stop if invalid records
|
||||
if (p->address() != RR->identity.address())
|
||||
_peers.set(p->address(),p);
|
||||
} catch ( ... ) {
|
||||
break; // stop if invalid records
|
||||
}
|
||||
}
|
||||
delete deserializeBuf;
|
||||
|
||||
clean(RR->node->now());
|
||||
|
||||
std::string dsWorld(RR->node->dataStoreGet("world"));
|
||||
World cachedWorld;
|
||||
if (dsWorld.length() > 0) {
|
||||
@@ -58,8 +86,6 @@ Topology::Topology(const RuntimeEnvironment *renv) :
|
||||
cachedWorld = World(); // clear if cached world is invalid
|
||||
}
|
||||
}
|
||||
|
||||
// Use default or cached world depending on which is shinier
|
||||
World defaultWorld;
|
||||
{
|
||||
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
|
||||
@@ -74,6 +100,34 @@ Topology::Topology(const RuntimeEnvironment *renv) :
|
||||
|
||||
Topology::~Topology()
|
||||
{
|
||||
Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> *pbuf = 0;
|
||||
try {
|
||||
pbuf = new Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>();
|
||||
std::string all;
|
||||
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
while (i.next(a,p)) {
|
||||
if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) {
|
||||
pbuf->clear();
|
||||
try {
|
||||
(*p)->serialize(*pbuf);
|
||||
try {
|
||||
all.append((const char *)pbuf->data(),pbuf->size());
|
||||
} catch ( ... ) {
|
||||
return; // out of memory? just skip
|
||||
}
|
||||
} catch ( ... ) {} // peer too big? shouldn't happen, but it so skip
|
||||
}
|
||||
}
|
||||
|
||||
RR->node->dataStorePut("peers.save",all,true);
|
||||
|
||||
delete pbuf;
|
||||
} catch ( ... ) {
|
||||
delete pbuf;
|
||||
}
|
||||
}
|
||||
|
||||
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
|
||||
@@ -96,6 +150,7 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
|
||||
np = hp;
|
||||
}
|
||||
|
||||
np->use(RR->node->now());
|
||||
saveIdentity(np->identity());
|
||||
|
||||
return np;
|
||||
@@ -112,6 +167,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
||||
Mutex::Lock _l(_lock);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap) {
|
||||
(*ap)->use(RR->node->now());
|
||||
return *ap;
|
||||
}
|
||||
}
|
||||
@@ -125,6 +181,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
||||
SharedPtr<Peer> &ap = _peers[zta];
|
||||
if (!ap)
|
||||
ap.swap(np);
|
||||
ap->use(RR->node->now());
|
||||
return ap;
|
||||
}
|
||||
}
|
||||
@@ -138,9 +195,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
|
||||
|
||||
Identity Topology::getIdentity(const Address &zta)
|
||||
{
|
||||
if (zta == RR->identity.address()) {
|
||||
return RR->identity;
|
||||
} else {
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
const SharedPtr<Peer> *const ap = _peers.get(zta);
|
||||
if (ap)
|
||||
@@ -173,8 +228,10 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
|
||||
if (_rootAddresses[p] == RR->identity.address()) {
|
||||
for(unsigned long q=1;q<_rootAddresses.size();++q) {
|
||||
const SharedPtr<Peer> *const nextsn = _peers.get(_rootAddresses[(p + q) % _rootAddresses.size()]);
|
||||
if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now)))
|
||||
if ((nextsn)&&((*nextsn)->hasActiveDirectPath(now))) {
|
||||
(*nextsn)->use(now);
|
||||
return *nextsn;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -209,8 +266,10 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
|
||||
}
|
||||
|
||||
if (bestNotAvoid) {
|
||||
(*bestNotAvoid)->use(now);
|
||||
return *bestNotAvoid;
|
||||
} else if ((!strictAvoid)&&(bestOverall)) {
|
||||
(*bestOverall)->use(now);
|
||||
return *bestOverall;
|
||||
}
|
||||
|
||||
@@ -221,7 +280,15 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
|
||||
|
||||
bool Topology::isUpstream(const Identity &id) const
|
||||
{
|
||||
return isRoot(id);
|
||||
if (isRoot(id))
|
||||
return true;
|
||||
std::vector< SharedPtr<Network> > nws(RR->node->allNetworks());
|
||||
for(std::vector< SharedPtr<Network> >::const_iterator nw(nws.begin());nw!=nws.end();++nw) {
|
||||
if ((*nw)->config().isRelay(id.address())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Topology::worldUpdateIfValid(const World &newWorld)
|
||||
@@ -244,22 +311,14 @@ bool Topology::worldUpdateIfValid(const World &newWorld)
|
||||
void Topology::clean(uint64_t now)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
{
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
if ( (!(*p)->isAlive(now)) && (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) )
|
||||
_peers.erase(*a);
|
||||
}
|
||||
}
|
||||
{
|
||||
Hashtable< Path::HashKey,SharedPtr<Path> >::Iterator i(_paths);
|
||||
Path::HashKey *k = (Path::HashKey *)0;
|
||||
SharedPtr<Path> *p = (SharedPtr<Path> *)0;
|
||||
while (i.next(k,p)) {
|
||||
if (p->reclaimIfWeak())
|
||||
_paths.erase(*k);
|
||||
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
|
||||
Address *a = (Address *)0;
|
||||
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
|
||||
while (i.next(a,p)) {
|
||||
if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
|
||||
_peers.erase(*a);
|
||||
} else {
|
||||
(*p)->clean(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,12 +28,10 @@
|
||||
#include <utility>
|
||||
|
||||
#include "Constants.hpp"
|
||||
#include "../include/ZeroTierOne.h"
|
||||
|
||||
#include "Address.hpp"
|
||||
#include "Identity.hpp"
|
||||
#include "Peer.hpp"
|
||||
#include "Path.hpp"
|
||||
#include "Mutex.hpp"
|
||||
#include "InetAddress.hpp"
|
||||
#include "Hashtable.hpp"
|
||||
@@ -90,22 +88,6 @@ public:
|
||||
return SharedPtr<Peer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Path object for a given local and remote physical address, creating if needed
|
||||
*
|
||||
* @param l Local address or NULL for 'any' or 'wildcard'
|
||||
* @param r Remote address
|
||||
* @return Pointer to canonicalized Path object
|
||||
*/
|
||||
inline SharedPtr<Path> getPath(const InetAddress &l,const InetAddress &r)
|
||||
{
|
||||
Mutex::Lock _l(_lock);
|
||||
SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
|
||||
if (!p)
|
||||
p.setToUnsafe(new Path(l,r));
|
||||
return p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the identity of a peer
|
||||
*
|
||||
@@ -170,14 +152,6 @@ public:
|
||||
return _rootAddresses;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector of active upstream addresses (including roots)
|
||||
*/
|
||||
inline std::vector<Address> upstreamAddresses() const
|
||||
{
|
||||
return rootAddresses();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current World (copy)
|
||||
*/
|
||||
@@ -278,70 +252,14 @@ public:
|
||||
*/
|
||||
inline bool amRoot() const throw() { return _amRoot; }
|
||||
|
||||
/**
|
||||
* Get the outbound trusted path ID for a physical address, or 0 if none
|
||||
*
|
||||
* @param physicalAddress Physical address to which we are sending the packet
|
||||
* @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
|
||||
*/
|
||||
inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
|
||||
{
|
||||
for(unsigned int i=0;i<_trustedPathCount;++i) {
|
||||
if (_trustedPathNetworks[i].containsAddress(physicalAddress))
|
||||
return _trustedPathIds[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether in incoming trusted path marked packet is valid
|
||||
*
|
||||
* @param physicalAddress Originating physical address
|
||||
* @param trustedPathId Trusted path ID from packet (from MAC field)
|
||||
*/
|
||||
inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
|
||||
{
|
||||
for(unsigned int i=0;i<_trustedPathCount;++i) {
|
||||
if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set trusted paths in this topology
|
||||
*
|
||||
* @param networks Array of networks (prefix/netmask bits)
|
||||
* @param ids Array of trusted path IDs
|
||||
* @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
|
||||
*/
|
||||
inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
|
||||
{
|
||||
if (count > ZT_MAX_TRUSTED_PATHS)
|
||||
count = ZT_MAX_TRUSTED_PATHS;
|
||||
Mutex::Lock _l(_lock);
|
||||
for(unsigned int i=0;i<count;++i) {
|
||||
_trustedPathIds[i] = ids[i];
|
||||
_trustedPathNetworks[i] = networks[i];
|
||||
}
|
||||
_trustedPathCount = count;
|
||||
}
|
||||
|
||||
private:
|
||||
Identity _getIdentity(const Address &zta);
|
||||
void _setWorld(const World &newWorld);
|
||||
|
||||
const RuntimeEnvironment *const RR;
|
||||
|
||||
uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
|
||||
InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
|
||||
unsigned int _trustedPathCount;
|
||||
|
||||
World _world;
|
||||
|
||||
Hashtable< Address,SharedPtr<Peer> > _peers;
|
||||
Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
|
||||
|
||||
std::vector< Address > _rootAddresses;
|
||||
std::vector< SharedPtr<Peer> > _rootPeers;
|
||||
bool _amRoot;
|
||||
|
||||
@@ -262,24 +262,6 @@ std::vector<std::string> Utils::split(const char *s,const char *const sep,const
|
||||
return fields;
|
||||
}
|
||||
|
||||
bool Utils::scopy(char *dest,unsigned int len,const char *src)
|
||||
{
|
||||
if (!len)
|
||||
return false; // sanity check
|
||||
if (!src) {
|
||||
*dest = (char)0;
|
||||
return true;
|
||||
}
|
||||
char *end = dest + len;
|
||||
while ((*dest++ = *src++)) {
|
||||
if (dest == end) {
|
||||
*(--dest) = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
|
||||
throw(std::length_error)
|
||||
{
|
||||
@@ -292,7 +274,6 @@ unsigned int Utils::snprintf(char *buf,unsigned int len,const char *fmt,...)
|
||||
if ((n >= (int)len)||(n < 0)) {
|
||||
if (len)
|
||||
buf[len - 1] = (char)0;
|
||||
abort();
|
||||
throw std::length_error("buf[] overflow in Utils::snprintf");
|
||||
}
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
* @return True if strings are equal
|
||||
*/
|
||||
static inline bool secureEq(const void *a,const void *b,unsigned int len)
|
||||
throw()
|
||||
{
|
||||
uint8_t diff = 0;
|
||||
for(unsigned int i=0;i<len;++i)
|
||||
@@ -224,17 +225,27 @@ public:
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a safe C string copy, ALWAYS null-terminating the result
|
||||
* Perform a safe C string copy
|
||||
*
|
||||
* This will never ever EVER result in dest[] not being null-terminated
|
||||
* regardless of any input parameter (other than len==0 which is invalid).
|
||||
*
|
||||
* @param dest Destination buffer (must not be NULL)
|
||||
* @param len Length of dest[] (if zero, false is returned and nothing happens)
|
||||
* @param src Source string (if NULL, dest will receive a zero-length string and true is returned)
|
||||
* @param dest Destination buffer
|
||||
* @param len Length of buffer
|
||||
* @param src Source string
|
||||
* @return True on success, false on overflow (buffer will still be 0-terminated)
|
||||
*/
|
||||
static bool scopy(char *dest,unsigned int len,const char *src);
|
||||
static inline bool scopy(char *dest,unsigned int len,const char *src)
|
||||
throw()
|
||||
{
|
||||
if (!len)
|
||||
return false; // sanity check
|
||||
char *end = dest + len;
|
||||
while ((*dest++ = *src++)) {
|
||||
if (dest == end) {
|
||||
*(--dest) = (char)0;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variant of snprintf that is portable and throws an exception
|
||||
@@ -258,6 +269,7 @@ public:
|
||||
* @return Number of bits set in this integer (0-32)
|
||||
*/
|
||||
static inline uint32_t countBits(uint32_t v)
|
||||
throw()
|
||||
{
|
||||
v = v - ((v >> 1) & (uint32_t)0x55555555);
|
||||
v = (v & (uint32_t)0x33333333) + ((v >> 2) & (uint32_t)0x33333333);
|
||||
@@ -272,6 +284,7 @@ public:
|
||||
* @return True if memory is all zero
|
||||
*/
|
||||
static inline bool isZero(const void *p,unsigned int len)
|
||||
throw()
|
||||
{
|
||||
for(unsigned int i=0;i<len;++i) {
|
||||
if (((const unsigned char *)p)[i])
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
if (fullSignatureCheck) {
|
||||
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
|
||||
update.serialize(tmp,true);
|
||||
return C25519::verify(_updatesMustBeSignedBy,tmp.data(),tmp.size(),update._signature);
|
||||
return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
|
||||
} else return true;
|
||||
}
|
||||
return false;
|
||||
@@ -164,12 +164,12 @@ public:
|
||||
template<unsigned int C>
|
||||
inline void serialize(Buffer<C> &b,bool forSign = false) const
|
||||
{
|
||||
if (forSign) b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
|
||||
b.append((uint8_t)0x01);
|
||||
if (forSign)
|
||||
b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
|
||||
b.append((uint8_t)0x01); // version -- only one valid value for now
|
||||
b.append((uint64_t)_id);
|
||||
b.append((uint64_t)_ts);
|
||||
b.append(_updatesMustBeSignedBy.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||
b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
|
||||
if (!forSign)
|
||||
b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
|
||||
b.append((uint8_t)_roots.size());
|
||||
@@ -179,8 +179,8 @@ public:
|
||||
for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
|
||||
ep->serialize(b);
|
||||
}
|
||||
|
||||
if (forSign) b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
|
||||
if (forSign)
|
||||
b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
|
||||
}
|
||||
|
||||
template<unsigned int C>
|
||||
@@ -191,11 +191,11 @@ public:
|
||||
_roots.clear();
|
||||
|
||||
if (b[p++] != 0x01)
|
||||
throw std::invalid_argument("invalid object type");
|
||||
throw std::invalid_argument("invalid World serialized version");
|
||||
|
||||
_id = b.template at<uint64_t>(p); p += 8;
|
||||
_ts = b.template at<uint64_t>(p); p += 8;
|
||||
memcpy(_updatesMustBeSignedBy.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||
memcpy(_updateSigningKey.data,b.field(p,ZT_C25519_PUBLIC_KEY_LEN),ZT_C25519_PUBLIC_KEY_LEN); p += ZT_C25519_PUBLIC_KEY_LEN;
|
||||
memcpy(_signature.data,b.field(p,ZT_C25519_SIGNATURE_LEN),ZT_C25519_SIGNATURE_LEN); p += ZT_C25519_SIGNATURE_LEN;
|
||||
unsigned int numRoots = b[p++];
|
||||
if (numRoots > ZT_WORLD_MAX_ROOTS)
|
||||
@@ -216,13 +216,13 @@ public:
|
||||
return (p - startAt);
|
||||
}
|
||||
|
||||
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updatesMustBeSignedBy == w._updatesMustBeSignedBy)&&(_signature == w._signature)&&(_roots == w._roots)); }
|
||||
inline bool operator==(const World &w) const throw() { return ((_id == w._id)&&(_ts == w._ts)&&(_updateSigningKey == w._updateSigningKey)&&(_signature == w._signature)&&(_roots == w._roots)); }
|
||||
inline bool operator!=(const World &w) const throw() { return (!(*this == w)); }
|
||||
|
||||
protected:
|
||||
uint64_t _id;
|
||||
uint64_t _ts;
|
||||
C25519::Public _updatesMustBeSignedBy;
|
||||
C25519::Public _updateSigningKey;
|
||||
C25519::Signature _signature;
|
||||
std::vector<Root> _roots;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user