MERGE current "dev" into "netcon" -- should not affect netcon itself but will retest -- brings ZeroTier core up to 1.1.0

This commit is contained in:
Adam Ierymenko
2015-11-23 10:46:52 -08:00
324 changed files with 23680 additions and 15524 deletions

View File

@@ -35,30 +35,35 @@
namespace ZeroTier {
#define ZT_ANTIRECURSION_TAIL_LEN 256
/**
* Size of anti-recursion history
*/
#define ZT_ANTIRECURSION_HISTORY_SIZE 16
/**
* Filter to prevent recursion (ZeroTier-over-ZeroTier)
*
* This works by logging ZeroTier packets that we send. It's then invoked
* again against packets read from local Ethernet taps. If the last N
* again against packets read from local Ethernet taps. If the last 32
* bytes representing the ZeroTier packet match in the tap frame, then
* the frame is a re-injection of a frame that we sent and is rejected.
*
* This means that ZeroTier packets simply will not traverse ZeroTier
* networks, which would cause all sorts of weird problems.
*
* NOTE: this is applied to low-level packets before they are sent to
* SocketManager and/or sockets, not to fully assembled packets before
* (possible) fragmentation.
* This is highly optimized code since it's checked for every packet.
*/
class AntiRecursion
{
public:
AntiRecursion()
throw()
{
memset(_history,0,sizeof(_history));
for(int i=0;i<ZT_ANTIRECURSION_HISTORY_SIZE;++i) {
_history[i].tail[0] = 0;
_history[i].tail[1] = 0;
_history[i].tail[2] = 0;
_history[i].tail[3] = 0;
}
_ptr = 0;
}
@@ -68,13 +73,20 @@ public:
* @param data ZT packet data
* @param len Length of packet
*/
inline void logOutgoingZT(const void *data,unsigned int len)
throw()
inline void logOutgoingZT(const void *const data,const unsigned int len)
{
ArItem *i = &(_history[_ptr++ % ZT_ANTIRECURSION_HISTORY_SIZE]);
const unsigned int tl = (len > ZT_ANTIRECURSION_TAIL_LEN) ? ZT_ANTIRECURSION_TAIL_LEN : len;
memcpy(i->tail,((const unsigned char *)data) + (len - tl),tl);
i->len = tl;
if (len < 32)
return;
#ifdef ZT_NO_TYPE_PUNNING
memcpy(_history[++_ptr % ZT_ANTIRECURSION_HISTORY_SIZE].tail,reinterpret_cast<const uint8_t *>(data) + (len - 32),32);
#else
uint64_t *t = _history[++_ptr % ZT_ANTIRECURSION_HISTORY_SIZE].tail;
const uint64_t *p = reinterpret_cast<const uint64_t *>(reinterpret_cast<const uint8_t *>(data) + (len - 32));
*(t++) = *(p++);
*(t++) = *(p++);
*(t++) = *(p++);
*t = *p;
#endif
}
/**
@@ -84,25 +96,36 @@ public:
* @param len Length of frame
* @return True if frame is OK to be passed, false if it's a ZT frame that we sent
*/
inline bool checkEthernetFrame(const void *data,unsigned int len)
throw()
inline bool checkEthernetFrame(const void *const data,const unsigned int len) const
{
for(unsigned int h=0;h<ZT_ANTIRECURSION_HISTORY_SIZE;++h) {
ArItem *i = &(_history[h]);
if ((i->len > 0)&&(len >= i->len)&&(!memcmp(((const unsigned char *)data) + (len - i->len),i->tail,i->len)))
if (len < 32)
return true;
const uint8_t *const pp = reinterpret_cast<const uint8_t *>(data) + (len - 32);
const _ArItem *i = _history;
const _ArItem *const end = i + ZT_ANTIRECURSION_HISTORY_SIZE;
while (i != end) {
#ifdef ZT_NO_TYPE_PUNNING
if (!memcmp(pp,i->tail,32))
return false;
#else
const uint64_t *t = i->tail;
const uint64_t *p = reinterpret_cast<const uint64_t *>(pp);
uint64_t bits = *(t++) ^ *(p++);
bits |= *(t++) ^ *(p++);
bits |= *(t++) ^ *(p++);
bits |= *t ^ *p;
if (!bits)
return false;
#endif
++i;
}
return true;
}
private:
struct ArItem
{
unsigned char tail[ZT_ANTIRECURSION_TAIL_LEN];
unsigned int len;
};
ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE];
volatile unsigned int _ptr;
struct _ArItem { uint64_t tail[4]; };
_ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE];
volatile unsigned long _ptr;
};
} // namespace ZeroTier

106
node/BinarySemaphore.hpp Normal file
View File

@@ -0,0 +1,106 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_BINARYSEMAPHORE_HPP
#define ZT_BINARYSEMAPHORE_HPP
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include "Constants.hpp"
#include "NonCopyable.hpp"
#ifdef __WINDOWS__
#include <Windows.h>
namespace ZeroTier {
class BinarySemaphore : NonCopyable
{
public:
BinarySemaphore() throw() { _sem = CreateSemaphore(NULL,0,1,NULL); }
~BinarySemaphore() { CloseHandle(_sem); }
inline void wait() { WaitForSingleObject(_sem,INFINITE); }
inline void post() { ReleaseSemaphore(_sem,1,NULL); }
private:
HANDLE _sem;
};
} // namespace ZeroTier
#else // !__WINDOWS__
#include <pthread.h>
namespace ZeroTier {
class BinarySemaphore : NonCopyable
{
public:
BinarySemaphore()
{
pthread_mutex_init(&_mh,(const pthread_mutexattr_t *)0);
pthread_cond_init(&_cond,(const pthread_condattr_t *)0);
_f = false;
}
~BinarySemaphore()
{
pthread_cond_destroy(&_cond);
pthread_mutex_destroy(&_mh);
}
inline void wait()
{
pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
while (!_f)
pthread_cond_wait(const_cast <pthread_cond_t *>(&_cond),const_cast <pthread_mutex_t *>(&_mh));
_f = false;
pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
}
inline void post()
{
pthread_mutex_lock(const_cast <pthread_mutex_t *>(&_mh));
_f = true;
pthread_mutex_unlock(const_cast <pthread_mutex_t *>(&_mh));
pthread_cond_signal(const_cast <pthread_cond_t *>(&_cond));
}
private:
pthread_cond_t _cond;
pthread_mutex_t _mh;
volatile bool _f;
};
} // namespace ZeroTier
#endif // !__WINDOWS__
#endif

913
node/Cluster.cpp Normal file
View File

@@ -0,0 +1,913 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifdef ZT_ENABLE_CLUSTER
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <map>
#include <algorithm>
#include <set>
#include <utility>
#include <list>
#include <stdexcept>
#include "../version.h"
#include "Cluster.hpp"
#include "RuntimeEnvironment.hpp"
#include "MulticastGroup.hpp"
#include "CertificateOfMembership.hpp"
#include "Salsa20.hpp"
#include "Poly1305.hpp"
#include "Identity.hpp"
#include "Topology.hpp"
#include "Packet.hpp"
#include "Switch.hpp"
#include "Node.hpp"
#include "Array.hpp"
namespace ZeroTier {
static inline double _dist3d(int x1,int y1,int z1,int x2,int y2,int z2)
throw()
{
double dx = ((double)x2 - (double)x1);
double dy = ((double)y2 - (double)y1);
double dz = ((double)z2 - (double)z1);
return sqrt((dx * dx) + (dy * dy) + (dz * dz));
}
// An entry in _ClusterSendQueue
struct _ClusterSendQueueEntry
{
uint64_t timestamp;
Address fromPeerAddress;
Address toPeerAddress;
// if we ever support larger transport MTUs this must be increased
unsigned char data[ZT_CLUSTER_SEND_QUEUE_DATA_MAX];
unsigned int len;
bool unite;
};
// A multi-index map with entry memory pooling -- this allows our queue to
// be O(log(N)) and is complex enough that it makes the code a lot cleaner
// to break it out from Cluster.
class _ClusterSendQueue
{
public:
_ClusterSendQueue() :
_poolCount(0) {}
~_ClusterSendQueue() {} // memory is automatically freed when _chunks is destroyed
inline void enqueue(uint64_t now,const Address &from,const Address &to,const void *data,unsigned int len,bool unite)
{
if (len > ZT_CLUSTER_SEND_QUEUE_DATA_MAX)
return;
Mutex::Lock _l(_lock);
// Delete oldest queue entry for this sender if this enqueue() would take them over the per-sender limit
{
std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_bySrc.lower_bound(std::pair<Address,_ClusterSendQueueEntry *>(from,(_ClusterSendQueueEntry *)0)));
std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator oldest(qi);
unsigned long countForSender = 0;
while ((qi != _bySrc.end())&&(qi->first == from)) {
if (qi->second->timestamp < oldest->second->timestamp)
oldest = qi;
++countForSender;
++qi;
}
if (countForSender >= ZT_CLUSTER_MAX_QUEUE_PER_SENDER) {
_byDest.erase(std::pair<Address,_ClusterSendQueueEntry *>(oldest->second->toPeerAddress,oldest->second));
_pool[_poolCount++] = oldest->second;
_bySrc.erase(oldest);
}
}
_ClusterSendQueueEntry *e;
if (_poolCount > 0) {
e = _pool[--_poolCount];
} else {
if (_chunks.size() >= ZT_CLUSTER_MAX_QUEUE_CHUNKS)
return; // queue is totally full!
_chunks.push_back(Array<_ClusterSendQueueEntry,ZT_CLUSTER_QUEUE_CHUNK_SIZE>());
e = &(_chunks.back().data[0]);
for(unsigned int i=1;i<ZT_CLUSTER_QUEUE_CHUNK_SIZE;++i)
_pool[_poolCount++] = &(_chunks.back().data[i]);
}
e->timestamp = now;
e->fromPeerAddress = from;
e->toPeerAddress = to;
memcpy(e->data,data,len);
e->len = len;
e->unite = unite;
_bySrc.insert(std::pair<Address,_ClusterSendQueueEntry *>(from,e));
_byDest.insert(std::pair<Address,_ClusterSendQueueEntry *>(to,e));
}
inline void expire(uint64_t now)
{
Mutex::Lock _l(_lock);
for(std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_bySrc.begin());qi!=_bySrc.end();) {
if ((now - qi->second->timestamp) > ZT_CLUSTER_QUEUE_EXPIRATION) {
_byDest.erase(std::pair<Address,_ClusterSendQueueEntry *>(qi->second->toPeerAddress,qi->second));
_pool[_poolCount++] = qi->second;
_bySrc.erase(qi++);
} else ++qi;
}
}
/**
* Get and dequeue entries for a given destination address
*
* After use these entries must be returned with returnToPool()!
*
* @param dest Destination address
* @param results Array to fill with results
* @param maxResults Size of results[] in pointers
* @return Number of actual results returned
*/
inline unsigned int getByDest(const Address &dest,_ClusterSendQueueEntry **results,unsigned int maxResults)
{
unsigned int count = 0;
Mutex::Lock _l(_lock);
std::set< std::pair<Address,_ClusterSendQueueEntry *> >::iterator qi(_byDest.lower_bound(std::pair<Address,_ClusterSendQueueEntry *>(dest,(_ClusterSendQueueEntry *)0)));
while ((qi != _byDest.end())&&(qi->first == dest)) {
_bySrc.erase(std::pair<Address,_ClusterSendQueueEntry *>(qi->second->fromPeerAddress,qi->second));
results[count++] = qi->second;
if (count == maxResults)
break;
_byDest.erase(qi++);
}
return count;
}
/**
* Return entries to pool after use
*
* @param entries Array of entries
* @param count Number of entries
*/
inline void returnToPool(_ClusterSendQueueEntry **entries,unsigned int count)
{
Mutex::Lock _l(_lock);
for(unsigned int i=0;i<count;++i)
_pool[_poolCount++] = entries[i];
}
private:
std::list< Array<_ClusterSendQueueEntry,ZT_CLUSTER_QUEUE_CHUNK_SIZE> > _chunks;
_ClusterSendQueueEntry *_pool[ZT_CLUSTER_QUEUE_CHUNK_SIZE * ZT_CLUSTER_MAX_QUEUE_CHUNKS];
unsigned long _poolCount;
std::set< std::pair<Address,_ClusterSendQueueEntry *> > _bySrc;
std::set< std::pair<Address,_ClusterSendQueueEntry *> > _byDest;
Mutex _lock;
};
Cluster::Cluster(
const RuntimeEnvironment *renv,
uint16_t id,
const std::vector<InetAddress> &zeroTierPhysicalEndpoints,
int32_t x,
int32_t y,
int32_t z,
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
void *sendFunctionArg,
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
void *addressToLocationFunctionArg) :
RR(renv),
_sendQueue(new _ClusterSendQueue()),
_sendFunction(sendFunction),
_sendFunctionArg(sendFunctionArg),
_addressToLocationFunction(addressToLocationFunction),
_addressToLocationFunctionArg(addressToLocationFunctionArg),
_x(x),
_y(y),
_z(z),
_id(id),
_zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints),
_members(new _Member[ZT_CLUSTER_MAX_MEMBERS]),
_lastFlushed(0),
_lastCleanedRemotePeers(0),
_lastCleanedQueue(0)
{
uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
// Generate master secret by hashing the secret from our Identity key pair
RR->identity.sha512PrivateKey(_masterSecret);
// Generate our inbound message key, which is the master secret XORed with our ID and hashed twice
memcpy(stmp,_masterSecret,sizeof(stmp));
stmp[0] ^= Utils::hton(id);
SHA512::hash(stmp,stmp,sizeof(stmp));
SHA512::hash(stmp,stmp,sizeof(stmp));
memcpy(_key,stmp,sizeof(_key));
Utils::burn(stmp,sizeof(stmp));
}
Cluster::~Cluster()
{
Utils::burn(_masterSecret,sizeof(_masterSecret));
Utils::burn(_key,sizeof(_key));
delete [] _members;
delete _sendQueue;
}
void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
{
Buffer<ZT_CLUSTER_MAX_MESSAGE_LENGTH> dmsg;
{
// FORMAT: <[16] iv><[8] MAC><... data>
if ((len < 24)||(len > ZT_CLUSTER_MAX_MESSAGE_LENGTH))
return;
// 16-byte IV: first 8 bytes XORed with key, last 8 bytes used as Salsa20 64-bit IV
char keytmp[32];
memcpy(keytmp,_key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= reinterpret_cast<const char *>(msg)[i];
Salsa20 s20(keytmp,256,reinterpret_cast<const char *>(msg) + 8);
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
s20.encrypt12(polykey,polykey,sizeof(polykey));
// Compute 16-byte MAC
char mac[ZT_POLY1305_MAC_LEN];
Poly1305::compute(mac,reinterpret_cast<const char *>(msg) + 24,len - 24,polykey);
// Check first 8 bytes of MAC against 64-bit MAC in stream
if (!Utils::secureEq(mac,reinterpret_cast<const char *>(msg) + 16,8))
return;
// Decrypt!
dmsg.setSize(len - 24);
s20.decrypt12(reinterpret_cast<const char *>(msg) + 24,const_cast<void *>(dmsg.data()),dmsg.size());
}
if (dmsg.size() < 4)
return;
const uint16_t fromMemberId = dmsg.at<uint16_t>(0);
unsigned int ptr = 2;
if (fromMemberId == _id) // sanity check: we don't talk to ourselves
return;
const uint16_t toMemberId = dmsg.at<uint16_t>(ptr);
ptr += 2;
if (toMemberId != _id) // sanity check: message not for us?
return;
{ // make sure sender is actually considered a member
Mutex::Lock _l3(_memberIds_m);
if (std::find(_memberIds.begin(),_memberIds.end(),fromMemberId) == _memberIds.end())
return;
}
try {
while (ptr < dmsg.size()) {
const unsigned int mlen = dmsg.at<uint16_t>(ptr); ptr += 2;
const unsigned int nextPtr = ptr + mlen;
if (nextPtr > dmsg.size())
break;
int mtype = -1;
try {
switch((StateMessageType)(mtype = (int)dmsg[ptr++])) {
default:
break;
case CLUSTER_MESSAGE_ALIVE: {
_Member &m = _members[fromMemberId];
Mutex::Lock mlck(m.lock);
ptr += 7; // skip version stuff, not used yet
m.x = dmsg.at<int32_t>(ptr); ptr += 4;
m.y = dmsg.at<int32_t>(ptr); ptr += 4;
m.z = dmsg.at<int32_t>(ptr); ptr += 4;
ptr += 8; // skip local clock, not used
m.load = dmsg.at<uint64_t>(ptr); ptr += 8;
m.peers = dmsg.at<uint64_t>(ptr); ptr += 8;
ptr += 8; // skip flags, unused
#ifdef ZT_TRACE
std::string addrs;
#endif
unsigned int physicalAddressCount = dmsg[ptr++];
m.zeroTierPhysicalEndpoints.clear();
for(unsigned int i=0;i<physicalAddressCount;++i) {
m.zeroTierPhysicalEndpoints.push_back(InetAddress());
ptr += m.zeroTierPhysicalEndpoints.back().deserialize(dmsg,ptr);
if (!(m.zeroTierPhysicalEndpoints.back())) {
m.zeroTierPhysicalEndpoints.pop_back();
}
#ifdef ZT_TRACE
else {
if (addrs.length() > 0)
addrs.push_back(',');
addrs.append(m.zeroTierPhysicalEndpoints.back().toString());
}
#endif
}
#ifdef ZT_TRACE
if ((RR->node->now() - m.lastReceivedAliveAnnouncement) >= ZT_CLUSTER_TIMEOUT) {
TRACE("[%u] I'm alive! peers close to %d,%d,%d can be redirected to: %s",(unsigned int)fromMemberId,m.x,m.y,m.z,addrs.c_str());
}
#endif
m.lastReceivedAliveAnnouncement = RR->node->now();
} break;
case CLUSTER_MESSAGE_HAVE_PEER: {
Identity id;
ptr += id.deserialize(dmsg,ptr);
if (id) {
RR->topology->saveIdentity(id);
{
Mutex::Lock _l(_remotePeers_m);
_remotePeers[std::pair<Address,unsigned int>(id.address(),(unsigned int)fromMemberId)] = RR->node->now();
}
_ClusterSendQueueEntry *q[16384]; // 16384 is "tons"
unsigned int qc = _sendQueue->getByDest(id.address(),q,16384);
for(unsigned int i=0;i<qc;++i)
this->sendViaCluster(q[i]->fromPeerAddress,q[i]->toPeerAddress,q[i]->data,q[i]->len,q[i]->unite);
_sendQueue->returnToPool(q,qc);
TRACE("[%u] has %s (retried %u queued sends)",(unsigned int)fromMemberId,id.address().toString().c_str(),qc);
}
} break;
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->hasClusterOptimalPath(RR->node->now())) ) {
Buffer<1024> buf;
peer->identity().serialize(buf);
Mutex::Lock _l2(_members[fromMemberId].lock);
_send(fromMemberId,CLUSTER_MESSAGE_HAVE_PEER,buf.data(),buf.size());
}
} break;
case CLUSTER_MESSAGE_REMOTE_PACKET: {
const unsigned int plen = dmsg.at<uint16_t>(ptr); ptr += 2;
if (plen) {
Packet remotep(dmsg.field(ptr,plen),plen); ptr += plen;
//TRACE("remote %s from %s via %u (%u bytes)",Packet::verbString(remotep.verb()),remotep.source().toString().c_str(),fromMemberId,plen);
switch(remotep.verb()) {
case Packet::VERB_WHOIS: _doREMOTE_WHOIS(fromMemberId,remotep); break;
case Packet::VERB_MULTICAST_GATHER: _doREMOTE_MULTICAST_GATHER(fromMemberId,remotep); break;
default: break; // ignore things we don't care about across cluster
}
}
} break;
case CLUSTER_MESSAGE_PROXY_UNITE: {
const Address localPeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
const Address remotePeerAddress(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
const unsigned int numRemotePeerPaths = dmsg[ptr++];
InetAddress remotePeerPaths[256]; // size is 8-bit, so 256 is max
for(unsigned int i=0;i<numRemotePeerPaths;++i)
ptr += remotePeerPaths[i].deserialize(dmsg,ptr);
TRACE("[%u] requested that we unite local %s with remote %s",(unsigned int)fromMemberId,localPeerAddress.toString().c_str(),remotePeerAddress.toString().c_str());
const uint64_t now = RR->node->now();
SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
if ((localPeer)&&(numRemotePeerPaths > 0)) {
InetAddress bestLocalV4,bestLocalV6;
localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6);
InetAddress bestRemoteV4,bestRemoteV6;
for(unsigned int i=0;i<numRemotePeerPaths;++i) {
if ((bestRemoteV4)&&(bestRemoteV6))
break;
switch(remotePeerPaths[i].ss_family) {
case AF_INET:
if (!bestRemoteV4)
bestRemoteV4 = remotePeerPaths[i];
break;
case AF_INET6:
if (!bestRemoteV6)
bestRemoteV6 = remotePeerPaths[i];
break;
}
}
Packet rendezvousForLocal(localPeerAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS);
rendezvousForLocal.append((uint8_t)0);
remotePeerAddress.appendTo(rendezvousForLocal);
Buffer<2048> rendezvousForRemote;
remotePeerAddress.appendTo(rendezvousForRemote);
rendezvousForRemote.append((uint8_t)Packet::VERB_RENDEZVOUS);
rendezvousForRemote.addSize(2); // space for actual packet payload length
rendezvousForRemote.append((uint8_t)0); // flags == 0
localPeerAddress.appendTo(rendezvousForRemote);
bool haveMatch = false;
if ((bestLocalV6)&&(bestRemoteV6)) {
haveMatch = true;
rendezvousForLocal.append((uint16_t)bestRemoteV6.port());
rendezvousForLocal.append((uint8_t)16);
rendezvousForLocal.append(bestRemoteV6.rawIpData(),16);
rendezvousForRemote.append((uint16_t)bestLocalV6.port());
rendezvousForRemote.append((uint8_t)16);
rendezvousForRemote.append(bestLocalV6.rawIpData(),16);
rendezvousForRemote.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(9 + 16));
} else if ((bestLocalV4)&&(bestRemoteV4)) {
haveMatch = true;
rendezvousForLocal.append((uint16_t)bestRemoteV4.port());
rendezvousForLocal.append((uint8_t)4);
rendezvousForLocal.append(bestRemoteV4.rawIpData(),4);
rendezvousForRemote.append((uint16_t)bestLocalV4.port());
rendezvousForRemote.append((uint8_t)4);
rendezvousForRemote.append(bestLocalV4.rawIpData(),4);
rendezvousForRemote.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(9 + 4));
}
if (haveMatch) {
{
Mutex::Lock _l2(_members[fromMemberId].lock);
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
}
RR->sw->send(rendezvousForLocal,true,0);
}
}
} break;
case CLUSTER_MESSAGE_PROXY_SEND: {
const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); ptr += ZT_ADDRESS_LENGTH;
const Packet::Verb verb = (Packet::Verb)dmsg[ptr++];
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,0);
//TRACE("[%u] proxy send %s to %s length %u",(unsigned int)fromMemberId,Packet::verbString(verb),rcpt.toString().c_str(),len);
} break;
}
} catch ( ... ) {
TRACE("invalid message of size %u type %d (inner decode), discarding",mlen,mtype);
// drop invalids
}
ptr = nextPtr;
}
} catch ( ... ) {
TRACE("invalid message (outer loop), discarding");
// drop invalids
}
}
void Cluster::broadcastHavePeer(const Identity &id)
{
Buffer<1024> buf;
id.serialize(buf);
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
Mutex::Lock _l2(_members[*mid].lock);
_send(*mid,CLUSTER_MESSAGE_HAVE_PEER,buf.data(),buf.size());
}
}
void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
{
if (len > ZT_PROTO_MAX_PACKET_LENGTH) // sanity check
return;
const uint64_t now = RR->node->now();
uint64_t mostRecentTs = 0;
unsigned int mostRecentMemberId = 0xffffffff;
{
Mutex::Lock _l2(_remotePeers_m);
std::map< std::pair<Address,unsigned int>,uint64_t >::const_iterator rpe(_remotePeers.lower_bound(std::pair<Address,unsigned int>(toPeerAddress,0)));
for(;;) {
if ((rpe == _remotePeers.end())||(rpe->first.first != toPeerAddress))
break;
else if (rpe->second > mostRecentTs) {
mostRecentTs = rpe->second;
mostRecentMemberId = rpe->first.second;
}
++rpe;
}
}
const uint64_t age = now - mostRecentTs;
if (age >= (ZT_PEER_ACTIVITY_TIMEOUT / 3)) {
const bool enqueueAndWait = ((age >= ZT_PEER_ACTIVITY_TIMEOUT)||(mostRecentMemberId > 0xffff));
// Poll everyone with WANT_PEER if the age of our most recent entry is
// approaching expiration (or has expired, or does not exist).
char tmp[ZT_ADDRESS_LENGTH];
toPeerAddress.copyTo(tmp,ZT_ADDRESS_LENGTH);
{
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
Mutex::Lock _l2(_members[*mid].lock);
_send(*mid,CLUSTER_MESSAGE_WANT_PEER,tmp,ZT_ADDRESS_LENGTH);
}
}
// If there isn't a good place to send via, then enqueue this for retrying
// later and return after having broadcasted a WANT_PEER.
if (enqueueAndWait) {
TRACE("sendViaCluster %s -> %s enqueueing to wait for HAVE_PEER",fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
_sendQueue->enqueue(now,fromPeerAddress,toPeerAddress,data,len,unite);
return;
}
}
Buffer<1024> buf;
if (unite) {
InetAddress v4,v6;
if (fromPeerAddress) {
SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
if (fromPeer)
fromPeer->getBestActiveAddresses(now,v4,v6);
}
uint8_t addrCount = 0;
if (v4)
++addrCount;
if (v6)
++addrCount;
if (addrCount) {
toPeerAddress.appendTo(buf);
fromPeerAddress.appendTo(buf);
buf.append(addrCount);
if (v4)
v4.serialize(buf);
if (v6)
v6.serialize(buf);
}
}
{
Mutex::Lock _l2(_members[mostRecentMemberId].lock);
if (buf.size() > 0)
_send(mostRecentMemberId,CLUSTER_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
if (_members[mostRecentMemberId].zeroTierPhysicalEndpoints.size() > 0) {
TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
RR->node->putPacket(InetAddress(),_members[mostRecentMemberId].zeroTierPhysicalEndpoints.front(),data,len);
}
}
}
void Cluster::sendDistributedQuery(const Packet &pkt)
{
Buffer<4096> buf;
buf.append((uint16_t)pkt.size());
buf.append(pkt.data(),pkt.size());
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
Mutex::Lock _l2(_members[*mid].lock);
_send(*mid,CLUSTER_MESSAGE_REMOTE_PACKET,buf.data(),buf.size());
}
}
void Cluster::doPeriodicTasks()
{
const uint64_t now = RR->node->now();
if ((now - _lastFlushed) >= ZT_CLUSTER_FLUSH_PERIOD) {
_lastFlushed = now;
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
Mutex::Lock _l2(_members[*mid].lock);
if ((now - _members[*mid].lastAnnouncedAliveTo) >= ((ZT_CLUSTER_TIMEOUT / 2) - 1000)) {
_members[*mid].lastAnnouncedAliveTo = now;
Buffer<2048> alive;
alive.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR);
alive.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR);
alive.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
alive.append((uint8_t)ZT_PROTO_VERSION);
if (_addressToLocationFunction) {
alive.append((int32_t)_x);
alive.append((int32_t)_y);
alive.append((int32_t)_z);
} else {
alive.append((int32_t)0);
alive.append((int32_t)0);
alive.append((int32_t)0);
}
alive.append((uint64_t)now);
alive.append((uint64_t)0); // TODO: compute and send load average
alive.append((uint64_t)RR->topology->countActive(now));
alive.append((uint64_t)0); // unused/reserved flags
alive.append((uint8_t)_zeroTierPhysicalEndpoints.size());
for(std::vector<InetAddress>::const_iterator pe(_zeroTierPhysicalEndpoints.begin());pe!=_zeroTierPhysicalEndpoints.end();++pe)
pe->serialize(alive);
_send(*mid,CLUSTER_MESSAGE_ALIVE,alive.data(),alive.size());
}
_flush(*mid);
}
}
if ((now - _lastCleanedRemotePeers) >= (ZT_PEER_ACTIVITY_TIMEOUT * 2)) {
_lastCleanedRemotePeers = now;
Mutex::Lock _l(_remotePeers_m);
for(std::map< std::pair<Address,unsigned int>,uint64_t >::iterator rp(_remotePeers.begin());rp!=_remotePeers.end();) {
if ((now - rp->second) >= ZT_PEER_ACTIVITY_TIMEOUT)
_remotePeers.erase(rp++);
else ++rp;
}
}
if ((now - _lastCleanedQueue) >= ZT_CLUSTER_QUEUE_EXPIRATION) {
_lastCleanedQueue = now;
_sendQueue->expire(now);
}
}
void Cluster::addMember(uint16_t memberId)
{
if ((memberId >= ZT_CLUSTER_MAX_MEMBERS)||(memberId == _id))
return;
Mutex::Lock _l2(_members[memberId].lock);
{
Mutex::Lock _l(_memberIds_m);
if (std::find(_memberIds.begin(),_memberIds.end(),memberId) != _memberIds.end())
return;
_memberIds.push_back(memberId);
std::sort(_memberIds.begin(),_memberIds.end());
}
_members[memberId].clear();
// Generate this member's message key from the master and its ID
uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
memcpy(stmp,_masterSecret,sizeof(stmp));
stmp[0] ^= Utils::hton(memberId);
SHA512::hash(stmp,stmp,sizeof(stmp));
SHA512::hash(stmp,stmp,sizeof(stmp));
memcpy(_members[memberId].key,stmp,sizeof(_members[memberId].key));
Utils::burn(stmp,sizeof(stmp));
// Prepare q
_members[memberId].q.clear();
char iv[16];
Utils::getSecureRandom(iv,16);
_members[memberId].q.append(iv,16);
_members[memberId].q.addSize(8); // room for MAC
_members[memberId].q.append((uint16_t)_id);
_members[memberId].q.append((uint16_t)memberId);
}
void Cluster::removeMember(uint16_t memberId)
{
Mutex::Lock _l(_memberIds_m);
std::vector<uint16_t> newMemberIds;
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
if (*mid != memberId)
newMemberIds.push_back(*mid);
}
_memberIds = newMemberIds;
}
bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload)
{
if (_addressToLocationFunction) {
// Pick based on location if it can be determined
int px = 0,py = 0,pz = 0;
if (_addressToLocationFunction(_addressToLocationFunctionArg,reinterpret_cast<const struct sockaddr_storage *>(&peerPhysicalAddress),&px,&py,&pz) == 0) {
TRACE("no geolocation data for %s (geo-lookup is lazy/async so it may work next time)",peerPhysicalAddress.toIpString().c_str());
return false;
}
// Find member closest to this peer
const uint64_t now = RR->node->now();
std::vector<InetAddress> best;
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
double bestDistance = (offload ? 2147483648.0 : currentDistance);
unsigned int bestMember = _id;
{
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
_Member &m = _members[*mid];
Mutex::Lock _ml(m.lock);
// Consider member if it's alive and has sent us a location and one or more physical endpoints to send peers to
if ( ((now - m.lastReceivedAliveAnnouncement) < ZT_CLUSTER_TIMEOUT) && ((m.x != 0)||(m.y != 0)||(m.z != 0)) && (m.zeroTierPhysicalEndpoints.size() > 0) ) {
const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
if (mdist < bestDistance) {
bestDistance = mdist;
bestMember = *mid;
best = m.zeroTierPhysicalEndpoints;
}
}
}
}
// Redirect to a closer member if it has a ZeroTier endpoint address in the same ss_family
for(std::vector<InetAddress>::const_iterator a(best.begin());a!=best.end();++a) {
if (a->ss_family == peerPhysicalAddress.ss_family) {
TRACE("%s at [%d,%d,%d] is %f from us but %f from %u, can redirect to %s",peerAddress.toString().c_str(),px,py,pz,currentDistance,bestDistance,bestMember,a->toString().c_str());
redirectTo = *a;
return true;
}
}
TRACE("%s at [%d,%d,%d] is %f from us, no better endpoints found",peerAddress.toString().c_str(),px,py,pz,currentDistance);
return false;
} else {
// TODO: pick based on load if no location info?
return false;
}
}
void Cluster::status(ZT_ClusterStatus &status) const
{
const uint64_t now = RR->node->now();
memset(&status,0,sizeof(ZT_ClusterStatus));
status.myId = _id;
{
ZT_ClusterMemberStatus *const s = &(status.members[status.clusterSize++]);
s->id = _id;
s->alive = 1;
s->x = _x;
s->y = _y;
s->z = _z;
s->load = 0; // TODO
s->peers = RR->topology->countActive(now);
for(std::vector<InetAddress>::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) {
if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check
break;
memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage));
}
}
{
Mutex::Lock _l1(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
if (status.clusterSize >= ZT_CLUSTER_MAX_MEMBERS) // sanity check
break;
_Member &m = _members[*mid];
Mutex::Lock ml(m.lock);
ZT_ClusterMemberStatus *const s = &(status.members[status.clusterSize++]);
s->id = *mid;
s->msSinceLastHeartbeat = (unsigned int)std::min((uint64_t)(~((unsigned int)0)),(now - m.lastReceivedAliveAnnouncement));
s->alive = (s->msSinceLastHeartbeat < ZT_CLUSTER_TIMEOUT) ? 1 : 0;
s->x = m.x;
s->y = m.y;
s->z = m.z;
s->load = m.load;
s->peers = m.peers;
for(std::vector<InetAddress>::const_iterator ep(m.zeroTierPhysicalEndpoints.begin());ep!=m.zeroTierPhysicalEndpoints.end();++ep) {
if (s->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check
break;
memcpy(&(s->zeroTierPhysicalEndpoints[s->numZeroTierPhysicalEndpoints++]),&(*ep),sizeof(struct sockaddr_storage));
}
}
}
}
void Cluster::_send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len)
{
if ((len + 3) > (ZT_CLUSTER_MAX_MESSAGE_LENGTH - (24 + 2 + 2))) // sanity check
return;
_Member &m = _members[memberId];
// assumes m.lock is locked!
if ((m.q.size() + len + 3) > ZT_CLUSTER_MAX_MESSAGE_LENGTH)
_flush(memberId);
m.q.append((uint16_t)(len + 1));
m.q.append((uint8_t)type);
m.q.append(msg,len);
}
void Cluster::_flush(uint16_t memberId)
{
_Member &m = _members[memberId];
// assumes m.lock is locked!
if (m.q.size() > (24 + 2 + 2)) { // 16-byte IV + 8-byte MAC + 2 byte from-member-ID + 2 byte to-member-ID
// Create key from member's key and IV
char keytmp[32];
memcpy(keytmp,m.key,32);
for(int i=0;i<8;++i)
keytmp[i] ^= m.q[i];
Salsa20 s20(keytmp,256,m.q.field(8,8));
Utils::burn(keytmp,sizeof(keytmp));
// One-time-use Poly1305 key from first 32 bytes of Salsa20 keystream (as per DJB/NaCl "standard")
char polykey[ZT_POLY1305_KEY_LEN];
memset(polykey,0,sizeof(polykey));
s20.encrypt12(polykey,polykey,sizeof(polykey));
// Encrypt m.q in place
s20.encrypt12(reinterpret_cast<const char *>(m.q.data()) + 24,const_cast<char *>(reinterpret_cast<const char *>(m.q.data())) + 24,m.q.size() - 24);
// Add MAC for authentication (encrypt-then-MAC)
char mac[ZT_POLY1305_MAC_LEN];
Poly1305::compute(mac,reinterpret_cast<const char *>(m.q.data()) + 24,m.q.size() - 24,polykey);
memcpy(m.q.field(16,8),mac,8);
// Send!
_sendFunction(_sendFunctionArg,memberId,m.q.data(),m.q.size());
// Prepare for more
m.q.clear();
char iv[16];
Utils::getSecureRandom(iv,16);
m.q.append(iv,16);
m.q.addSize(8); // room for MAC
m.q.append((uint16_t)_id); // from member ID
m.q.append((uint16_t)memberId); // to member ID
}
}
void Cluster::_doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep)
{
if (remotep.payloadLength() >= ZT_ADDRESS_LENGTH) {
Identity queried(RR->topology->getIdentity(Address(remotep.payload(),ZT_ADDRESS_LENGTH)));
if (queried) {
Buffer<1024> routp;
remotep.source().appendTo(routp);
routp.append((uint8_t)Packet::VERB_OK);
routp.addSize(2); // space for length
routp.append((uint8_t)Packet::VERB_WHOIS);
routp.append(remotep.packetId());
queried.serialize(routp);
routp.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(routp.size() - ZT_ADDRESS_LENGTH - 3));
TRACE("responding to remote WHOIS from %s @ %u with identity of %s",remotep.source().toString().c_str(),(unsigned int)fromMemberId,queried.address().toString().c_str());
Mutex::Lock _l2(_members[fromMemberId].lock);
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,routp.data(),routp.size());
}
}
}
void Cluster::_doREMOTE_MULTICAST_GATHER(uint64_t fromMemberId,const Packet &remotep)
{
const uint64_t nwid = remotep.at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
const MulticastGroup mg(MAC(remotep.field(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_MAC,6),6),remotep.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_ADI));
unsigned int gatherLimit = remotep.at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_GATHER_LIMIT);
const Address remotePeerAddress(remotep.source());
if (gatherLimit) {
Buffer<ZT_PROTO_MAX_PACKET_LENGTH> routp;
remotePeerAddress.appendTo(routp);
routp.append((uint8_t)Packet::VERB_OK);
routp.addSize(2); // space for length
routp.append((uint8_t)Packet::VERB_MULTICAST_GATHER);
routp.append(remotep.packetId());
routp.append(nwid);
mg.mac().appendTo(routp);
routp.append((uint32_t)mg.adi());
if (gatherLimit > ((ZT_CLUSTER_MAX_MESSAGE_LENGTH - 80) / 5))
gatherLimit = ((ZT_CLUSTER_MAX_MESSAGE_LENGTH - 80) / 5);
if (RR->mc->gather(remotePeerAddress,nwid,mg,routp,gatherLimit)) {
routp.setAt<uint16_t>(ZT_ADDRESS_LENGTH + 1,(uint16_t)(routp.size() - ZT_ADDRESS_LENGTH - 3));
TRACE("responding to remote MULTICAST_GATHER from %s @ %u with %u bytes",remotePeerAddress.toString().c_str(),(unsigned int)fromMemberId,routp.size());
Mutex::Lock _l2(_members[fromMemberId].lock);
_send(fromMemberId,CLUSTER_MESSAGE_PROXY_SEND,routp.data(),routp.size());
}
}
}
} // namespace ZeroTier
#endif // ZT_ENABLE_CLUSTER

418
node/Cluster.hpp Normal file
View File

@@ -0,0 +1,418 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_CLUSTER_HPP
#define ZT_CLUSTER_HPP
#ifdef ZT_ENABLE_CLUSTER
#include <map>
#include "Constants.hpp"
#include "../include/ZeroTierOne.h"
#include "Address.hpp"
#include "InetAddress.hpp"
#include "SHA512.hpp"
#include "Utils.hpp"
#include "Buffer.hpp"
#include "Mutex.hpp"
#include "SharedPtr.hpp"
#include "Hashtable.hpp"
#include "Packet.hpp"
#include "SharedPtr.hpp"
/**
* Timeout for cluster members being considered "alive"
*
* A cluster member is considered dead and will no longer have peers
* redirected to it if we have not heard a heartbeat in this long.
*/
#define ZT_CLUSTER_TIMEOUT 5000
/**
* Desired period between doPeriodicTasks() in milliseconds
*/
#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 50
/**
* How often to flush outgoing message queues (maximum interval)
*/
#define ZT_CLUSTER_FLUSH_PERIOD 100
/**
* Maximum number of queued outgoing packets per sender address
*/
#define ZT_CLUSTER_MAX_QUEUE_PER_SENDER 8
/**
* Expiration time for send queue entries
*/
#define ZT_CLUSTER_QUEUE_EXPIRATION 5000
/**
* Chunk size for allocating queue entries
*
* Queue entries are allocated in chunks of this many and are added to a pool.
* ZT_CLUSTER_MAX_QUEUE_GLOBAL must be evenly divisible by this.
*/
#define ZT_CLUSTER_QUEUE_CHUNK_SIZE 32
/**
* Maximum number of chunks to ever allocate
*
* This is a global sanity limit to prevent resource exhaustion attacks. It
* works out to about 600mb of RAM. You'll never see this on a normal edge
* node. We're unlikely to see this on a root server unless someone is DOSing
* us. In that case cluster relaying will be affected but other functions
* should continue to operate normally.
*/
#define ZT_CLUSTER_MAX_QUEUE_CHUNKS 8194
/**
* Max data per queue entry
*
* If we ever support larger transport MTUs this must be increased. The plus
* 16 is just a small margin and has no special meaning.
*/
#define ZT_CLUSTER_SEND_QUEUE_DATA_MAX (ZT_UDP_DEFAULT_PAYLOAD_MTU + 16)
namespace ZeroTier {
class RuntimeEnvironment;
class MulticastGroup;
class Peer;
class Identity;
// Internal class implemented inside Cluster.cpp
class _ClusterSendQueue;
/**
* Multi-homing cluster state replication and packet relaying
*
* Multi-homing means more than one node sharing the same ZeroTier identity.
* There is nothing in the protocol to prevent this, but to make it work well
* requires the devices sharing an identity to cooperate and share some
* information.
*
* There are three use cases we want to fulfill:
*
* (1) Multi-homing of root servers with handoff for efficient routing,
* HA, and load balancing across many commodity nodes.
* (2) Multi-homing of network controllers for the same reason.
* (3) Multi-homing of nodes on virtual networks, such as domain servers
* and other important endpoints.
*
* These use cases are in order of escalating difficulty. The initial
* version of Cluster is aimed at satisfying the first, though you are
* free to try #2 and #3.
*/
class Cluster
{
public:
/**
* State message types
*/
enum StateMessageType
{
CLUSTER_MESSAGE_NOP = 0,
/**
* This cluster member is alive:
* <[2] version minor>
* <[2] version major>
* <[2] version revision>
* <[1] protocol version>
* <[4] X location (signed 32-bit)>
* <[4] Y location (signed 32-bit)>
* <[4] Z location (signed 32-bit)>
* <[8] local clock at this member>
* <[8] load average>
* <[8] number of peers>
* <[8] flags (currently unused, must be zero)>
* <[1] number of preferred ZeroTier endpoints>
* <[...] InetAddress(es) of preferred ZeroTier endpoint(s)>
*
* Cluster members constantly broadcast an alive heartbeat and will only
* receive peer redirects if they've done so within the timeout.
*/
CLUSTER_MESSAGE_ALIVE = 1,
/**
* Cluster member has this peer:
* <[...] serialized identity of peer>
*
* This is typically sent in response to WANT_PEER but can also be pushed
* to prepopulate if this makes sense.
*/
CLUSTER_MESSAGE_HAVE_PEER = 2,
/**
* Cluster member wants this peer:
* <[5] ZeroTier address of peer>
*
* Members that have a direct link to this peer will respond with
* HAVE_PEER.
*/
CLUSTER_MESSAGE_WANT_PEER = 3,
/**
* A remote packet that we should also possibly respond to:
* <[2] 16-bit length of remote packet>
* <[...] remote packet payload>
*
* Cluster members may relay requests by relaying the request packet.
* These may include requests such as WHOIS and MULTICAST_GATHER. The
* packet must be already decrypted, decompressed, and authenticated.
*
* This can only be used for small request packets as per the cluster
* message size limit, but since these are the only ones in question
* this is fine.
*
* If a response is generated it is sent via PROXY_SEND.
*/
CLUSTER_MESSAGE_REMOTE_PACKET = 4,
/**
* Request that VERB_RENDEZVOUS be sent to a peer that we have:
* <[5] ZeroTier address of peer on recipient's side>
* <[5] ZeroTier address of peer on sender's side>
* <[1] 8-bit number of sender's peer's active path addresses>
* <[...] series of serialized InetAddresses of sender's peer's paths>
*
* This requests that we perform NAT-t introduction between a peer that
* we have and one on the sender's side. The sender furnishes contact
* info for its peer, and we send VERB_RENDEZVOUS to both sides: to ours
* directly and with PROXY_SEND to theirs.
*/
CLUSTER_MESSAGE_PROXY_UNITE = 5,
/**
* Request that a cluster member send a packet to a locally-known peer:
* <[5] ZeroTier address of recipient>
* <[1] packet verb>
* <[2] length of packet payload>
* <[...] packet payload>
*
* This differs from RELAY in that it requests the receiving cluster
* member to actually compose a ZeroTier Packet from itself to the
* provided recipient. RELAY simply says "please forward this blob."
* RELAY is used to implement peer-to-peer relaying with RENDEZVOUS,
* while PROXY_SEND is used to implement proxy sending (which right
* now is only used to send RENDEZVOUS).
*/
CLUSTER_MESSAGE_PROXY_SEND = 6,
/**
* Replicate a network config for a network we belong to:
* <[8] 64-bit network ID>
* <[2] 16-bit length of network config>
* <[...] serialized network config>
*
* This is used by clusters to avoid every member having to query
* for the same netconf for networks all members belong to.
*
* TODO: not implemented yet!
*/
CLUSTER_MESSAGE_NETWORK_CONFIG = 7
};
/**
* Construct a new cluster
*/
Cluster(
const RuntimeEnvironment *renv,
uint16_t id,
const std::vector<InetAddress> &zeroTierPhysicalEndpoints,
int32_t x,
int32_t y,
int32_t z,
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
void *sendFunctionArg,
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
void *addressToLocationFunctionArg);
~Cluster();
/**
* @return This cluster member's ID
*/
inline uint16_t id() const throw() { return _id; }
/**
* Handle an incoming intra-cluster message
*
* @param data Message data
* @param len Message length (max: ZT_CLUSTER_MAX_MESSAGE_LENGTH)
*/
void handleIncomingStateMessage(const void *msg,unsigned int len);
/**
* Broadcast that we have a given peer
*
* This should be done when new peers are first contacted.
*
* @param id Identity of peer
*/
void broadcastHavePeer(const Identity &id);
/**
* Send this packet via another node in this cluster if another node has this peer
*
* This is used in the outgoing packet and relaying logic in Switch to
* relay packets to other cluster members. It isn't PROXY_SEND-- that is
* used internally in Cluster to send responses to peer queries.
*
* @param fromPeerAddress Source peer address (if known, should be NULL for fragments)
* @param toPeerAddress Destination peer address
* @param data Packet or packet fragment data
* @param len Length of packet or fragment
* @param unite If true, also request proxy unite across cluster
*/
void sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
/**
* Send a distributed query to other cluster members
*
* Some queries such as WHOIS or MULTICAST_GATHER need a response from other
* cluster members. Replies (if any) will be sent back to the peer via
* PROXY_SEND across the cluster.
*
* @param pkt Packet to distribute
*/
void sendDistributedQuery(const Packet &pkt);
/**
* Call every ~ZT_CLUSTER_PERIODIC_TASK_PERIOD milliseconds.
*/
void doPeriodicTasks();
/**
* Add a member ID to this cluster
*
* @param memberId Member ID
*/
void addMember(uint16_t memberId);
/**
* Remove a member ID from this cluster
*
* @param memberId Member ID to remove
*/
void removeMember(uint16_t memberId);
/**
* Find a better cluster endpoint for this peer (if any)
*
* @param redirectTo InetAddress to be set to a better endpoint (if there is one)
* @param peerAddress Address of peer to (possibly) redirect
* @param peerPhysicalAddress Physical address of peer's current best path (where packet was most recently received or getBestPath()->address())
* @param offload Always redirect if possible -- can be used to offload peers during shutdown
* @return True if redirectTo was set to a new address, false if redirectTo was not modified
*/
bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
/**
* Fill out ZT_ClusterStatus structure (from core API)
*
* @param status Reference to structure to hold result (anything there is replaced)
*/
void status(ZT_ClusterStatus &status) const;
private:
void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len);
void _flush(uint16_t memberId);
void _doREMOTE_WHOIS(uint64_t fromMemberId,const Packet &remotep);
void _doREMOTE_MULTICAST_GATHER(uint64_t fromMemberId,const Packet &remotep);
// These are initialized in the constructor and remain immutable ------------
uint16_t _masterSecret[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
const RuntimeEnvironment *RR;
_ClusterSendQueue *const _sendQueue;
void (*_sendFunction)(void *,unsigned int,const void *,unsigned int);
void *_sendFunctionArg;
int (*_addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *);
void *_addressToLocationFunctionArg;
const int32_t _x;
const int32_t _y;
const int32_t _z;
const uint16_t _id;
const std::vector<InetAddress> _zeroTierPhysicalEndpoints;
// end immutable fields -----------------------------------------------------
struct _Member
{
unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
uint64_t lastReceivedAliveAnnouncement;
uint64_t lastAnnouncedAliveTo;
uint64_t load;
uint64_t peers;
int32_t x,y,z;
std::vector<InetAddress> zeroTierPhysicalEndpoints;
Buffer<ZT_CLUSTER_MAX_MESSAGE_LENGTH> q;
Mutex lock;
inline void clear()
{
lastReceivedAliveAnnouncement = 0;
lastAnnouncedAliveTo = 0;
load = 0;
peers = 0;
x = 0;
y = 0;
z = 0;
zeroTierPhysicalEndpoints.clear();
q.clear();
}
_Member() { this->clear(); }
~_Member() { Utils::burn(key,sizeof(key)); }
};
_Member *const _members;
std::vector<uint16_t> _memberIds;
Mutex _memberIds_m;
std::map< std::pair<Address,unsigned int>,uint64_t > _remotePeers; // we need ordered behavior and lower_bound here
Mutex _remotePeers_m;
uint64_t _lastFlushed;
uint64_t _lastCleanedRemotePeers;
uint64_t _lastCleanedQueue;
};
} // namespace ZeroTier
#endif // ZT_ENABLE_CLUSTER
#endif

View File

@@ -173,16 +173,11 @@
/**
* Timeout for receipt of fragmented packets in ms
*
* Since there's no retransmits, this is just a really bad case scenario for
* transit time. It's short enough that a DOS attack from exhausing buffers is
* very unlikely, as the transfer rate would have to be fast enough to fill
* system memory in this time.
*/
#define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 1000
#define ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT 500
/**
* Length of secret key in bytes -- 256-bit for Salsa20
* Length of secret key in bytes -- 256-bit -- do not change
*/
#define ZT_PEER_SECRET_KEY_LENGTH 32
@@ -194,7 +189,7 @@
/**
* Overriding granularity for timer tasks to prevent CPU-intensive thrashing on every packet
*/
#define ZT_CORE_TIMER_TASK_GRANULARITY 1000
#define ZT_CORE_TIMER_TASK_GRANULARITY 500
/**
* How long to remember peer records in RAM if they haven't been used
@@ -204,7 +199,7 @@
/**
* Delay between WHOIS retries in ms
*/
#define ZT_WHOIS_RETRY_DELAY 500
#define ZT_WHOIS_RETRY_DELAY 1000
/**
* Maximum identity WHOIS retries (each attempt tries consulting a different peer)
@@ -264,34 +259,23 @@
* This is also how often pings will be retried to upstream peers (relays, roots)
* constantly until something is heard.
*/
#define ZT_PING_CHECK_INVERVAL 6250
#define ZT_PING_CHECK_INVERVAL 9500
/**
* Delay between ordinary case pings of direct links
*/
#define ZT_PEER_DIRECT_PING_DELAY 120000
#define ZT_PEER_DIRECT_PING_DELAY 60000
/**
* Timeout for overall peer activity (measured from last receive)
*/
#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 4) + ZT_PING_CHECK_INVERVAL)
/**
* Delay between requests for updated network autoconf information
*/
#define ZT_NETWORK_AUTOCONF_DELAY 60000
/**
* Timeout for overall peer activity (measured from last receive)
*/
#define ZT_PEER_ACTIVITY_TIMEOUT (ZT_PEER_DIRECT_PING_DELAY + (ZT_PING_CHECK_INVERVAL * 3))
/**
* Stop relaying via peers that have not responded to direct sends
*
* When we send something (including frames), we generally expect a response.
* Switching relays if no response in a short period of time causes more
* rapid failover if a root server goes down or becomes unreachable. In the
* mistaken case, little harm is done as it'll pick the next-fastest
* root server and will switch back eventually.
*/
#define ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD 10000
/**
* Minimum interval between attempts by relays to unite peers
*
@@ -299,7 +283,7 @@
* a RENDEZVOUS message no more than this often. This instructs the peers
* to attempt NAT-t and gives each the other's corresponding IP:port pair.
*/
#define ZT_MIN_UNITE_INTERVAL 60000
#define ZT_MIN_UNITE_INTERVAL 30000
/**
* Delay between initial direct NAT-t packet and more aggressive techniques
@@ -309,20 +293,10 @@
*/
#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000
/**
* Size of anti-recursion history (see AntiRecursion.hpp)
*/
#define ZT_ANTIRECURSION_HISTORY_SIZE 16
/**
* Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding)
*/
#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000
/**
* Interval between direct path pushes in milliseconds
*/
#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000
#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000
/**
* How long (max) to remember network certificates of membership?
@@ -347,6 +321,30 @@
*/
#define ZT_MAX_BRIDGE_SPAM 16
/**
* Interval between direct path pushes in milliseconds
*/
#define ZT_DIRECT_PATH_PUSH_INTERVAL 120000
/**
* Time horizon for push direct paths cutoff
*/
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME 60000
/**
* Maximum number of direct path pushes within cutoff time
*
* This limits response to PUSH_DIRECT_PATHS to CUTOFF_LIMIT responses
* per CUTOFF_TIME milliseconds per peer to prevent this from being
* useful for DOS amplification attacks.
*/
#define ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT 5
/**
* Maximum number of paths per IP scope (e.g. global, link-local) and family (e.g. v4/v6)
*/
#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 1
/**
* A test pseudo-network-ID that can be joined
*

View File

@@ -1,82 +0,0 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../include/ZeroTierOne.h"
#include "Constants.hpp"
#include "Defaults.hpp"
#include "Utils.hpp"
// bin2c'd signed default root topology dictionary
#include "../root-topology/ZT_DEFAULT_ROOT_TOPOLOGY.c"
#ifdef __WINDOWS__
#include <WinSock2.h>
#include <Windows.h>
#include <ShlObj.h>
#endif
namespace ZeroTier {
const Defaults ZT_DEFAULTS;
static inline std::map< Address,Identity > _mkRootTopologyAuth()
{
std::map< Address,Identity > ua;
{ // 0001
Identity id("77792b1c02:0:b5c361e8e9c2154e82c3e902fdfc337468b092a7c4d8dc685c37eb10ee4f3c17cc0bb1d024167e8cb0824d12263428373582da3d0a9a14b36e4546c317e811e6");
ua[id.address()] = id;
}
{ // 0002
Identity id("86921e6de1:0:9ba04f9f12ed54ef567f548cb69d31e404537d7b0ee000c63f3d7c8d490a1a47a5a5b2af0cbe12d23f9194270593f298d936d7c872612ea509ef1c67ce2c7fc1");
ua[id.address()] = id;
}
{ // 0003
Identity id("90302b7025:0:358154a57af1b7afa07d0d91b69b92eaad2f11ade7f02343861f0c1b757d15626e8cb7f08fc52993d2202a39cbf5128c5647ee8c63d27d92db5a1d0fbe1eba19");
ua[id.address()] = id;
}
{ // 0004
Identity id("e5174078ee:0:c3f90daa834a74ee47105f5726ae2e29fc8ae0e939c9326788b52b16d847354de8de3b13a81896bbb509b91e1da21763073a30bbfb2b8e994550798d30a2d709");
ua[id.address()] = id;
}
return ua;
}
Defaults::Defaults() :
defaultRootTopology((const char *)ZT_DEFAULT_ROOT_TOPOLOGY,ZT_DEFAULT_ROOT_TOPOLOGY_LEN),
rootTopologyAuthorities(_mkRootTopologyAuth()),
v4Broadcast(((uint32_t)0xffffffff),ZT_DEFAULT_PORT)
{
}
} // namespace ZeroTier

95
node/DeferredPackets.cpp Normal file
View File

@@ -0,0 +1,95 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include "Constants.hpp"
#include "DeferredPackets.hpp"
#include "IncomingPacket.hpp"
#include "RuntimeEnvironment.hpp"
#include "Node.hpp"
namespace ZeroTier {
DeferredPackets::DeferredPackets(const RuntimeEnvironment *renv) :
RR(renv),
_readPtr(0),
_writePtr(0),
_die(false)
{
}
DeferredPackets::~DeferredPackets()
{
_q_m.lock();
_die = true;
_q_m.unlock();
_q_s.post();
}
bool DeferredPackets::enqueue(IncomingPacket *pkt)
{
_q_m.lock();
const unsigned long p = _writePtr % ZT_DEFFEREDPACKETS_MAX;
if (_q[p]) {
_q_m.unlock();
return false;
} else {
_q[p].setToUnsafe(pkt);
++_writePtr;
_q_m.unlock();
_q_s.post();
return true;
}
}
int DeferredPackets::process()
{
SharedPtr<IncomingPacket> pkt;
_q_m.lock();
if (_die) {
_q_m.unlock();
_q_s.post();
return -1;
}
while (_readPtr == _writePtr) {
_q_m.unlock();
_q_s.wait();
_q_m.lock();
if (_die) {
_q_m.unlock();
_q_s.post();
return -1;
}
}
pkt.swap(_q[_readPtr++ % ZT_DEFFEREDPACKETS_MAX]);
_q_m.unlock();
pkt->tryDecode(RR,true);
return 1;
}
} // namespace ZeroTier

98
node/DeferredPackets.hpp Normal file
View File

@@ -0,0 +1,98 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_DEFERREDPACKETS_HPP
#define ZT_DEFERREDPACKETS_HPP
#include "Constants.hpp"
#include "SharedPtr.hpp"
#include "Mutex.hpp"
#include "DeferredPackets.hpp"
#include "BinarySemaphore.hpp"
/**
* Maximum number of deferred packets
*/
#define ZT_DEFFEREDPACKETS_MAX 1024
namespace ZeroTier {
class IncomingPacket;
class RuntimeEnvironment;
/**
* Deferred packets
*
* IncomingPacket can defer its decoding this way by enqueueing itself here.
* When this is done, deferredDecode() is called later. This is done for
* operations that may be expensive to allow them to potentially be handled
* in the background or rate limited to maintain quality of service for more
* routine operations.
*/
class DeferredPackets
{
public:
DeferredPackets(const RuntimeEnvironment *renv);
~DeferredPackets();
/**
* Enqueue a packet
*
* Since packets enqueue themselves, they call it with 'this' and we wrap
* them in a SharedPtr<>. This is safe as SharedPtr<> is introspective and
* supports this. This should not be called from any other code outside
* IncomingPacket.
*
* @param pkt Packet to process later (possibly in the background)
* @return False if queue is full
*/
bool enqueue(IncomingPacket *pkt);
/**
* Wait for and then process a deferred packet
*
* If we are shutting down (in destructor), this returns -1 and should
* not be called again. Otherwise it returns the number of packets
* processed.
*
* @return Number processed or -1 if shutting down
*/
int process();
private:
SharedPtr<IncomingPacket> _q[ZT_DEFFEREDPACKETS_MAX];
const RuntimeEnvironment *const RR;
unsigned long _readPtr;
unsigned long _writePtr;
bool _die;
Mutex _q_m;
BinarySemaphore _q_s;
};
} // namespace ZeroTier
#endif

View File

@@ -103,7 +103,7 @@ public:
private:
unsigned long _idx;
Hashtable *_ht;
Hashtable::_Bucket *_b;
_Bucket *_b;
};
friend class Hashtable::Iterator;
@@ -322,7 +322,6 @@ public:
b->next = _t[bidx];
_t[bidx] = b;
++_s;
return b->v;
}
@@ -351,7 +350,6 @@ public:
b->next = _t[bidx];
_t[bidx] = b;
++_s;
return b->v;
}
@@ -382,7 +380,10 @@ private:
}
static inline unsigned long _hc(const uint32_t i)
{
// In the uint32_t case we use a simple multiplier for hashing to ensure coverage
return ((unsigned long)i * (unsigned long)0x9e3779b1);
}
static inline unsigned long _hc(const uint16_t i)
{
return ((unsigned long)i * (unsigned long)0x9e3779b1);
}

View File

@@ -41,7 +41,6 @@
#define ZT_IDENTITY_GEN_HASHCASH_FIRST_BYTE_LESS_THAN 17
#define ZT_IDENTITY_GEN_MEMORY 2097152
#define ZT_IDENTITY_GEN_SALSA20_ROUNDS 20
namespace ZeroTier {
@@ -55,8 +54,8 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
// ordinary Salsa20 is randomly seekable. This is good for a cipher
// but is not what we want for sequential memory-harndess.
memset(genmem,0,ZT_IDENTITY_GEN_MEMORY);
Salsa20 s20(digest,256,(char *)digest + 32,ZT_IDENTITY_GEN_SALSA20_ROUNDS);
s20.encrypt((char *)genmem,(char *)genmem,64);
Salsa20 s20(digest,256,(char *)digest + 32);
s20.encrypt20((char *)genmem,(char *)genmem,64);
for(unsigned long i=64;i<ZT_IDENTITY_GEN_MEMORY;i+=64) {
unsigned long k = i - 64;
*((uint64_t *)((char *)genmem + i)) = *((uint64_t *)((char *)genmem + k));
@@ -67,7 +66,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
*((uint64_t *)((char *)genmem + i + 40)) = *((uint64_t *)((char *)genmem + k + 40));
*((uint64_t *)((char *)genmem + i + 48)) = *((uint64_t *)((char *)genmem + k + 48));
*((uint64_t *)((char *)genmem + i + 56)) = *((uint64_t *)((char *)genmem + k + 56));
s20.encrypt((char *)genmem + i,(char *)genmem + i,64);
s20.encrypt20((char *)genmem + i,(char *)genmem + i,64);
}
// Render final digest using genmem as a lookup table
@@ -77,7 +76,7 @@ static inline void _computeMemoryHardHash(const void *publicKey,unsigned int pub
uint64_t tmp = ((uint64_t *)genmem)[idx2];
((uint64_t *)genmem)[idx2] = ((uint64_t *)digest)[idx1];
((uint64_t *)digest)[idx1] = tmp;
s20.encrypt(digest,digest,64);
s20.encrypt20(digest,digest,64);
}
}
@@ -159,7 +158,7 @@ bool Identity::fromString(const char *str)
return false;
char *saveptr = (char *)0;
char tmp[4096];
char tmp[1024];
if (!Utils::scopy(tmp,sizeof(tmp),str))
return false;

View File

@@ -38,8 +38,7 @@
#include "Address.hpp"
#include "C25519.hpp"
#include "Buffer.hpp"
#define ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)
#include "SHA512.hpp"
namespace ZeroTier {
@@ -93,8 +92,7 @@ public:
}
template<unsigned int C>
Identity(const Buffer<C> &b,unsigned int startAt = 0)
throw(std::out_of_range,std::invalid_argument) :
Identity(const Buffer<C> &b,unsigned int startAt = 0) :
_privateKey((C25519::Private *)0)
{
deserialize(b,startAt);
@@ -139,6 +137,21 @@ public:
*/
inline bool hasPrivate() const throw() { return (_privateKey != (C25519::Private *)0); }
/**
* Compute the SHA512 hash of our private key (if we have one)
*
* @param sha Buffer to receive SHA512 (MUST be ZT_SHA512_DIGEST_LEN (64) bytes in length)
* @return True on success, false if no private key
*/
inline bool sha512PrivateKey(void *sha) const
{
if (_privateKey) {
SHA512::hash(sha,_privateKey->data,ZT_C25519_PRIVATE_KEY_LEN);
return true;
}
return false;
}
/**
* Sign a message with this identity (private key required)
*

File diff suppressed because it is too large Load Diff

View File

@@ -93,30 +93,59 @@ 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
* @throws std::out_of_range Range error processing packet (should be discarded)
* @throws std::runtime_error Other error processing packet (should be discarded)
*/
bool tryDecode(const RuntimeEnvironment *RR);
bool tryDecode(const RuntimeEnvironment *RR,bool deferred);
/**
* @return Time of packet receipt / start of decode
*/
inline uint64_t receiveTime() const throw() { return _receiveTime; }
/**
* Compute the Salsa20/12+SHA512 proof of work function
*
* @param difficulty Difficulty in bits (max: 64)
* @param challenge Challenge string
* @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
* @param result Buffer to fill with 16-byte result
*/
static void computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16]);
/**
* Verify the result of Salsa20/12+SHA512 proof of work
*
* @param difficulty Difficulty in bits (max: 64)
* @param challenge Challenge bytes
* @param challengeLength Length of challenge in bytes (max allowed: ZT_PROTO_MAX_PACKET_LENGTH)
* @param proposedResult Result supplied by client
* @return True if result is valid
*/
static bool testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16]);
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);
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);
bool _doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
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_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
@@ -126,6 +155,7 @@ private:
bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
bool _doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
// 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);

View File

@@ -77,14 +77,12 @@ InetAddress::IpScope InetAddress::ipScope() const
if ((ip & 0xffff0000) == 0xc0a80000) return IP_SCOPE_PRIVATE; // 192.168.0.0/16
break;
case 0xff: return IP_SCOPE_NONE; // 255.0.0.0/8 (broadcast, or unused/unusable)
default:
switch(ip >> 28) {
case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4
case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable)
default: return IP_SCOPE_GLOBAL; // everything else
}
break;
}
switch(ip >> 28) {
case 0xe: return IP_SCOPE_MULTICAST; // 224.0.0.0/4
case 0xf: return IP_SCOPE_PSEUDOPRIVATE; // 240.0.0.0/4 ("reserved," usually unusable)
}
return IP_SCOPE_GLOBAL;
} break;
case AF_INET6: {
@@ -236,7 +234,6 @@ void InetAddress::fromString(const std::string &ipSlashPort)
}
InetAddress InetAddress::netmask() const
throw()
{
InetAddress r(*this);
switch(r.ss_family) {
@@ -244,36 +241,40 @@ InetAddress InetAddress::netmask() const
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
break;
case AF_INET6: {
unsigned char *bf = reinterpret_cast<unsigned char *>(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
signed int bitsLeft = (signed int)netmaskBits();
for(unsigned int i=0;i<16;++i) {
if (bitsLeft > 0) {
bf[i] |= (unsigned char)((bitsLeft >= 8) ? 0x00 : (0xff >> bitsLeft));
bitsLeft -= 8;
}
}
uint64_t nm[2];
const unsigned int bits = netmaskBits();
nm[0] = Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
nm[1] = Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;
}
InetAddress InetAddress::broadcast() const
throw()
{
if (ss_family == AF_INET) {
InetAddress r(*this);
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits()));
return r;
}
return InetAddress();
}
InetAddress InetAddress::network() const
{
InetAddress r(*this);
switch(r.ss_family) {
case AF_INET:
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffff >> netmaskBits()));
reinterpret_cast<struct sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffff << (32 - netmaskBits())));
break;
case AF_INET6: {
unsigned char *bf = reinterpret_cast<unsigned char *>(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
signed int bitsLeft = (signed int)netmaskBits();
for(unsigned int i=0;i<16;++i) {
if (bitsLeft > 0) {
bf[i] |= (unsigned char)((bitsLeft >= 8) ? 0x00 : (0xff >> bitsLeft));
bitsLeft -= 8;
}
}
uint64_t nm[2];
const unsigned int bits = netmaskBits();
memcpy(nm,reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
memcpy(reinterpret_cast<struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm,16);
} break;
}
return r;

View File

@@ -42,13 +42,18 @@
namespace ZeroTier {
/**
* Maximum integer value of enum IpScope
*/
#define ZT_INETADDRESS_MAX_SCOPE 7
/**
* Extends sockaddr_storage with friendly C++ methods
*
* This is basically a "mixin" for sockaddr_storage. It adds methods and
* operators, but does not modify the structure. This can be cast to/from
* sockaddr_storage and used interchangeably. Don't change this as it's
* used in a few places.
* sockaddr_storage and used interchangeably. DO NOT change this by e.g.
* adding non-static fields, since much code depends on this identity.
*/
struct InetAddress : public sockaddr_storage
{
@@ -66,7 +71,8 @@ struct InetAddress : public sockaddr_storage
* IP address scope
*
* Note that these values are in ascending order of path preference and
* MUST remain that way or Path must be changed to reflect.
* MUST remain that way or Path must be changed to reflect. Also be sure
* to change ZT_INETADDRESS_MAX_SCOPE if the max changes.
*/
enum IpScope
{
@@ -100,74 +106,88 @@ struct InetAddress : public sockaddr_storage
inline InetAddress &operator=(const InetAddress &a)
throw()
{
memcpy(this,&a,sizeof(InetAddress));
if (&a != this)
memcpy(this,&a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const InetAddress *a)
throw()
{
memcpy(this,a,sizeof(InetAddress));
if (a != this)
memcpy(this,a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage &ss)
throw()
{
memcpy(this,&ss,sizeof(InetAddress));
if (reinterpret_cast<const InetAddress *>(&ss) != this)
memcpy(this,&ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage *ss)
throw()
{
memcpy(this,ss,sizeof(InetAddress));
if (reinterpret_cast<const InetAddress *>(ss) != this)
memcpy(this,ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in &sa)
throw()
{
memset(this,0,sizeof(InetAddress));
memcpy(this,&sa,sizeof(struct sockaddr_in));
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
memcpy(this,&sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in *sa)
throw()
{
memset(this,0,sizeof(InetAddress));
memcpy(this,sa,sizeof(struct sockaddr_in));
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
memcpy(this,sa,sizeof(struct sockaddr_in));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
throw()
{
memset(this,0,sizeof(InetAddress));
memcpy(this,&sa,sizeof(struct sockaddr_in6));
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
memcpy(this,&sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
throw()
{
memset(this,0,sizeof(InetAddress));
memcpy(this,sa,sizeof(struct sockaddr_in6));
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
memcpy(this,sa,sizeof(struct sockaddr_in6));
}
return *this;
}
inline InetAddress &operator=(const struct sockaddr &sa)
throw()
{
memset(this,0,sizeof(InetAddress));
switch(sa.sa_family) {
case AF_INET:
memcpy(this,&sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
memcpy(this,&sa,sizeof(struct sockaddr_in6));
break;
if (reinterpret_cast<const InetAddress *>(&sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa.sa_family) {
case AF_INET:
memcpy(this,&sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
memcpy(this,&sa,sizeof(struct sockaddr_in6));
break;
}
}
return *this;
}
@@ -175,14 +195,16 @@ struct InetAddress : public sockaddr_storage
inline InetAddress &operator=(const struct sockaddr *sa)
throw()
{
memset(this,0,sizeof(InetAddress));
switch(sa->sa_family) {
case AF_INET:
memcpy(this,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
memcpy(this,sa,sizeof(struct sockaddr_in6));
break;
if (reinterpret_cast<const InetAddress *>(sa) != this) {
memset(this,0,sizeof(InetAddress));
switch(sa->sa_family) {
case AF_INET:
memcpy(this,sa,sizeof(struct sockaddr_in));
break;
case AF_INET6:
memcpy(this,sa,sizeof(struct sockaddr_in6));
break;
}
}
return *this;
}
@@ -281,17 +303,27 @@ struct InetAddress : public sockaddr_storage
/**
* Construct a full netmask as an InetAddress
*
* @return Netmask such as 255.255.255.0 if this address is /24 (port field will be unchanged)
*/
InetAddress netmask() const
throw();
InetAddress netmask() const;
/**
* Constructs a broadcast address from a network/netmask address
*
* This is only valid for IPv4 and will return a NULL InetAddress for other
* address families.
*
* @return Broadcast address (only IP portion is meaningful)
*/
InetAddress broadcast() const
throw();
InetAddress broadcast() const;
/**
* Return the network -- a.k.a. the IP ANDed with the netmask
*
* @return Network e.g. 10.0.1.0/24 from 10.0.1.200/24
*/
InetAddress network() const;
/**
* @return True if this is an IPv4 address
@@ -304,7 +336,7 @@ struct InetAddress : public sockaddr_storage
inline bool isV6() const throw() { return (ss_family == AF_INET6); }
/**
* @return pointer to raw IP address bytes
* @return pointer to raw address bytes or NULL if not available
*/
inline const void *rawIpData() const
throw()
@@ -317,27 +349,19 @@ struct InetAddress : public sockaddr_storage
}
/**
* @return pointer to raw IP address bytes
*/
inline void *rawIpData()
throw()
{
switch(ss_family) {
case AF_INET: return (void *)&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr);
case AF_INET6: return (void *)(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
default: return 0;
}
}
/**
* Performs an IP-only comparison or, if that is impossible, a memcmp()
*
* @param a InetAddress to compare again
* @return True if only IP portions are equal (false for non-IP or null addresses)
*/
inline bool ipsEqual(const InetAddress &a) const
{
switch(ss_family) {
case AF_INET: return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
case AF_INET6: return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
if (ss_family == a.ss_family) {
if (ss_family == AF_INET)
return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
if (ss_family == AF_INET6)
return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
return (memcmp(this,&a,sizeof(InetAddress)) == 0);
}
return false;
}
@@ -366,7 +390,8 @@ struct InetAddress : public sockaddr_storage
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
// Format is the same as in VERB_HELLO in Packet.hpp
// This is used in the protocol and must be the same as describe in places
// like VERB_HELLO in Packet.hpp.
switch(ss_family) {
case AF_INET:
b.append((uint8_t)0x04);
@@ -387,11 +412,21 @@ struct InetAddress : public sockaddr_storage
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
memset(this,0,sizeof(InetAddress));
unsigned int p = startAt;
switch(b[p++]) {
case 0:
return 1;
case 0x01:
// TODO: Ethernet address (but accept for forward compatibility)
return 7;
case 0x02:
// TODO: Bluetooth address (but accept for forward compatibility)
return 7;
case 0x03:
// TODO: Other address types (but accept for forward compatibility)
// These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc.
return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
case 0x04:
ss_family = AF_INET;
memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;

View File

@@ -37,6 +37,7 @@
#include "Peer.hpp"
#include "C25519.hpp"
#include "CertificateOfMembership.hpp"
#include "Node.hpp"
namespace ZeroTier {
@@ -77,7 +78,7 @@ void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &m
}
}
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const
unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const
{
unsigned char *p;
unsigned int added = 0,i,k,rptr,totalKnown = 0;
@@ -174,129 +175,134 @@ void Multicaster::send(
unsigned long idxbuf[8194];
unsigned long *indexes = idxbuf;
Mutex::Lock _l(_groups_m);
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
try {
Mutex::Lock _l(_groups_m);
MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
if (!gs.members.empty()) {
// Allocate a memory buffer if group is monstrous
if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long)))
indexes = new unsigned long[gs.members.size()];
if (!gs.members.empty()) {
// Allocate a memory buffer if group is monstrous
if (gs.members.size() > (sizeof(idxbuf) / sizeof(unsigned long)))
indexes = new unsigned long[gs.members.size()];
// Generate a random permutation of member indexes
for(unsigned long i=0;i<gs.members.size();++i)
indexes[i] = i;
for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--i) {
unsigned long j = (unsigned long)RR->node->prng() % (i + 1);
unsigned long tmp = indexes[j];
indexes[j] = indexes[i];
indexes[i] = tmp;
}
}
if (gs.members.size() >= limit) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out;
out.init(
RR,
now,
nwid,
com,
limit,
1, // we'll still gather a little from peers to keep multicast list fresh
src,
mg,
etherType,
data,
len);
unsigned int count = 0;
for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
if (*ast != RR->identity.address()) {
out.sendOnly(RR,*ast);
if (++count >= limit)
break;
// Generate a random permutation of member indexes
for(unsigned long i=0;i<gs.members.size();++i)
indexes[i] = i;
for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--i) {
unsigned long j = (unsigned long)RR->node->prng() % (i + 1);
unsigned long tmp = indexes[j];
indexes[j] = indexes[i];
indexes[i] = tmp;
}
}
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
out.sendOnly(RR,ma);
++count;
}
}
} else {
unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
if (gs.members.size() >= limit) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out;
if ((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY) {
gs.lastExplicitGather = now;
SharedPtr<Peer> sn(RR->topology->getBestRoot());
if (sn) {
TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
out.init(
RR,
now,
nwid,
com,
limit,
1, // we'll still gather a little from peers to keep multicast list fresh
src,
mg,
etherType,
data,
len);
const CertificateOfMembership *com = (CertificateOfMembership *)0;
SharedPtr<NetworkConfig> nconf;
if (sn->needsOurNetworkMembershipCertificate(nwid,now,true)) {
SharedPtr<Network> nw(RR->node->network(nwid));
if (nw) {
nconf = nw->config2();
if (nconf)
com = &(nconf->com());
}
unsigned int count = 0;
for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
if (*ast != RR->identity.address()) {
out.sendOnly(RR,*ast); // optimization: don't use dedup log if it's a one-pass send
if (++count >= limit)
break;
}
Packet outp(sn->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
outp.append(nwid);
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);
outp.armor(sn->key(),true);
sn->send(RR,outp.data(),outp.size(),now);
}
gatherLimit = 0;
}
gs.txQueue.push_back(OutboundMulticast());
OutboundMulticast &out = gs.txQueue.back();
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
out.sendOnly(RR,ma); // optimization: don't use dedup log if it's a one-pass send
++count;
}
}
} else {
unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
out.init(
RR,
now,
nwid,
com,
limit,
gatherLimit,
src,
mg,
etherType,
data,
len);
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());
unsigned int count = 0;
const CertificateOfMembership *com = (CertificateOfMembership *)0;
{
SharedPtr<Network> nw(RR->node->network(nwid));
if (nw) {
SharedPtr<NetworkConfig> nconf(nw->config2());
if ((nconf)&&(nconf->com())&&(nconf->isPrivate())&&(p->needsOurNetworkMembershipCertificate(nwid,now,true)))
com = &(nconf->com());
}
}
for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
if (*ast != RR->identity.address()) {
out.sendAndLog(RR,*ast);
if (++count >= limit)
break;
Packet outp(p->address(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
outp.append(nwid);
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->sw->send(outp,true,0);
}
gatherLimit = 0;
}
gs.txQueue.push_back(OutboundMulticast());
OutboundMulticast &out = gs.txQueue.back();
out.init(
RR,
now,
nwid,
com,
limit,
gatherLimit,
src,
mg,
etherType,
data,
len);
unsigned int count = 0;
for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
if (*ast != RR->identity.address()) {
out.sendAndLog(RR,*ast);
if (++count >= limit)
break;
}
}
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
out.sendAndLog(RR,ma);
++count;
}
}
}
unsigned long idx = 0;
while ((count < limit)&&(idx < gs.members.size())) {
Address ma(gs.members[indexes[idx++]].address);
if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
out.sendAndLog(RR,ma);
++count;
}
}
}
} catch ( ... ) {} // this is a sanity check to catch any failures and make sure indexes[] still gets deleted
// Free allocated memory buffer if any
if (indexes != idxbuf)

View File

@@ -146,7 +146,7 @@ public:
* @return Number of addresses appended
* @throws std::out_of_range Buffer overflow writing to packet
*/
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const;
unsigned int gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &appendTo,unsigned int limit) const;
/**
* Get subscribers to a multicast group

View File

@@ -37,6 +37,7 @@
#include "Packet.hpp"
#include "Buffer.hpp"
#include "NetworkController.hpp"
#include "Node.hpp"
#include "../version.h"
@@ -144,7 +145,15 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg)
bool Network::tryAnnounceMulticastGroupsTo(const SharedPtr<Peer> &peer)
{
Mutex::Lock _l(_lock);
return _tryAnnounceMulticastGroupsTo(RR->topology->rootAddresses(),_allMulticastGroups(),peer,RR->node->now());
if (
(_isAllowed(peer)) ||
(peer->address() == this->controller()) ||
(RR->topology->isRoot(peer->identity()))
) {
_announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
return true;
}
return false;
}
bool Network::applyConfiguration(const SharedPtr<NetworkConfig> &conf)
@@ -400,79 +409,80 @@ bool Network::_isAllowed(const SharedPtr<Peer> &peer) const
return false; // default position on any failure
}
bool Network::_tryAnnounceMulticastGroupsTo(const std::vector<Address> &alwaysAddresses,const std::vector<MulticastGroup> &allMulticastGroups,const SharedPtr<Peer> &peer,uint64_t now) const
{
// assumes _lock is locked
if (
(_isAllowed(peer)) ||
(peer->address() == this->controller()) ||
(std::find(alwaysAddresses.begin(),alwaysAddresses.end(),peer->address()) != alwaysAddresses.end())
) {
if ((_config)&&(_config->com())&&(!_config->isPublic())&&(peer->needsOurNetworkMembershipCertificate(_id,now,true))) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
_config->com().serialize(outp);
outp.armor(peer->key(),true);
peer->send(RR,outp.data(),outp.size(),now);
}
{
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) {
outp.armor(peer->key(),true);
peer->send(RR,outp.data(),outp.size(),now);
outp.reset(peer->address(),RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
}
// network ID, MAC, ADI
outp.append((uint64_t)_id);
mg->mac().appendTo(outp);
outp.append((uint32_t)mg->adi());
}
if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) {
outp.armor(peer->key(),true);
peer->send(RR,outp.data(),outp.size(),now);
}
}
return true;
}
return false;
}
class _AnnounceMulticastGroupsToAll
class _GetPeersThatNeedMulticastAnnouncement
{
public:
_AnnounceMulticastGroupsToAll(const RuntimeEnvironment *renv,Network *nw) :
_GetPeersThatNeedMulticastAnnouncement(const RuntimeEnvironment *renv,Network *nw) :
_now(renv->node->now()),
RR(renv),
_controller(nw->controller()),
_network(nw),
_rootAddresses(renv->topology->rootAddresses()),
_allMulticastGroups(nw->_allMulticastGroups())
_rootAddresses(renv->topology->rootAddresses())
{}
inline void operator()(Topology &t,const SharedPtr<Peer> &p) { _network->_tryAnnounceMulticastGroupsTo(_rootAddresses,_allMulticastGroups,p,_now); }
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
if (
(_network->_isAllowed(p)) ||
(p->address() == _controller) ||
(std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end())
) {
peers.push_back(p->address());
}
}
std::vector<Address> peers;
private:
uint64_t _now;
const RuntimeEnvironment *RR;
Address _controller;
Network *_network;
std::vector<Address> _rootAddresses;
std::vector<MulticastGroup> _allMulticastGroups;
};
void Network::_announceMulticastGroups()
{
// Assumes _lock is locked
_AnnounceMulticastGroupsToAll afunc(RR,this);
RR->topology->eachPeer<_AnnounceMulticastGroupsToAll &>(afunc);
_GetPeersThatNeedMulticastAnnouncement gpfunc(RR,this);
RR->topology->eachPeer<_GetPeersThatNeedMulticastAnnouncement &>(gpfunc);
std::vector<MulticastGroup> allMulticastGroups(_allMulticastGroups());
for(std::vector<Address>::const_iterator pa(gpfunc.peers.begin());pa!=gpfunc.peers.end();++pa)
_announceMulticastGroupsTo(*pa,allMulticastGroups);
}
void Network::_announceMulticastGroupsTo(const Address &peerAddress,const std::vector<MulticastGroup> &allMulticastGroups) const
{
// Assumes _lock is locked
// We push COMs ahead of MULTICAST_LIKE since they're used for access control -- a COM is a public
// credential so "over-sharing" isn't really an issue (and we only do so with roots).
if ((_config)&&(_config->com())&&(!_config->isPublic())) {
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
_config->com().serialize(outp);
RR->sw->send(outp,true,0);
}
{
Packet outp(peerAddress,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
if ((outp.size() + 18) >= ZT_UDP_DEFAULT_PAYLOAD_MTU) {
RR->sw->send(outp,true,0);
outp.reset(peerAddress,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
}
// network ID, MAC, ADI
outp.append((uint64_t)_id);
mg->mac().appendTo(outp);
outp.append((uint32_t)mg->adi());
}
if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH)
RR->sw->send(outp,true,0);
}
}
std::vector<MulticastGroup> Network::_allMulticastGroups() const
{
// Assumes _lock is locked
std::vector<MulticastGroup> mgs;
mgs.reserve(_myMulticastGroups.size() + _multicastGroupsBehindMe.size() + 1);
mgs.insert(mgs.end(),_myMulticastGroups.begin(),_myMulticastGroups.end());
@@ -481,6 +491,7 @@ std::vector<MulticastGroup> Network::_allMulticastGroups() const
mgs.push_back(Network::BROADCAST);
std::sort(mgs.begin(),mgs.end());
mgs.erase(std::unique(mgs.begin(),mgs.end()),mgs.end());
return mgs;
}

View File

@@ -56,7 +56,7 @@ namespace ZeroTier {
class RuntimeEnvironment;
class Peer;
class _AnnounceMulticastGroupsToAll; // internal function object in Network.cpp
class _GetPeersThatNeedMulticastAnnouncement;
/**
* A virtual LAN
@@ -64,7 +64,7 @@ class _AnnounceMulticastGroupsToAll; // internal function object in Network.cpp
class Network : NonCopyable
{
friend class SharedPtr<Network>;
friend class _AnnounceMulticastGroupsToAll;
friend class _GetPeersThatNeedMulticastAnnouncement; // internal function object
public:
/**
@@ -344,6 +344,7 @@ private:
bool _isAllowed(const SharedPtr<Peer> &peer) const;
bool _tryAnnounceMulticastGroupsTo(const std::vector<Address> &rootAddresses,const std::vector<MulticastGroup> &allMulticastGroups,const SharedPtr<Peer> &peer,uint64_t now) const;
void _announceMulticastGroups();
void _announceMulticastGroupsTo(const Address &peerAddress,const std::vector<MulticastGroup> &allMulticastGroups) const;
std::vector<MulticastGroup> _allMulticastGroups() const;
const RuntimeEnvironment *RR;

View File

@@ -55,6 +55,9 @@ SharedPtr<NetworkConfig> NetworkConfig::createTestNetworkConfig(const Address &s
if ((ip & 0x000000ff) == 0x00000000) ip ^= 0x00000001; // or .0
nc->_staticIps.push_back(InetAddress(Utils::hton(ip),8));
// Assign an RFC4193-compliant IPv6 address -- will never collide
nc->_staticIps.push_back(InetAddress::makeIpv6rfc4193(ZT_TEST_NETWORK_ID,self.toInt()));
return nc;
}

View File

@@ -46,7 +46,8 @@
#include "Address.hpp"
#include "Identity.hpp"
#include "SelfAwareness.hpp"
#include "Defaults.hpp"
#include "Cluster.hpp"
#include "DeferredPackets.hpp"
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
@@ -64,8 +65,7 @@ Node::Node(
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_EventCallback eventCallback,
const char *overrideRootTopology) :
ZT_EventCallback eventCallback) :
_RR(this),
RR(&_RR),
_uPtr(uptr),
@@ -82,35 +82,33 @@ Node::Node(
_lastPingCheck(0),
_lastHousekeepingRun(0)
{
_newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR;
_newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR;
_newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION;
_online = false;
// Use Salsa20 alone as a high-quality non-crypto PRNG
{
char foo[32];
Utils::getSecureRandom(foo,32);
_prng.init(foo,256,foo,8);
_prng.init(foo,256,foo);
memset(_prngStream,0,sizeof(_prngStream));
_prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream));
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
}
std::string idtmp(dataStoreGet("identity.secret"));
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
TRACE("identity.secret not found, generating...");
RR->identity.generate();
idtmp = RR->identity.toString(true);
if (!dataStorePut("identity.secret",idtmp,true))
throw std::runtime_error("unable to write identity.secret");
}
RR->publicIdentityStr = RR->identity.toString(false);
RR->secretIdentityStr = RR->identity.toString(true);
idtmp = dataStoreGet("identity.public");
if (idtmp != RR->publicIdentityStr) {
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
throw std::runtime_error("unable to write identity.public");
{
std::string idtmp(dataStoreGet("identity.secret"));
if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
TRACE("identity.secret not found, generating...");
RR->identity.generate();
idtmp = RR->identity.toString(true);
if (!dataStorePut("identity.secret",idtmp,true))
throw std::runtime_error("unable to write identity.secret");
}
RR->publicIdentityStr = RR->identity.toString(false);
RR->secretIdentityStr = RR->identity.toString(true);
idtmp = dataStoreGet("identity.public");
if (idtmp != RR->publicIdentityStr) {
if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
throw std::runtime_error("unable to write identity.public");
}
}
try {
@@ -119,7 +117,9 @@ Node::Node(
RR->antiRec = new AntiRecursion();
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->antiRec;
@@ -128,33 +128,25 @@ Node::Node(
throw;
}
Dictionary rt;
if (overrideRootTopology) {
rt.fromString(std::string(overrideRootTopology));
} else {
std::string rttmp(dataStoreGet("root-topology"));
if (rttmp.length() > 0) {
rt.fromString(rttmp);
if (!Topology::authenticateRootTopology(rt))
rt.clear();
}
if ((!rt.size())||(!rt.contains("rootservers")))
rt.fromString(ZT_DEFAULTS.defaultRootTopology);
}
RR->topology->setRootServers(Dictionary(rt.get("rootservers","")));
postEvent(ZT_EVENT_UP);
}
Node::~Node()
{
Mutex::Lock _l(_networks_m);
_networks.clear(); // ensure that networks are destroyed before shutdown
_networks.clear(); // ensure that networks are destroyed before shutdow
RR->dpEnabled = 0;
delete RR->dp;
delete RR->sa;
delete RR->topology;
delete RR->antiRec;
delete RR->mc;
delete RR->sw;
#ifdef ZT_ENABLE_CLUSTER
delete RR->cluster;
#endif
}
ZT_ResultCode Node::processWirePacket(
@@ -197,29 +189,91 @@ public:
RR(renv),
_now(now),
_relays(relays),
_rootAddresses(RR->topology->rootAddresses())
_world(RR->topology->world())
{
}
uint64_t lastReceiveFromUpstream;
uint64_t lastReceiveFromUpstream; // tracks last time we got a packet from an 'upstream' peer like a root or a relay
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
bool isRelay = false;
for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(_relays.begin());r!=_relays.end();++r) {
if (r->first == p->address()) {
isRelay = true;
bool upstream = false;
InetAddress stableEndpoint4,stableEndpoint6;
// If this is a world root, pick (if possible) both an IPv4 and an IPv6 stable endpoint to use if link isn't currently alive.
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
if (r->identity.address() == p->address()) {
upstream = true;
for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)r->stableEndpoints.size();++k) {
const InetAddress &addr = r->stableEndpoints[ptr++ % r->stableEndpoints.size()];
if (!stableEndpoint4) {
if (addr.ss_family == AF_INET)
stableEndpoint4 = addr;
}
if (!stableEndpoint6) {
if (addr.ss_family == AF_INET6)
stableEndpoint6 = addr;
}
}
break;
}
}
if ((isRelay)||(std::find(_rootAddresses.begin(),_rootAddresses.end(),p->address()) != _rootAddresses.end())) {
p->doPingAndKeepalive(RR,_now);
if (p->lastReceive() > lastReceiveFromUpstream)
lastReceiveFromUpstream = p->lastReceive();
} else {
if (p->alive(_now))
p->doPingAndKeepalive(RR,_now);
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< std::pair<Address,InetAddress> >::const_iterator r(_relays.begin());r!=_relays.end();++r) {
if (r->first == p->address()) {
if (r->second.ss_family == AF_INET)
stableEndpoint4 = r->second;
else if (r->second.ss_family == AF_INET6)
stableEndpoint6 = r->second;
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.
bool needToContactIndirect = true;
if (p->doPingAndKeepalive(RR,_now,AF_INET)) {
needToContactIndirect = false;
} else {
if (stableEndpoint4) {
needToContactIndirect = false;
p->sendHELLO(RR,InetAddress(),stableEndpoint4,_now);
}
}
if (p->doPingAndKeepalive(RR,_now,AF_INET6)) {
needToContactIndirect = false;
} else {
if (stableEndpoint6) {
needToContactIndirect = false;
p->sendHELLO(RR,InetAddress(),stableEndpoint6,_now);
}
}
if (needToContactIndirect) {
// If this is an upstream and we have no stable endpoint for either IPv4 or IPv6,
// send a NOP indirectly if possible to see if we can get to this peer in any
// 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,0);
}
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
} else if (p->activelyTransferringFrames(_now)) {
// Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
p->doPingAndKeepalive(RR,_now,0);
}
}
@@ -227,7 +281,7 @@ private:
const RuntimeEnvironment *RR;
uint64_t _now;
const std::vector< std::pair<Address,InetAddress> > &_relays;
std::vector<Address> _rootAddresses;
World _world;
};
ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
@@ -259,24 +313,13 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
(*n)->requestConfiguration();
// Attempt to contact network preferred relays that we don't have direct links to
std::sort(networkRelays.begin(),networkRelays.end());
networkRelays.erase(std::unique(networkRelays.begin(),networkRelays.end()),networkRelays.end());
for(std::vector< std::pair<Address,InetAddress> >::const_iterator nr(networkRelays.begin());nr!=networkRelays.end();++nr) {
if (nr->second) {
SharedPtr<Peer> rp(RR->topology->getPeer(nr->first));
if ((rp)&&(!rp->hasActiveDirectPath(now)))
rp->attemptToContactAt(RR,InetAddress(),nr->second,now);
}
}
// Ping living or root server/relay peers
// Do pings and keepalives
_PingPeersThatNeedPing pfunc(RR,now,networkRelays);
RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
// Update online status, post status change as event
bool oldOnline = _online;
_online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT);
const bool oldOnline = _online;
_online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
if (oldOnline != _online)
postEvent(_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
} catch ( ... ) {
@@ -298,7 +341,18 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
}
try {
*nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
#ifdef ZT_ENABLE_CLUSTER
// If clustering is enabled we have to call cluster->doPeriodicTasks() very often, so we override normal timer deadline behavior
if (RR->cluster) {
RR->sw->doTimerTasks(now);
RR->cluster->doPeriodicTasks();
*nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate
} else {
#endif
*nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
#ifdef ZT_ENABLE_CLUSTER
}
#endif
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
@@ -355,6 +409,8 @@ uint64_t Node::address() const
void Node::status(ZT_NodeStatus *status) const
{
status->address = RR->identity.address().toInt();
status->worldId = RR->topology->worldId();
status->worldTimestamp = RR->topology->worldTimestamp();
status->publicIdentity = RR->publicIdentityStr.c_str();
status->secretIdentity = RR->secretIdentityStr.c_str();
status->online = _online ? 1 : 0;
@@ -389,14 +445,13 @@ 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<RemotePath> paths(pi->second->paths());
RemotePath *bestPath = pi->second->getBestPath(_now);
std::vector<Path> paths(pi->second->paths());
Path *bestPath = pi->second->getBestPath(_now);
p->pathCount = 0;
for(std::vector<RemotePath>::iterator path(paths.begin());path!=paths.end();++path) {
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].fixed = path->fixed() ? 1 : 0;
p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
++p->pathCount;
@@ -441,11 +496,11 @@ void Node::freeQueryResult(void *qr)
::free(qr);
}
int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust)
int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr)
{
if (Path::isAddressValidForPath(*(reinterpret_cast<const InetAddress *>(addr)))) {
Mutex::Lock _l(_directPaths_m);
_directPaths.push_back(Path(*(reinterpret_cast<const InetAddress *>(addr)),metric,(Path::Trust)trust));
_directPaths.push_back(*(reinterpret_cast<const InetAddress *>(addr)));
std::sort(_directPaths.begin(),_directPaths.end());
_directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end());
return 1;
@@ -482,7 +537,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)
outp.append((uint16_t)0);
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const char *>(outp.data()) + ZT_PACKET_IDX_PAYLOAD,outp.size() - ZT_PACKET_IDX_PAYLOAD));
outp.append((uint16_t)sig.size());
outp.append(sig.data,sig.size());
outp.append(sig.data,(unsigned int)sig.size());
outp.append((uint16_t)0); // originator doesn't need an extra credential, since it's the originator
for(unsigned int h=1;h<test->hopCount;++h) {
outp.append((uint8_t)0);
@@ -494,7 +549,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,test->credentialNetworkId);
RR->sw->send(outp,true,0);
}
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
@@ -522,13 +577,93 @@ void Node::circuitTestEnd(ZT_CircuitTest *test)
}
}
ZT_ResultCode Node::clusterInit(
unsigned int myId,
const struct sockaddr_storage *zeroTierPhysicalEndpoints,
unsigned int numZeroTierPhysicalEndpoints,
int x,
int y,
int z,
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
void *sendFunctionArg,
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
void *addressToLocationFunctionArg)
{
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster)
return ZT_RESULT_ERROR_BAD_PARAMETER;
std::vector<InetAddress> eps;
for(unsigned int i=0;i<numZeroTierPhysicalEndpoints;++i)
eps.push_back(InetAddress(zeroTierPhysicalEndpoints[i]));
std::sort(eps.begin(),eps.end());
RR->cluster = new Cluster(RR,myId,eps,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
return ZT_RESULT_OK;
#else
return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
#endif
}
ZT_ResultCode Node::clusterAddMember(unsigned int memberId)
{
#ifdef ZT_ENABLE_CLUSTER
if (!RR->cluster)
return ZT_RESULT_ERROR_BAD_PARAMETER;
RR->cluster->addMember((uint16_t)memberId);
return ZT_RESULT_OK;
#else
return ZT_RESULT_ERROR_UNSUPPORTED_OPERATION;
#endif
}
void Node::clusterRemoveMember(unsigned int memberId)
{
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster)
RR->cluster->removeMember((uint16_t)memberId);
#endif
}
void Node::clusterHandleIncomingMessage(const void *msg,unsigned int len)
{
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster)
RR->cluster->handleIncomingStateMessage(msg,len);
#endif
}
void Node::clusterStatus(ZT_ClusterStatus *cs)
{
if (!cs)
return;
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster)
RR->cluster->status(*cs);
else
#endif
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/ */
/****************************************************************************/
std::string Node::dataStoreGet(const char *name)
{
char buf[16384];
char buf[1024];
std::string r;
unsigned long olen = 0;
do {
@@ -540,16 +675,6 @@ std::string Node::dataStoreGet(const char *name)
return r;
}
void Node::postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev)
{
if (Utils::compareVersion(major,minor,rev,_newestVersionSeen[0],_newestVersionSeen[1],_newestVersionSeen[2]) > 0) {
_newestVersionSeen[0] = major;
_newestVersionSeen[1] = minor;
_newestVersionSeen[2] = rev;
this->postEvent(ZT_EVENT_SAW_MORE_RECENT_VERSION,(const void *)_newestVersionSeen);
}
}
#ifdef ZT_TRACE
void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
{
@@ -587,7 +712,7 @@ uint64_t Node::prng()
{
unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
if (!p)
_prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream));
_prng.encrypt12(_prngStream,_prngStream,sizeof(_prngStream));
return _prngStream[p];
}
@@ -622,12 +747,11 @@ enum ZT_ResultCode ZT_Node_new(
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_EventCallback eventCallback,
const char *overrideRootTopology)
ZT_EventCallback eventCallback)
{
*node = (ZT_Node *)0;
try {
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback,overrideRootTopology));
*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback));
return ZT_RESULT_OK;
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
@@ -659,8 +783,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
} catch ( ... ) {
reinterpret_cast<ZeroTier::Node *>(node)->postEvent(ZT_EVENT_INVALID_PACKET,(const void *)remoteAddress);
return ZT_RESULT_OK;
return ZT_RESULT_OK; // "OK" since invalid packets are simply dropped, but the system is still up
}
}
@@ -786,6 +909,22 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr)
} catch ( ... ) {}
}
int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr);
} catch ( ... ) {
return 0;
}
}
void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->clearLocalInterfaceAddresses();
} catch ( ... ) {}
}
void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
{
try {
@@ -793,7 +932,7 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
} catch ( ... ) {}
}
ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(test,reportCallback);
@@ -809,19 +948,60 @@ void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test)
} catch ( ... ) {}
}
int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust)
enum ZT_ResultCode ZT_Node_clusterInit(
ZT_Node *node,
unsigned int myId,
const struct sockaddr_storage *zeroTierPhysicalEndpoints,
unsigned int numZeroTierPhysicalEndpoints,
int x,
int y,
int z,
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
void *sendFunctionArg,
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
void *addressToLocationFunctionArg)
{
try {
return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust);
return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
} catch ( ... ) {
return 0;
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->clearLocalInterfaceAddresses();
return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId);
} catch ( ... ) {
return ZT_RESULT_FATAL_ERROR_INTERNAL;
}
}
void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId);
} catch ( ... ) {}
}
void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len);
} catch ( ... ) {}
}
void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs);
} catch ( ... ) {}
}
void ZT_Node_backgroundThreadMain(ZT_Node *node)
{
try {
reinterpret_cast<ZeroTier::Node *>(node)->backgroundThreadMain();
} catch ( ... ) {}
}

View File

@@ -71,8 +71,7 @@ public:
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
ZT_EventCallback eventCallback,
const char *overrideRootTopology);
ZT_EventCallback eventCallback);
~Node();
@@ -106,14 +105,38 @@ public:
ZT_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
ZT_VirtualNetworkList *networks() const;
void freeQueryResult(void *qr);
int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust);
int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
void clearLocalInterfaceAddresses();
void setNetconfMaster(void *networkControllerInstance);
ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
void circuitTestEnd(ZT_CircuitTest *test);
ZT_ResultCode clusterInit(
unsigned int myId,
const struct sockaddr_storage *zeroTierPhysicalEndpoints,
unsigned int numZeroTierPhysicalEndpoints,
int x,
int y,
int z,
void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
void *sendFunctionArg,
int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
void *addressToLocationFunctionArg);
ZT_ResultCode clusterAddMember(unsigned int memberId);
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()
*/
@@ -126,9 +149,10 @@ public:
* @param addr Destination address
* @param data Packet data
* @param len Packet length
* @param ttl Desired TTL (default: 0 for unchanged/default TTL)
* @return True if packet appears to have been sent
*/
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len)
inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
{
return (_wirePacketSendFunction(
reinterpret_cast<ZT_Node *>(this),
@@ -136,7 +160,8 @@ public:
reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
reinterpret_cast<const struct sockaddr_storage *>(&addr),
data,
len) == 0);
len,
ttl) == 0);
}
/**
@@ -193,7 +218,7 @@ public:
/**
* @return Potential direct paths to me a.k.a. local interface addresses
*/
inline std::vector<Path> directPaths() const
inline std::vector<InetAddress> directPaths() const
{
Mutex::Lock _l(_directPaths_m);
return _directPaths;
@@ -226,11 +251,6 @@ public:
*/
inline bool online() const throw() { return _online; }
/**
* If this version is newer than the newest we've seen, post a new version seen event
*/
void postNewerVersionIfNewer(unsigned int major,unsigned int minor,unsigned int rev);
#ifdef ZT_TRACE
void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif
@@ -276,7 +296,7 @@ private:
std::vector< ZT_CircuitTest * > _circuitTests;
Mutex _circuitTests_m;
std::vector<Path> _directPaths;
std::vector<InetAddress> _directPaths;
Mutex _directPaths_m;
Mutex _backgroundTasksLock;
@@ -288,7 +308,6 @@ private:
uint64_t _now;
uint64_t _lastPingCheck;
uint64_t _lastHousekeepingRun;
unsigned int _newestVersionSeen[3]; // major, minor, revision
bool _online;
};

View File

@@ -45,7 +45,7 @@ const char *Packet::verbString(Verb v)
case VERB_RENDEZVOUS: return "RENDEZVOUS";
case VERB_FRAME: return "FRAME";
case VERB_EXT_FRAME: return "EXT_FRAME";
case VERB_P5_MULTICAST_FRAME: return "P5_MULTICAST_FRAME";
case VERB_ECHO: return "ECHO";
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
@@ -56,6 +56,7 @@ const char *Packet::verbString(Verb v)
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";
}
return "(unknown)";
}
@@ -91,14 +92,14 @@ void Packet::armor(const void *key,bool encryptPayload)
setCipher(encryptPayload ? ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 : ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE);
_salsa20MangleKey((const unsigned char *)key,mangledKey);
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS);
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
// MAC key is always the first 32 bytes of the Salsa20 key stream
// This is the same construction DJB's NaCl library uses
s20.encrypt(ZERO_KEY,macKey,sizeof(macKey));
s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
if (encryptPayload)
s20.encrypt(payload,payload,payloadLen);
s20.encrypt12(payload,payload,payloadLen);
Poly1305::compute(mac,payload,payloadLen,macKey);
memcpy(field(ZT_PACKET_IDX_MAC,8),mac,8);
@@ -115,15 +116,15 @@ bool Packet::dearmor(const void *key)
if ((cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)||(cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)) {
_salsa20MangleKey((const unsigned char *)key,mangledKey);
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8),ZT_PROTO_SALSA20_ROUNDS);
Salsa20 s20(mangledKey,256,field(ZT_PACKET_IDX_IV,8)/*,ZT_PROTO_SALSA20_ROUNDS*/);
s20.encrypt(ZERO_KEY,macKey,sizeof(macKey));
s20.encrypt12(ZERO_KEY,macKey,sizeof(macKey));
Poly1305::compute(mac,payload,payloadLen,macKey);
if (!Utils::secureEq(mac,field(ZT_PACKET_IDX_MAC,8),8))
return false;
if (cs == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
s20.decrypt(payload,payload,payloadLen);
s20.decrypt12(payload,payload,payloadLen);
return true;
} else return false; // unrecognized cipher suite

View File

@@ -46,22 +46,24 @@
#include "../ext/lz4/lz4.h"
/**
* Protocol version -- incremented only for MAJOR changes
* Protocol version -- incremented only for major changes
*
* 1 - 0.2.0 ... 0.2.5
* 2 - 0.3.0 ... 0.4.5
* * Added signature and originating peer to multicast frame
* * Double size of multicast frame bloom filter
* + Added signature and originating peer to multicast frame
* + Double size of multicast frame bloom filter
* 3 - 0.5.0 ... 0.6.0
* * Yet another multicast redesign
* * New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... CURRENT
* * New identity format based on hashcash design
*
* This isn't going to change again for a long time unless your
* author wakes up again at 4am with another great idea. :P
* + Yet another multicast redesign
* + New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... 1.0.6
* + 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
*/
#define ZT_PROTO_VERSION 4
#define ZT_PROTO_VERSION 5
/**
* Minimum supported protocol version
@@ -233,15 +235,6 @@
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
// Destination address types from HELLO, OK(HELLO), and other message types
#define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0
#define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused
#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 2 // future use
#define ZT_PROTO_DEST_ADDRESS_TYPE_BLUETOOTH 3 // future use
#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4
#define ZT_PROTO_DEST_ADDRESS_TYPE_LTE_DIRECT 5 // future use
#define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6
// Ephemeral key record flags
#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 // future use
@@ -329,8 +322,6 @@
#define ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
#define ZT_PROTO_VERB_WHOIS__ERROR__IDX_ZTADDRESS (ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID (ZT_PROTO_VERB_OK_IDX_PAYLOAD)
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID + 8)
#define ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT (ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN + 2)
@@ -354,11 +345,11 @@ namespace ZeroTier {
* ZeroTier packet
*
* Packet format:
* <[8] random initialization vector (doubles as 64-bit packet ID)>
* <[8] 64-bit random packet ID and crypto initialization vector>
* <[5] destination ZT address>
* <[5] source ZT address>
* <[1] flags/cipher (top 5 bits) and ZT hop count (last 3 bits)>
* <[8] 8-bit MAC (currently first 8 bytes of poly1305 tag)>
* <[8] 64-bit MAC>
* [... -- begin encryption envelope -- ...]
* <[1] encrypted flags (top 3 bits) and verb (last 5 bits)>
* [... verb-specific payload ...]
@@ -374,6 +365,10 @@ namespace ZeroTier {
* immutable. This is because intermediate nodes can increment the hop
* count up to 7 (protocol max).
*
* A hop count of 7 also indicates that receiving peers should not attempt
* to learn direct paths from this packet. (Right now direct paths are only
* learned from direct packets anyway.)
*
* http://tonyarcieri.com/all-the-crypto-code-youve-ever-written-is-probably-broken
*
* For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
@@ -530,10 +525,13 @@ public:
*/
enum Verb /* Max value: 32 (5 bits) */
{
/* No operation, payload ignored, no reply */
/**
* No operation (ignored, no reply)
*/
VERB_NOP = 0,
/* Announcement of a node's existence:
/**
* Announcement of a node's existence:
* <[1] protocol version>
* <[1] software major version>
* <[1] software minor version>
@@ -542,6 +540,8 @@ public:
* <[...] binary serialized identity (see Identity)>
* <[1] destination address type>
* [<[...] destination address>]
* <[8] 64-bit world ID of current world>
* <[8] 64-bit timestamp of current world>
*
* This is the only message that ever must be sent in the clear, since it
* is used to push an identity to a new peer.
@@ -566,12 +566,15 @@ public:
* <[2] software revision (of responder)>
* <[1] destination address type (for this OK, not copied from HELLO)>
* [<[...] destination address>]
* <[2] 16-bit length of world update or 0 if none>
* [[...] world update]
*
* ERROR has no payload.
*/
VERB_HELLO = 1,
/* Error response:
/**
* Error response:
* <[1] in-re verb>
* <[8] in-re packet ID>
* <[1] error code>
@@ -579,25 +582,31 @@ public:
*/
VERB_ERROR = 2,
/* Success response:
/**
* Success response:
* <[1] in-re verb>
* <[8] in-re packet ID>
* <[...] request-specific payload>
*/
VERB_OK = 3,
/* Query an identity by address:
/**
* Query an identity by address:
* <[5] address to look up>
*
* OK response payload:
* <[...] binary serialized identity>
*
* ERROR response payload:
* <[5] address>
* If querying a cluster, duplicate OK responses may occasionally occur.
* These should be discarded.
*
* 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 = 4,
/* Meet another node at a given protocol address:
/**
* 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>
@@ -616,11 +625,16 @@ public:
* 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 = 5,
/* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
/**
* ZT-to-ZT unicast ethernet frame (shortened EXT_FRAME):
* <[8] 64-bit network ID>
* <[2] 16-bit ethertype>
* <[...] ethernet payload>
@@ -635,7 +649,8 @@ public:
*/
VERB_FRAME = 6,
/* Full Ethernet frame with MAC addressing and optional fields:
/**
* Full Ethernet frame with MAC addressing and optional fields:
* <[8] 64-bit network ID>
* <[1] flags>
* [<[...] certificate of network membership>]
@@ -658,23 +673,44 @@ public:
*/
VERB_EXT_FRAME = 7,
/* DEPRECATED */
VERB_P5_MULTICAST_FRAME = 8,
/**
* ECHO request (a.k.a. ping):
* <[...] arbitrary payload to be echoed back>
*
* This generates OK with a copy of the transmitted payload. No ERROR
* is generated. Response to ECHO requests is optional.
*
* Support for fragmented echo packets is optional and their use is not
* recommended.
*/
VERB_ECHO = 8,
/* Announce interest in multicast group(s):
/**
* Announce interest in multicast group(s):
* <[8] 64-bit network ID>
* <[6] multicast Ethernet address>
* <[4] multicast additional distinguishing information (ADI)>
* [... additional tuples of network/address/adi ...]
*
* LIKEs are sent to peers with whom you have a direct peer to peer
* connection, and always including root servers.
* LIKEs may be sent to any peer, though a good implementation should
* restrict them to peers on the same network they're for and to network
* controllers and root servers. In the current network, root servers
* will provide the service of final multicast cache.
*
* 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 = 9,
/* Network member certificate replication/push:
/**
* Network member certificate replication/push:
* <[...] serialized certificate of membership>
* [ ... additional certificates may follow ...]
*
@@ -685,7 +721,8 @@ public:
*/
VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10,
/* Network configuration request:
/**
* Network configuration request:
* <[8] 64-bit network ID>
* <[2] 16-bit length of request meta-data dictionary>
* <[...] string-serialized request meta-data>
@@ -720,7 +757,8 @@ public:
*/
VERB_NETWORK_CONFIG_REQUEST = 11,
/* Network configuration refresh request:
/**
* Network configuration refresh request:
* <[...] array of 64-bit network IDs>
*
* This can be sent by the network controller to inform a node that it
@@ -731,7 +769,8 @@ public:
*/
VERB_NETWORK_CONFIG_REFRESH = 12,
/* Request endpoints for multicast distribution:
/**
* Request endpoints for multicast distribution:
* <[8] 64-bit network ID>
* <[1] flags>
* <[6] MAC address of multicast group being queried>
@@ -747,6 +786,9 @@ public:
* to send multicast but does not have the desired number of recipient
* peers.
*
* More than one OK response can occur if the response is broken up across
* multiple packets or if querying a clustered node.
*
* OK response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
@@ -756,20 +798,12 @@ public:
* <[2] 16-bit number of members enumerated in this packet>
* <[...] series of 5-byte ZeroTier addresses of enumerated members>
*
* If no endpoints are known, OK and ERROR are both optional. It's okay
* to return nothing in that case since gathering is "lazy."
*
* ERROR response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
*
* ERRORs are optional and are only generated if permission is denied,
* certificate of membership is out of date, etc.
* ERROR is not generated; queries that return no response are dropped.
*/
VERB_MULTICAST_GATHER = 13,
/* Multicast frame:
/**
* Multicast frame:
* <[8] 64-bit network ID>
* <[1] flags>
* [<[...] network certificate of membership>]
@@ -810,7 +844,8 @@ public:
*/
VERB_MULTICAST_FRAME = 14,
/* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET)
/**
* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET)
* <[2] flags (unused and reserved, must be 0)>
* <[2] length of padding / extra field section>
* <[...] padding / extra field section>
@@ -866,7 +901,8 @@ public:
*/
VERB_SET_EPHEMERAL_KEY = 15,
/* Push of potential endpoints for direct communication:
/**
* Push of potential endpoints for direct communication:
* <[2] 16-bit number of paths>
* <[...] paths>
*
@@ -880,13 +916,10 @@ public:
*
* Path record flags:
* 0x01 - Forget this path if it is currently known
* 0x02 - Blacklist this path, do not use
* 0x02 - (Unused)
* 0x04 - Disable encryption (trust: privacy)
* 0x08 - Disable encryption and authentication (trust: ultimate)
*
* Address types and addresses are of the same format as the destination
* address type and address in HELLO.
*
* The receiver may, upon receiving a push, attempt to establish a
* direct link to one or more of the indicated addresses. It is the
* responsibility of the sender to limit which peers it pushes direct
@@ -906,7 +939,8 @@ public:
*/
VERB_PUSH_DIRECT_PATHS = 16,
/* Source-routed circuit test message:
/**
* Source-routed circuit test message:
* <[5] address of originator of circuit test>
* <[2] 16-bit flags>
* <[8] 64-bit timestamp>
@@ -984,7 +1018,8 @@ public:
*/
VERB_CIRCUIT_TEST = 17,
/* Circuit test hop report:
/**
* Circuit test hop report:
* <[8] 64-bit timestamp (from original test)>
* <[8] 64-bit test ID (from original test)>
* <[8] 64-bit reporter timestamp (reporter's clock, 0 if unspec)>
@@ -998,6 +1033,7 @@ public:
* <[2] 16-bit error code (set to 0, currently unused)>
* <[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>
@@ -1017,7 +1053,50 @@ 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 = 18
VERB_CIRCUIT_TEST_REPORT = 18,
/**
* Request proof of work:
* <[1] 8-bit proof of work type>
* <[1] 8-bit proof of work difficulty>
* <[2] 16-bit length of proof of work challenge>
* <[...] proof of work challenge>
*
* This requests that a peer perform a proof of work calucation. It can be
* sent by highly trusted peers (e.g. root servers, network controllers)
* under suspected denial of service conditions in an attempt to filter
* out "non-serious" peers and remain responsive to those proving their
* intent to actually communicate.
*
* If the peer obliges to perform the work, it does so and responds with
* an OK containing the result. Otherwise it may ignore the message or
* response with an ERROR_INVALID_REQUEST or ERROR_UNSUPPORTED_OPERATION.
*
* Proof of work type IDs:
* 0x01 - Salsa20/12+SHA512 hashcash function
*
* Salsa20/12+SHA512 is based on the following composite hash function:
*
* (1) Compute SHA512(candidate)
* (2) Use the first 256 bits of the result of #1 as a key to encrypt
* 131072 zero bytes with Salsa20/12 (with a zero IV).
* (3) Compute SHA512(the result of step #2)
* (4) Accept this candiate if the first [difficulty] bits of the result
* from step #3 are zero. Otherwise generate a new candidate and try
* again.
*
* This is performed repeatedly on candidates generated by appending the
* supplied challenge to an arbitrary nonce until a valid candidate
* is found. This chosen prepended nonce is then returned as the result
* in OK.
*
* OK payload:
* <[2] 16-bit length of result>
* <[...] computed proof of work>
*
* ERROR has no payload.
*/
VERB_REQUEST_PROOF_OF_WORK = 19
};
/**
@@ -1034,7 +1113,7 @@ public:
/* Bad/unsupported protocol version */
ERROR_BAD_PROTOCOL_VERSION = 2,
/* Unknown object queried (e.g. with WHOIS) */
/* Unknown object queried */
ERROR_OBJ_NOT_FOUND = 3,
/* HELLO pushed an identity whose address is already claimed */

View File

@@ -25,50 +25,21 @@
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_DEFAULTS_HPP
#define ZT_DEFAULTS_HPP
#include <stdexcept>
#include <string>
#include <vector>
#include <map>
#include "Constants.hpp"
#include "Identity.hpp"
#include "InetAddress.hpp"
#include "Path.hpp"
#include "AntiRecursion.hpp"
#include "RuntimeEnvironment.hpp"
#include "Node.hpp"
namespace ZeroTier {
/**
* Static configuration defaults
*
* These are the default values that ship baked into the ZeroTier binary. They
* define the basic parameters required for it to connect to the rest of the
* network and obtain software updates.
*/
class Defaults
bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
{
public:
Defaults();
/**
* Default root topology dictionary
*/
const std::string defaultRootTopology;
/**
* Identities permitted to sign root topology dictionaries
*/
const std::map< Address,Identity > rootTopologyAuthorities;
/**
* Address for IPv4 LAN auto-location broadcasts: 255.255.255.255:9993
*/
const InetAddress v4Broadcast;
};
extern const Defaults ZT_DEFAULTS;
if (RR->node->putPacket(_localAddress,address(),data,len)) {
sent(now);
RR->antiRec->logOutgoingZT(data,len);
return true;
}
return false;
}
} // namespace ZeroTier
#endif

View File

@@ -28,12 +28,29 @@
#ifndef ZT_PATH_HPP
#define ZT_PATH_HPP
#include <stdint.h>
#include <string.h>
#include <stdexcept>
#include <algorithm>
#include "Constants.hpp"
#include "InetAddress.hpp"
#include "Utils.hpp"
/**
* Flag indicating that this path is suboptimal
*
* This is used in cluster mode to indicate that the peer has been directed
* to a better path. This path can continue to be used but shouldn't be kept
* or advertised to other cluster members. Not used if clustering is not
* built and enabled.
*/
#define ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL 0x0001
namespace ZeroTier {
class RuntimeEnvironment;
/**
* Base class for paths
*
@@ -42,44 +59,85 @@ namespace ZeroTier {
class Path
{
public:
/**
* Path trust category
*
* Note that this is NOT peer trust and has nothing to do with root server
* designations or other trust metrics. This indicates how much we trust
* this path to be secure and/or private. A trust level of normal means
* encrypt and authenticate all traffic. Privacy trust means we can send
* traffic in the clear. Ultimate trust means we don't even need
* authentication. Generally a private path would be a hard-wired local
* LAN, while an ultimate trust path would be a physically isolated private
* server backplane.
*
* Nearly all paths will be normal trust. The other levels are for high
* performance local SDN use only.
*
* These values MUST match ZT_LocalInterfaceAddressTrust in ZeroTierOne.h
*/
enum Trust // NOTE: max 255
{
TRUST_NORMAL = 0,
TRUST_PRIVACY = 10,
TRUST_ULTIMATE = 20
};
Path() :
_lastSend(0),
_lastReceived(0),
_addr(),
_ipScope(InetAddress::IP_SCOPE_NONE),
_trust(TRUST_NORMAL)
_localAddress(),
_flags(0),
_ipScope(InetAddress::IP_SCOPE_NONE)
{
}
Path(const InetAddress &addr,int metric,Trust trust) :
Path(const InetAddress &localAddress,const InetAddress &addr) :
_lastSend(0),
_lastReceived(0),
_addr(addr),
_ipScope(addr.ipScope()),
_trust(trust)
_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 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 a packet is received from this remote path
*
* @param t Time of receive
*/
inline void received(uint64_t t) { _lastReceived = t; }
/**
* @param now Current time
* @return True if this path appears active
*/
inline bool active(uint64_t now) const
throw()
{
return ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT);
}
/**
* Send a packet via this path
*
* @param RR Runtime environment
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return True if transport reported success
*/
bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
/**
* @return Address of local side of this path or NULL if unspecified
*/
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 of last receive from this path
*/
inline uint64_t lastReceived() const throw() { return _lastReceived; }
/**
* @return Physical address
*/
@@ -104,17 +162,14 @@ public:
return ( ((int)_ipScope * 2) + ((_addr.ss_family == AF_INET6) ? 1 : 0) );
}
/**
* @return Path trust level
*/
inline Trust trust() const throw() { return _trust; }
/**
* @return True if path is considered reliable (no NAT keepalives etc. are needed)
*/
inline bool reliable() const throw()
{
return ( (_addr.ss_family == AF_INET6) || ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE)) );
if (_addr.ss_family == AF_INET)
return ((_ipScope != InetAddress::IP_SCOPE_GLOBAL)&&(_ipScope != InetAddress::IP_SCOPE_PSEUDOPRIVATE));
return true;
}
/**
@@ -155,10 +210,51 @@ public:
return false;
}
protected:
#ifdef ZT_ENABLE_CLUSTER
/**
* @param f New value of ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL
*/
inline void setClusterSuboptimal(bool f) { _flags = ((f) ? (_flags | ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) : (_flags & (~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); }
#endif
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
b.append((uint8_t)0); // version
b.append((uint64_t)_lastSend);
b.append((uint64_t)_lastReceived);
_addr.serialize(b);
_localAddress.serialize(b);
b.append((uint16_t)_flags);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
if (b[p++] != 0)
throw std::invalid_argument("invalid serialized Path");
_lastSend = 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;
_ipScope = _addr.ipScope();
return (p - startAt);
}
private:
uint64_t _lastSend;
uint64_t _lastReceived;
InetAddress _addr;
InetAddress _localAddress;
unsigned int _flags;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
Trust _trust;
};
} // namespace ZeroTier

View File

@@ -34,6 +34,8 @@
#include "Network.hpp"
#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Cluster.hpp"
#include "Packet.hpp"
#include <algorithm>
@@ -52,14 +54,17 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_lastMulticastFrame(0),
_lastAnnouncedTo(0),
_lastPathConfirmationSent(0),
_lastDirectPathPush(0),
_lastDirectPathPushSent(0),
_lastDirectPathPushReceive(0),
_lastPathSort(0),
_vProto(0),
_vMajor(0),
_vMinor(0),
_vRevision(0),
_id(peerIdentity),
_numPaths(0),
_latency(0),
_directPathPushCutoffCount(0),
_networkComs(4),
_lastPushedComs(4)
{
@@ -77,81 +82,132 @@ void Peer::received(
uint64_t inRePacketId,
Packet::Verb inReVerb)
{
#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 ( (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) && (verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS) ) {
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);
outp.append((uint16_t)1); // count == 1
outp.append((uint8_t)0); // no flags
outp.append((uint16_t)0); // no extensions
if (redirectTo.ss_family == AF_INET) {
outp.append((uint8_t)4);
outp.append((uint8_t)6);
outp.append(redirectTo.rawIpData(),4);
} else {
outp.append((uint8_t)6);
outp.append((uint8_t)18);
outp.append(redirectTo.rawIpData(),16);
}
outp.append((uint16_t)redirectTo.port());
outp.armor(_key,true);
RR->antiRec->logOutgoingZT(outp.data(),outp.size());
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);
outp.append((uint8_t)0); // no flags
RR->identity.address().appendTo(outp);
outp.append((uint16_t)redirectTo.port());
if (redirectTo.ss_family == AF_INET) {
outp.append((uint8_t)4);
outp.append(redirectTo.rawIpData(),4);
} else {
outp.append((uint8_t)16);
outp.append(redirectTo.rawIpData(),16);
}
outp.armor(_key,true);
RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
}
suboptimalPath = true;
}
}
#endif
const uint64_t now = RR->node->now();
bool needMulticastGroupAnnounce = false;
bool pathIsConfirmed = false;
{
{ // begin _lock
Mutex::Lock _l(_lock);
_lastReceive = now;
if (!hops) {
bool pathIsConfirmed = false;
/* Learn new paths from direct (hops == 0) packets */
{
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);
pathIsConfirmed = true;
break;
}
}
if (!pathIsConfirmed) {
if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) {
// Learn paths if they've been confirmed via a HELLO
RemotePath *slot = (RemotePath *)0;
if (np < ZT_MAX_PEER_NETWORK_PATHS) {
// Add new path
slot = &(_paths[np++]);
} else {
// Replace oldest non-fixed path
uint64_t slotLRmin = 0xffffffffffffffffULL;
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
if ((!_paths[p].fixed())&&(_paths[p].lastReceived() <= slotLRmin)) {
slotLRmin = _paths[p].lastReceived();
slot = &(_paths[p]);
}
}
}
if (slot) {
*slot = RemotePath(localAddr,remoteAddr,false);
slot->received(now);
_numPaths = np;
pathIsConfirmed = true;
_sortPaths(now);
}
} else {
/* If this path is not known, send a HELLO. We don't learn
* paths without confirming that a bidirectional link is in
* fact present, but any packet that decodes and authenticates
* correctly is considered valid. */
if ((now - _lastPathConfirmationSent) >= ZT_MIN_PATH_CONFIRMATION_INTERVAL) {
_lastPathConfirmationSent = now;
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
attemptToContactAt(RR,localAddr,remoteAddr,now);
}
}
}
}
}
if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
_lastUnicastFrame = now;
else if (verb == Packet::VERB_MULTICAST_FRAME)
_lastMulticastFrame = now;
if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
_lastAnnouncedTo = now;
needMulticastGroupAnnounce = true;
}
if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
_lastUnicastFrame = now;
else if (verb == Packet::VERB_MULTICAST_FRAME)
_lastMulticastFrame = now;
}
if (hops == 0) {
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].setClusterSuboptimal(suboptimalPath);
#endif
pathIsConfirmed = true;
break;
}
}
if (!pathIsConfirmed) {
if (verb == Packet::VERB_OK) {
Path *slot = (Path *)0;
if (np < ZT_MAX_PEER_NETWORK_PATHS) {
slot = &(_paths[np++]);
} else {
uint64_t slotLRmin = 0xffffffffffffffffULL;
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
if (_paths[p].lastReceived() <= slotLRmin) {
slotLRmin = _paths[p].lastReceived();
slot = &(_paths[p]);
}
}
}
if (slot) {
*slot = Path(localAddr,remoteAddr);
slot->received(now);
#ifdef ZT_ENABLE_CLUSTER
slot->setClusterSuboptimal(suboptimalPath);
#endif
_numPaths = np;
pathIsConfirmed = true;
_sortPaths(now);
}
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster)
RR->cluster->broadcastHavePeer(_id);
#endif
} else {
/* If this path is not known, send a HELLO. We don't learn
* paths without confirming that a bidirectional link is in
* fact present, but any packet that decodes and authenticates
* correctly is considered valid. */
if ((now - _lastPathConfirmationSent) >= ZT_MIN_PATH_CONFIRMATION_INTERVAL) {
_lastPathConfirmationSent = now;
TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),remoteAddr.toString().c_str());
sendHELLO(RR,localAddr,remoteAddr,now);
}
}
}
}
} // end _lock
if (needMulticastGroupAnnounce) {
const std::vector< SharedPtr<Network> > networks(RR->node->allNetworks());
@@ -160,7 +216,7 @@ void Peer::received(
}
}
void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now)
void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
{
// _lock not required here since _id is immutable and nothing else is accessed
@@ -170,69 +226,76 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
outp.append(now);
RR->identity.serialize(outp,false);
switch(atAddress.ss_family) {
case AF_INET:
outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV4);
outp.append(atAddress.rawIpData(),4);
outp.append((uint16_t)atAddress.port());
break;
case AF_INET6:
outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_IPV6);
outp.append(atAddress.rawIpData(),16);
outp.append((uint16_t)atAddress.port());
break;
default:
outp.append((unsigned char)ZT_PROTO_DEST_ADDRESS_TYPE_NONE);
break;
}
atAddress.serialize(outp);
outp.append((uint64_t)RR->topology->worldId());
outp.append((uint64_t)RR->topology->worldTimestamp());
outp.armor(_key,false); // HELLO is sent in the clear
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
}
void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily)
{
Path *p = (Path *)0;
Mutex::Lock _l(_lock);
RemotePath *const bestPath = _getBestPath(now);
if (bestPath) {
if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str());
attemptToContactAt(RR,bestPath->localAddress(),bestPath->address(),now);
bestPath->sent(now);
} else if (((now - bestPath->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!bestPath->reliable())) {
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
TRACE("NAT keepalive %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str());
RR->node->putPacket(bestPath->localAddress(),bestPath->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
bestPath->sent(now);
}
if (inetAddressFamily != 0) {
p = _getBestPath(now,inetAddressFamily);
} else {
p = _getBestPath(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(RR,p->localAddress(),p->address(),now);
p->sent(now);
} else if (((now - p->lastSend()) >= 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
RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
p->sent(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;
}
return false;
}
void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force)
void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force)
{
#ifdef ZT_ENABLE_CLUSTER
// Cluster mode disables normal PUSH_DIRECT_PATHS in favor of cluster-based peer redirection
if (RR->cluster)
return;
#endif
Mutex::Lock _l(_lock);
if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) {
_lastDirectPathPush = now;
if (((now - _lastDirectPathPushSent) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) {
_lastDirectPathPushSent = now;
std::vector<Path> dps(RR->node->directPaths());
std::vector<InetAddress> dps(RR->node->directPaths());
if (dps.empty())
return;
#ifdef ZT_TRACE
{
std::string ps;
for(std::vector<Path>::const_iterator p(dps.begin());p!=dps.end();++p) {
for(std::vector<InetAddress>::const_iterator p(dps.begin());p!=dps.end();++p) {
if (ps.length() > 0)
ps.push_back(',');
ps.append(p->address().toString());
ps.append(p->toString());
}
TRACE("pushing %u direct paths to %s: %s",(unsigned int)dps.size(),_id.address().toString().c_str(),ps.c_str());
}
#endif
std::vector<Path>::const_iterator p(dps.begin());
std::vector<InetAddress>::const_iterator p(dps.begin());
while (p != dps.end()) {
Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
outp.addSize(2); // leave room for count
@@ -240,7 +303,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
unsigned int count = 0;
while ((p != dps.end())&&((outp.size() + 24) < ZT_PROTO_MAX_PACKET_LENGTH)) {
uint8_t addressType = 4;
switch(p->address().ss_family) {
switch(p->ss_family) {
case AF_INET:
break;
case AF_INET6:
@@ -252,6 +315,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
}
uint8_t flags = 0;
/* TODO: path trust is not implemented yet
switch(p->trust()) {
default:
break;
@@ -262,13 +326,14 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
flags |= (0x04 | 0x08); // no encryption, no authentication (redundant but go ahead and set both)
break;
}
*/
outp.append(flags);
outp.append((uint16_t)0); // no extensions
outp.append(addressType);
outp.append((uint8_t)((addressType == 4) ? 6 : 18));
outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16));
outp.append((uint16_t)p->address().port());
outp.append(p->rawIpData(),((addressType == 4) ? 4 : 16));
outp.append((uint16_t)p->port());
++count;
++p;
@@ -283,59 +348,6 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
}
}
void Peer::addPath(const RemotePath &newp,uint64_t now)
{
Mutex::Lock _l(_lock);
unsigned int np = _numPaths;
for(unsigned int p=0;p<np;++p) {
if (_paths[p].address() == newp.address()) {
_paths[p].setFixed(newp.fixed());
_sortPaths(now);
return;
}
}
RemotePath *slot = (RemotePath *)0;
if (np < ZT_MAX_PEER_NETWORK_PATHS) {
// Add new path
slot = &(_paths[np++]);
} else {
// Replace oldest non-fixed path
uint64_t slotLRmin = 0xffffffffffffffffULL;
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
if ((!_paths[p].fixed())&&(_paths[p].lastReceived() <= slotLRmin)) {
slotLRmin = _paths[p].lastReceived();
slot = &(_paths[p]);
}
}
}
if (slot) {
*slot = newp;
_numPaths = np;
}
_sortPaths(now);
}
void Peer::clearPaths(bool fixedToo)
{
if (fixedToo) {
_numPaths = 0;
} else {
unsigned int np = _numPaths;
unsigned int x = 0;
unsigned int y = 0;
while (x < np) {
if (_paths[x].fixed())
_paths[y++] = _paths[x];
++x;
}
_numPaths = y;
}
}
bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now)
{
Mutex::Lock _l(_lock);
@@ -344,12 +356,9 @@ bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope sc
unsigned int y = 0;
while (x < np) {
if (_paths[x].address().ipScope() == scope) {
if (_paths[x].fixed()) {
attemptToContactAt(RR,_paths[x].localAddress(),_paths[x].address(),now);
_paths[y++] = _paths[x]; // keep fixed paths
}
sendHELLO(RR,_paths[x].localAddress(),_paths[x].address(),now);
} else {
_paths[y++] = _paths[x]; // keep paths not in this scope
_paths[y++] = _paths[x];
}
++x;
}
@@ -497,7 +506,7 @@ struct _SortPathsByQuality
{
uint64_t _now;
_SortPathsByQuality(const uint64_t now) : _now(now) {}
inline bool operator()(const RemotePath &a,const RemotePath &b) const
inline bool operator()(const Path &a,const Path &b) const
{
const uint64_t qa = (
((uint64_t)a.active(_now) << 63) |
@@ -517,7 +526,7 @@ void Peer::_sortPaths(const uint64_t now)
std::sort(&(_paths[0]),&(_paths[_numPaths]),_SortPathsByQuality(now));
}
RemotePath *Peer::_getBestPath(const uint64_t now)
Path *Peer::_getBestPath(const uint64_t now)
{
// assumes _lock is locked
if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL)
@@ -529,7 +538,22 @@ RemotePath *Peer::_getBestPath(const uint64_t now)
if (_paths[0].active(now))
return &(_paths[0]);
}
return (RemotePath *)0;
return (Path *)0;
}
Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
{
// assumes _lock is locked
if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL)
_sortPaths(now);
for(int k=0;k<2;++k) { // try once, and if it fails sort and try one more time
for(unsigned int i=0;i<_numPaths;++i) {
if ((_paths[i].active(now))&&((int)_paths[i].address().ss_family == inetAddressFamily))
return &(_paths[i]);
}
_sortPaths(now);
}
return (Path *)0;
}
} // namespace ZeroTier

View File

@@ -41,7 +41,7 @@
#include "RuntimeEnvironment.hpp"
#include "CertificateOfMembership.hpp"
#include "RemotePath.hpp"
#include "Path.hpp"
#include "Address.hpp"
#include "Utils.hpp"
#include "Identity.hpp"
@@ -130,12 +130,12 @@ public:
Packet::Verb inReVerb = Packet::VERB_NOP);
/**
* Get the best direct path to this peer
* Get the current best direct path to this peer
*
* @param now Current time
* @return Best path or NULL if there are no active (or fixed) direct paths
* @return Best path or NULL if there are no active direct paths
*/
inline RemotePath *getBestPath(uint64_t now)
inline Path *getBestPath(uint64_t now)
{
Mutex::Lock _l(_lock);
return _getBestPath(now);
@@ -150,14 +150,14 @@ public:
* @param now Current time
* @return Path used on success or NULL on failure
*/
inline RemotePath *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
{
RemotePath *bestPath = getBestPath(now);
Path *bestPath = getBestPath(now);
if (bestPath) {
if (bestPath->send(RR,data,len,now))
return bestPath;
}
return (RemotePath *)0;
return (Path *)0;
}
/**
@@ -170,16 +170,19 @@ public:
* @param localAddr Local address
* @param atAddress Destination address
* @param now Current time
* @param ttl Desired IP TTL (default: 0 to leave alone)
*/
void attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now);
void sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
/**
* Send pings or keepalives depending on configured timeouts
*
* @param RR Runtime environment
* @param now Current time
* @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
*/
void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now);
bool doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily);
/**
* Push direct paths if we haven't done so in [rate limit] milliseconds
@@ -189,46 +192,20 @@ public:
* @param now Current time
* @param force If true, push regardless of rate limit
*/
void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force);
void pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force);
/**
* @return All known direct paths to this peer
*/
inline std::vector<RemotePath> paths() const
inline std::vector<Path> paths() const
{
std::vector<RemotePath> pp;
std::vector<Path> pp;
Mutex::Lock _l(_lock);
for(unsigned int p=0,np=_numPaths;p<np;++p)
pp.push_back(_paths[p]);
return pp;
}
/**
* @return Time of last direct packet receive for any path
*/
inline uint64_t lastDirectReceive() const
throw()
{
Mutex::Lock _l(_lock);
uint64_t x = 0;
for(unsigned int p=0,np=_numPaths;p<np;++p)
x = std::max(x,_paths[p].lastReceived());
return x;
}
/**
* @return Time of last direct packet send for any path
*/
inline uint64_t lastDirectSend() const
throw()
{
Mutex::Lock _l(_lock);
uint64_t x = 0;
for(unsigned int p=0,np=_numPaths;p<np;++p)
x = std::max(x,_paths[p].lastSend());
return x;
}
/**
* @return Time of last receive of anything, whether direct or relayed
*/
@@ -255,27 +232,44 @@ public:
inline uint64_t lastAnnouncedTo() const throw() { return _lastAnnouncedTo; }
/**
* @return True if peer has received an actual data frame within ZT_PEER_ACTIVITY_TIMEOUT milliseconds
* @return True if this peer is actively sending real network frames
*/
inline uint64_t alive(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
inline uint64_t activelyTransferringFrames(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
/**
* @return Current latency or 0 if unknown (max: 65535)
* @return Latency in milliseconds or 0 if unknown
*/
inline unsigned int latency() const
throw()
inline unsigned int latency() const { return _latency; }
/**
* This computes a quality score for relays and root servers
*
* If we haven't heard anything from these in ZT_PEER_ACTIVITY_TIMEOUT, they
* receive the worst possible quality (max unsigned int). Otherwise the
* quality is a product of latency and the number of potential missed
* pings. This causes roots and relays to switch over a bit faster if they
* fail.
*
* @return Relay quality score computed from latency and other factors, lower is better
*/
inline unsigned int relayQuality(const uint64_t now) const
{
const uint64_t tsr = now - _lastReceive;
if (tsr >= ZT_PEER_ACTIVITY_TIMEOUT)
return (~(unsigned int)0);
unsigned int l = _latency;
return std::min(l,(unsigned int)65535);
if (!l)
l = 0xffff;
return (l * (((unsigned int)tsr / (ZT_PEER_DIRECT_PING_DELAY + 1000)) + 1));
}
/**
* Update latency with a new direct measurment
*
* @param l Direct latency measurment in ms
*/
inline void addDirectLatencyMeasurment(unsigned int l)
throw()
{
unsigned int ol = _latency;
if ((ol > 0)&&(ol < 10000))
@@ -283,17 +277,11 @@ public:
else _latency = std::min(l,(unsigned int)65535);
}
/**
* @return True if this peer has at least one direct IP address path
*/
inline bool hasDirectPath() const throw() { return (_numPaths != 0); }
/**
* @param now Current time
* @return True if this peer has at least one active or fixed direct path
* @return True if this peer has at least one active direct path
*/
inline bool hasActiveDirectPath(uint64_t now) const
throw()
{
Mutex::Lock _l(_lock);
for(unsigned int p=0,np=_numPaths;p<np;++p) {
@@ -303,27 +291,25 @@ public:
return false;
}
#ifdef ZT_ENABLE_CLUSTER
/**
* Add a path (if we don't already have it)
*
* @param p New path to add
* @param now Current time
* @return True if this peer has at least one active direct path that is not cluster-suboptimal
*/
void addPath(const RemotePath &newp,uint64_t now);
/**
* Clear paths
*
* @param fixedToo If true, clear fixed paths as well as learned ones
*/
void clearPaths(bool fixedToo);
inline bool hasClusterOptimalPath(uint64_t now) const
{
Mutex::Lock _l(_lock);
for(unsigned int p=0,np=_numPaths;p<np;++p) {
if ((_paths[p].active(now))&&(!_paths[p].isClusterSuboptimal()))
return true;
}
return false;
}
#endif
/**
* Reset paths within a given scope
*
* For fixed paths in this scope, a packet is sent. Non-fixed paths in this
* scope are forgotten.
*
* @param RR Runtime environment
* @param scope IP scope of paths to reset
* @param now Current time
@@ -346,7 +332,6 @@ public:
*/
inline void setRemoteVersion(unsigned int vproto,unsigned int vmaj,unsigned int vmin,unsigned int vrev)
{
Mutex::Lock _l(_lock);
_vProto = (uint16_t)vproto;
_vMajor = (uint16_t)vmaj;
_vMinor = (uint16_t)vmin;
@@ -359,33 +344,6 @@ public:
inline unsigned int remoteVersionRevision() const throw() { return _vRevision; }
inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); }
/**
* Check whether this peer's version is both known and is at least what is specified
*
* @param major Major version to check against
* @param minor Minor version
* @param rev Revision
* @return True if peer's version is at least supplied tuple
*/
inline bool atLeastVersion(unsigned int major,unsigned int minor,unsigned int rev)
throw()
{
Mutex::Lock _l(_lock);
if ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)) {
if (_vMajor > major)
return true;
else if (_vMajor == major) {
if (_vMinor > minor)
return true;
else if (_vMinor == minor) {
if (_vRevision >= rev)
return true;
}
}
}
return false;
}
/**
* Get most recently active path addresses for IPv4 and/or IPv6
*
@@ -429,6 +387,46 @@ public:
*/
void clean(const RuntimeEnvironment *RR,uint64_t now);
/**
* Remove all paths with this remote address
*
* @param addr Remote address to remove
*/
inline void removePathByAddress(const InetAddress &addr)
{
Mutex::Lock _l(_lock);
unsigned int np = _numPaths;
unsigned int x = 0;
unsigned int y = 0;
while (x < np) {
if (_paths[x].address() != addr)
_paths[y++] = _paths[x];
++x;
}
_numPaths = y;
}
/**
* 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)
{
Mutex::Lock _l(_lock);
if ((now - _lastDirectPathPushReceive) <= ZT_PUSH_DIRECT_PATHS_CUTOFF_TIME)
++_directPathPushCutoffCount;
else _directPathPushCutoffCount = 0;
_lastDirectPathPushReceive = now;
return (_directPathPushCutoffCount < ZT_PUSH_DIRECT_PATHS_CUTOFF_LIMIT);
}
/**
* Find a common set of addresses by which two peers can link, if any
*
@@ -454,10 +452,10 @@ public:
{
Mutex::Lock _l(_lock);
const unsigned int atPos = b.size();
const unsigned int recSizePos = b.size();
b.addSize(4); // space for uint32_t field length
b.append((uint32_t)1); // version of serialized Peer data
b.append((uint16_t)0); // version of serialized Peer data
_id.serialize(b,false);
@@ -467,15 +465,17 @@ public:
b.append((uint64_t)_lastMulticastFrame);
b.append((uint64_t)_lastAnnouncedTo);
b.append((uint64_t)_lastPathConfirmationSent);
b.append((uint64_t)_lastDirectPathPush);
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((uint32_t)_numPaths);
b.append((uint16_t)_numPaths);
for(unsigned int i=0;i<_numPaths;++i)
_paths[i].serialize(b);
@@ -502,7 +502,7 @@ public:
}
}
b.setAt(atPos,(uint32_t)(b.size() - atPos)); // set size
b.template setAt<uint32_t>(recSizePos,(uint32_t)(b.size() - (recSizePos + 4))); // set size
}
/**
@@ -516,13 +516,12 @@ public:
template<unsigned int C>
static inline SharedPtr<Peer> deserializeNew(const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
{
const uint32_t recSize = b.template at<uint32_t>(p);
const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
if ((p + recSize) > b.size())
return SharedPtr<Peer>(); // size invalid
p += 4;
if (b.template at<uint32_t>(p) != 1)
if (b.template at<uint16_t>(p) != 0)
return SharedPtr<Peer>(); // version mismatch
p += 4;
p += 2;
Identity npid;
p += npid.deserialize(b,p);
@@ -537,21 +536,23 @@ public:
np->_lastMulticastFrame = b.template at<uint64_t>(p); p += 8;
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
np->_lastPathConfirmationSent = b.template at<uint64_t>(p); p += 8;
np->_lastDirectPathPush = 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<uint32_t>(p); p += 4;
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
RemotePath foo;
Path foo;
p += foo.deserialize(b,p);
}
}
@@ -575,25 +576,29 @@ public:
private:
void _sortPaths(const uint64_t now);
RemotePath *_getBestPath(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
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
uint64_t _lastUsed;
uint64_t _lastReceive; // direct or indirect
uint64_t _lastUnicastFrame;
uint64_t _lastMulticastFrame;
uint64_t _lastAnnouncedTo;
uint64_t _lastPathConfirmationSent;
uint64_t _lastDirectPathPush;
uint64_t _lastDirectPathPushSent;
uint64_t _lastDirectPathPushReceive;
uint64_t _lastPathSort;
uint16_t _vProto;
uint16_t _vMajor;
uint16_t _vMinor;
uint16_t _vRevision;
Identity _id;
RemotePath _paths[ZT_MAX_PEER_NETWORK_PATHS];
Path _paths[ZT_MAX_PEER_NETWORK_PATHS];
unsigned int _numPaths;
unsigned int _latency;
unsigned int _directPathPushCutoffCount;
struct _NetworkCom
{

View File

@@ -20,6 +20,9 @@ namespace ZeroTier {
#if 0
// "Naive" implementation, which is slower... might still want this on some older
// or weird platforms if the later versions have issues.
static inline void add(unsigned int h[17],const unsigned int c[17])
{
unsigned int j;
@@ -132,9 +135,236 @@ typedef struct poly1305_context {
unsigned char opaque[136];
} poly1305_context;
/*
poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition
*/
#if (defined(_MSC_VER) || defined(__GNUC__)) && (defined(__amd64) || defined(__amd64__) || defined(__x86_64) || defined(__x86_64__) || defined(__AMD64) || defined(__AMD64__))
//////////////////////////////////////////////////////////////////////////////
// 128-bit implementation for MSC and GCC from Poly1305-donna
#if defined(_MSC_VER)
#include <intrin.h>
typedef struct uint128_t {
unsigned long long lo;
unsigned long long hi;
} uint128_t;
#define MUL(out, x, y) out.lo = _umul128((x), (y), &out.hi)
#define ADD(out, in) { unsigned long long t = out.lo; out.lo += in.lo; out.hi += (out.lo < t) + in.hi; }
#define ADDLO(out, in) { unsigned long long t = out.lo; out.lo += in; out.hi += (out.lo < t); }
#define SHR(in, shift) (__shiftright128(in.lo, in.hi, (shift)))
#define LO(in) (in.lo)
// #define POLY1305_NOINLINE __declspec(noinline)
#elif defined(__GNUC__)
#if defined(__SIZEOF_INT128__)
typedef unsigned __int128 uint128_t;
#else
typedef unsigned uint128_t __attribute__((mode(TI)));
#endif
#define MUL(out, x, y) out = ((uint128_t)x * y)
#define ADD(out, in) out += in
#define ADDLO(out, in) out += in
#define SHR(in, shift) (unsigned long long)(in >> (shift))
#define LO(in) (unsigned long long)(in)
// #define POLY1305_NOINLINE __attribute__((noinline))
#endif
#define poly1305_block_size 16
/* 17 + sizeof(size_t) + 8*sizeof(unsigned long long) */
typedef struct poly1305_state_internal_t {
unsigned long long r[3];
unsigned long long h[3];
unsigned long long pad[2];
size_t leftover;
unsigned char buffer[poly1305_block_size];
unsigned char final;
} poly1305_state_internal_t;
/* interpret eight 8 bit unsigned integers as a 64 bit unsigned integer in little endian */
static inline unsigned long long
U8TO64(const unsigned char *p) {
return
(((unsigned long long)(p[0] & 0xff) ) |
((unsigned long long)(p[1] & 0xff) << 8) |
((unsigned long long)(p[2] & 0xff) << 16) |
((unsigned long long)(p[3] & 0xff) << 24) |
((unsigned long long)(p[4] & 0xff) << 32) |
((unsigned long long)(p[5] & 0xff) << 40) |
((unsigned long long)(p[6] & 0xff) << 48) |
((unsigned long long)(p[7] & 0xff) << 56));
}
/* store a 64 bit unsigned integer as eight 8 bit unsigned integers in little endian */
static inline void
U64TO8(unsigned char *p, unsigned long long v) {
p[0] = (v ) & 0xff;
p[1] = (v >> 8) & 0xff;
p[2] = (v >> 16) & 0xff;
p[3] = (v >> 24) & 0xff;
p[4] = (v >> 32) & 0xff;
p[5] = (v >> 40) & 0xff;
p[6] = (v >> 48) & 0xff;
p[7] = (v >> 56) & 0xff;
}
static inline void
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
unsigned long long t0,t1;
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
t0 = U8TO64(&key[0]);
t1 = U8TO64(&key[8]);
st->r[0] = ( t0 ) & 0xffc0fffffff;
st->r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff;
st->r[2] = ((t1 >> 24) ) & 0x00ffffffc0f;
/* h = 0 */
st->h[0] = 0;
st->h[1] = 0;
st->h[2] = 0;
/* save pad for later */
st->pad[0] = U8TO64(&key[16]);
st->pad[1] = U8TO64(&key[24]);
st->leftover = 0;
st->final = 0;
}
static inline void
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
const unsigned long long hibit = (st->final) ? 0 : ((unsigned long long)1 << 40); /* 1 << 128 */
unsigned long long r0,r1,r2;
unsigned long long s1,s2;
unsigned long long h0,h1,h2;
unsigned long long c;
uint128_t d0,d1,d2,d;
r0 = st->r[0];
r1 = st->r[1];
r2 = st->r[2];
h0 = st->h[0];
h1 = st->h[1];
h2 = st->h[2];
s1 = r1 * (5 << 2);
s2 = r2 * (5 << 2);
while (bytes >= poly1305_block_size) {
unsigned long long t0,t1;
/* h += m[i] */
t0 = U8TO64(&m[0]);
t1 = U8TO64(&m[8]);
h0 += (( t0 ) & 0xfffffffffff);
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff);
h2 += (((t1 >> 24) ) & 0x3ffffffffff) | hibit;
/* h *= r */
MUL(d0, h0, r0); MUL(d, h1, s2); ADD(d0, d); MUL(d, h2, s1); ADD(d0, d);
MUL(d1, h0, r1); MUL(d, h1, r0); ADD(d1, d); MUL(d, h2, s2); ADD(d1, d);
MUL(d2, h0, r2); MUL(d, h1, r1); ADD(d2, d); MUL(d, h2, r0); ADD(d2, d);
/* (partial) h %= p */
c = SHR(d0, 44); h0 = LO(d0) & 0xfffffffffff;
ADDLO(d1, c); c = SHR(d1, 44); h1 = LO(d1) & 0xfffffffffff;
ADDLO(d2, c); c = SHR(d2, 42); h2 = LO(d2) & 0x3ffffffffff;
h0 += c * 5; c = (h0 >> 44); h0 = h0 & 0xfffffffffff;
h1 += c;
m += poly1305_block_size;
bytes -= poly1305_block_size;
}
st->h[0] = h0;
st->h[1] = h1;
st->h[2] = h2;
}
static inline void
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
unsigned long long h0,h1,h2,c;
unsigned long long g0,g1,g2;
unsigned long long t0,t1;
/* process the remaining block */
if (st->leftover) {
size_t i = st->leftover;
st->buffer[i] = 1;
for (i = i + 1; i < poly1305_block_size; i++)
st->buffer[i] = 0;
st->final = 1;
poly1305_blocks(st, st->buffer, poly1305_block_size);
}
/* fully carry h */
h0 = st->h[0];
h1 = st->h[1];
h2 = st->h[2];
c = (h1 >> 44); h1 &= 0xfffffffffff;
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
h1 += c; c = (h1 >> 44); h1 &= 0xfffffffffff;
h2 += c; c = (h2 >> 42); h2 &= 0x3ffffffffff;
h0 += c * 5; c = (h0 >> 44); h0 &= 0xfffffffffff;
h1 += c;
/* compute h + -p */
g0 = h0 + 5; c = (g0 >> 44); g0 &= 0xfffffffffff;
g1 = h1 + c; c = (g1 >> 44); g1 &= 0xfffffffffff;
g2 = h2 + c - ((unsigned long long)1 << 42);
/* select h if h < p, or h + -p if h >= p */
c = (g2 >> ((sizeof(unsigned long long) * 8) - 1)) - 1;
g0 &= c;
g1 &= c;
g2 &= c;
c = ~c;
h0 = (h0 & c) | g0;
h1 = (h1 & c) | g1;
h2 = (h2 & c) | g2;
/* h = (h + pad) */
t0 = st->pad[0];
t1 = st->pad[1];
h0 += (( t0 ) & 0xfffffffffff) ; c = (h0 >> 44); h0 &= 0xfffffffffff;
h1 += (((t0 >> 44) | (t1 << 20)) & 0xfffffffffff) + c; c = (h1 >> 44); h1 &= 0xfffffffffff;
h2 += (((t1 >> 24) ) & 0x3ffffffffff) + c; h2 &= 0x3ffffffffff;
/* mac = h % (2^128) */
h0 = ((h0 ) | (h1 << 44));
h1 = ((h1 >> 20) | (h2 << 24));
U64TO8(&mac[0], h0);
U64TO8(&mac[8], h1);
/* zero out the state */
st->h[0] = 0;
st->h[1] = 0;
st->h[2] = 0;
st->r[0] = 0;
st->r[1] = 0;
st->r[2] = 0;
st->pad[0] = 0;
st->pad[1] = 0;
}
//////////////////////////////////////////////////////////////////////////////
#else
//////////////////////////////////////////////////////////////////////////////
// More portable 64-bit implementation
#define poly1305_block_size 16
@@ -256,43 +486,6 @@ poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t by
st->h[4] = h4;
}
static inline void
poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
size_t i;
/* handle leftover */
if (st->leftover) {
size_t want = (poly1305_block_size - st->leftover);
if (want > bytes)
want = bytes;
for (i = 0; i < want; i++)
st->buffer[st->leftover + i] = m[i];
bytes -= want;
m += want;
st->leftover += want;
if (st->leftover < poly1305_block_size)
return;
poly1305_blocks(st, st->buffer, poly1305_block_size);
st->leftover = 0;
}
/* process full blocks */
if (bytes >= poly1305_block_size) {
size_t want = (bytes & ~(poly1305_block_size - 1));
poly1305_blocks(st, m, want);
m += want;
bytes -= want;
}
/* store leftover */
if (bytes) {
for (i = 0; i < bytes; i++)
st->buffer[st->leftover + i] = m[i];
st->leftover += bytes;
}
}
static inline void
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
@@ -380,6 +573,47 @@ poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
st->pad[3] = 0;
}
//////////////////////////////////////////////////////////////////////////////
#endif // MSC/GCC or not
static inline void
poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
size_t i;
/* handle leftover */
if (st->leftover) {
size_t want = (poly1305_block_size - st->leftover);
if (want > bytes)
want = bytes;
for (i = 0; i < want; i++)
st->buffer[st->leftover + i] = m[i];
bytes -= want;
m += want;
st->leftover += want;
if (st->leftover < poly1305_block_size)
return;
poly1305_blocks(st, st->buffer, poly1305_block_size);
st->leftover = 0;
}
/* process full blocks */
if (bytes >= poly1305_block_size) {
size_t want = (bytes & ~(poly1305_block_size - 1));
poly1305_blocks(st, m, want);
m += want;
bytes -= want;
}
/* store leftover */
if (bytes) {
for (i = 0; i < bytes; i++)
st->buffer[st->leftover + i] = m[i];
st->leftover += bytes;
}
}
} // anonymous namespace
void Poly1305::compute(void *auth,const void *data,unsigned int len,const void *key)

View File

@@ -1,179 +0,0 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_REMOTEPATH_HPP
#define ZT_REMOTEPATH_HPP
#include <stdint.h>
#include <string.h>
#include <stdexcept>
#include <algorithm>
#include "Path.hpp"
#include "Node.hpp"
#include "AntiRecursion.hpp"
#include "RuntimeEnvironment.hpp"
#define ZT_REMOTEPATH_FLAG_FIXED 0x0001
namespace ZeroTier {
/**
* Path to a remote peer
*
* This extends Path to include status information about path activity.
*/
class RemotePath : public Path
{
public:
RemotePath() :
Path(),
_lastSend(0),
_lastReceived(0),
_localAddress(),
_flags(0) {}
RemotePath(const InetAddress &localAddress,const InetAddress &addr,bool fixed) :
Path(addr,0,TRUST_NORMAL),
_lastSend(0),
_lastReceived(0),
_localAddress(localAddress),
_flags(fixed ? ZT_REMOTEPATH_FLAG_FIXED : 0) {}
inline const InetAddress &localAddress() const throw() { return _localAddress; }
inline uint64_t lastSend() const throw() { return _lastSend; }
inline uint64_t lastReceived() const throw() { return _lastReceived; }
/**
* @return Is this a fixed path?
*/
inline bool fixed() const throw() { return ((_flags & ZT_REMOTEPATH_FLAG_FIXED) != 0); }
/**
* @param f New value of fixed flag
*/
inline void setFixed(const bool f)
throw()
{
if (f)
_flags |= ZT_REMOTEPATH_FLAG_FIXED;
else _flags &= ~ZT_REMOTEPATH_FLAG_FIXED;
}
/**
* Called when a packet is sent to this remote path
*
* This is called automatically by RemotePath::send().
*
* @param t Time of send
*/
inline void sent(uint64_t t)
throw()
{
_lastSend = t;
}
/**
* Called when a packet is received from this remote path
*
* @param t Time of receive
*/
inline void received(uint64_t t)
throw()
{
_lastReceived = t;
}
/**
* @param now Current time
* @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms
*/
inline bool active(uint64_t now) const
throw()
{
return ( ((_flags & ZT_REMOTEPATH_FLAG_FIXED) != 0) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) );
}
/**
* Send a packet via this path
*
* @param RR Runtime environment
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return True if transport reported success
*/
inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
{
if (RR->node->putPacket(_localAddress,address(),data,len)) {
sent(now);
RR->antiRec->logOutgoingZT(data,len);
return true;
}
return false;
}
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
b.append((uint8_t)1); // version
_addr.serialize(b);
b.append((uint8_t)_trust);
b.append((uint64_t)_lastSend);
b.append((uint64_t)_lastReceived);
_localAddress.serialize(b);
b.append((uint16_t)_flags);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
if (b[p++] != 1)
throw std::invalid_argument("invalid serialized RemotePath");
p += _addr.deserialize(b,p);
_ipScope = _addr.ipScope();
_trust = (Path::Trust)b[p++];
_lastSend = b.template at<uint64_t>(p); p += 8;
_lastReceived = b.template at<uint64_t>(p); p += 8;
p += _localAddress.deserialize(b,p);
_flags = b.template at<uint16_t>(p); p += 2;
return (p - startAt);
}
protected:
uint64_t _lastSend;
uint64_t _lastReceived;
InetAddress _localAddress;
uint16_t _flags;
};
} // namespace ZeroTier
#endif

View File

@@ -32,6 +32,7 @@
#include "Constants.hpp"
#include "Identity.hpp"
#include "Mutex.hpp"
namespace ZeroTier {
@@ -43,6 +44,8 @@ class Multicaster;
class AntiRecursion;
class NetworkController;
class SelfAwareness;
class Cluster;
class DeferredPackets;
/**
* Holds global state for an instance of ZeroTier::Node
@@ -51,14 +54,18 @@ class RuntimeEnvironment
{
public:
RuntimeEnvironment(Node *n) :
node(n),
identity(),
localNetworkController((NetworkController *)0),
sw((Switch *)0),
mc((Multicaster *)0),
antiRec((AntiRecursion *)0),
topology((Topology *)0),
sa((SelfAwareness *)0)
node(n)
,identity()
,localNetworkController((NetworkController *)0)
,sw((Switch *)0)
,mc((Multicaster *)0)
,antiRec((AntiRecursion *)0)
,topology((Topology *)0)
,sa((SelfAwareness *)0)
,dp((DeferredPackets *)0)
#ifdef ZT_ENABLE_CLUSTER
,cluster((Cluster *)0)
#endif
{
}
@@ -86,6 +93,15 @@ public:
AntiRecursion *antiRec;
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

File diff suppressed because it is too large Load Diff

View File

@@ -12,6 +12,7 @@
#include <stdlib.h>
#include "Constants.hpp"
#include "Utils.hpp"
#if (!defined(ZT_SALSA20_SSE)) && (defined(__SSE2__) || defined(__WINDOWS__))
#define ZT_SALSA20_SSE 1
@@ -31,16 +32,17 @@ class Salsa20
public:
Salsa20() throw() {}
~Salsa20() { Utils::burn(&_state,sizeof(_state)); }
/**
* @param key Key bits
* @param kbits Number of key bits: 128 or 256 (recommended)
* @param iv 64-bit initialization vector
* @param rounds Number of rounds: 8, 12, or 20
*/
Salsa20(const void *key,unsigned int kbits,const void *iv,unsigned int rounds)
Salsa20(const void *key,unsigned int kbits,const void *iv)
throw()
{
init(key,kbits,iv,rounds);
init(key,kbits,iv);
}
/**
@@ -49,19 +51,28 @@ public:
* @param key Key bits
* @param kbits Number of key bits: 128 or 256 (recommended)
* @param iv 64-bit initialization vector
* @param rounds Number of rounds: 8, 12, or 20
*/
void init(const void *key,unsigned int kbits,const void *iv,unsigned int rounds)
void init(const void *key,unsigned int kbits,const void *iv)
throw();
/**
* Encrypt data
* Encrypt data using Salsa20/12
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
void encrypt(const void *in,void *out,unsigned int bytes)
void encrypt12(const void *in,void *out,unsigned int bytes)
throw();
/**
* Encrypt data using Salsa20/20
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
void encrypt20(const void *in,void *out,unsigned int bytes)
throw();
/**
@@ -71,10 +82,23 @@ public:
* @param out Output buffer
* @param bytes Length of data
*/
inline void decrypt(const void *in,void *out,unsigned int bytes)
inline void decrypt12(const void *in,void *out,unsigned int bytes)
throw()
{
encrypt(in,out,bytes);
encrypt12(in,out,bytes);
}
/**
* Decrypt data
*
* @param in Input data
* @param out Output buffer
* @param bytes Length of data
*/
inline void decrypt20(const void *in,void *out,unsigned int bytes)
throw()
{
encrypt20(in,out,bytes);
}
private:
@@ -84,7 +108,6 @@ private:
#endif // ZT_SALSA20_SSE
uint32_t i[16];
} _state;
unsigned int _roundsDiv4;
};
} // namespace ZeroTier

View File

@@ -36,6 +36,7 @@
#include "Topology.hpp"
#include "Packet.hpp"
#include "Peer.hpp"
#include "Switch.hpp"
// Entry timeout -- make it fairly long since this is just to prevent stale buildup
#define ZT_SELFAWARENESS_ENTRY_TIMEOUT 3600000
@@ -65,7 +66,8 @@ private:
};
SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
RR(renv)
RR(renv),
_phy(32)
{
}
@@ -77,66 +79,62 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi
{
const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
// This would be weird, e.g. a public IP talking to 10.0.0.1, so just ignore it.
// If your network is this weird it's probably not reliable information.
if (scope != reporterPhysicalAddress.ipScope())
return;
// Some scopes we ignore, and global scope IPs are only used for this
// mechanism if they come from someone we trust (e.g. a root).
switch(scope) {
case InetAddress::IP_SCOPE_NONE:
case InetAddress::IP_SCOPE_LOOPBACK:
case InetAddress::IP_SCOPE_MULTICAST:
return;
case InetAddress::IP_SCOPE_GLOBAL:
if ((!trusted)||(scope != reporterPhysicalAddress.ipScope()))
if (!trusted)
return;
break;
default:
if (scope != reporterPhysicalAddress.ipScope())
return;
break;
}
Mutex::Lock _l(_phy_m);
PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,reporterPhysicalAddress,scope)];
PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,scope)];
if ((now - entry.ts) >= ZT_SELFAWARENESS_ENTRY_TIMEOUT) {
if ( ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
entry.mySurface = myPhysicalAddress;
entry.ts = now;
TRACE("learned physical address %s for scope %u as seen from %s(%s) (replaced <null>)",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str());
} else if (entry.mySurface != myPhysicalAddress) {
entry.mySurface = myPhysicalAddress;
entry.ts = now;
TRACE("learned physical address %s for scope %u as seen from %s(%s) (replaced %s, resetting all in scope)",myPhysicalAddress.toString().c_str(),(unsigned int)scope,reporter.toString().c_str(),reporterPhysicalAddress.toString().c_str(),entry.mySurface.toString().c_str());
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 (other than this one) for this scope to prevent thrashing
// Note: we should probably not use 'entry' after this
// Erase all entries in this scope that were not reported from this remote address to prevent 'thrashing'
// due to multiple reports of endpoint change.
// Don't use 'entry' after this since hash table gets modified.
{
Hashtable< PhySurfaceKey,PhySurfaceEntry >::Iterator i(_phy);
PhySurfaceKey *k = (PhySurfaceKey *)0;
PhySurfaceEntry *e = (PhySurfaceEntry *)0;
while (i.next(k,e)) {
if ((k->reporter != reporter)&&(k->scope == scope))
if ((k->reporterPhysicalAddress != reporterPhysicalAddress)&&(k->scope == scope))
_phy.erase(*k);
}
}
// Reset all paths within this scope
_ResetWithinScope rset(RR,now,(InetAddress::IpScope)scope);
RR->topology->eachPeer<_ResetWithinScope &>(rset);
// For all peers for whom we forgot an address, send a packet indirectly if
// they are still considered alive so that we will re-establish direct links.
SharedPtr<Peer> sn(RR->topology->getBestRoot());
if (sn) {
RemotePath *snp = sn->getBestPath(now);
if (snp) {
for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
if ((*p)->alive(now)) {
TRACE("sending indirect NOP to %s via %s(%s) to re-establish link",(*p)->address().toString().c_str(),sn->address().toString().c_str(),snp->address().toString().c_str());
Packet outp((*p)->address(),RR->identity.address(),Packet::VERB_NOP);
outp.armor((*p)->key(),true);
snp->send(RR,outp.data(),outp.size(),now);
}
}
// 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 {
entry.mySurface = myPhysicalAddress;
entry.ts = now;
}
}

View File

@@ -69,14 +69,14 @@ private:
struct PhySurfaceKey
{
Address reporter;
InetAddress reporterPhysicalAddress;
InetAddress::IpScope scope;
inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
PhySurfaceKey(const Address &r,InetAddress::IpScope s) : reporter(r),scope(s) {}
inline bool operator<(const PhySurfaceKey &k) const throw() { return ((reporter < k.reporter) ? true : ((reporter == k.reporter) ? ((int)scope < (int)k.scope) : false)); }
inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(scope == k.scope)); }
PhySurfaceKey(const Address &r,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),reporterPhysicalAddress(ra),scope(s) {}
inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
};
struct PhySurfaceEntry
{

View File

@@ -64,20 +64,6 @@ public:
++obj->__refCount;
}
SharedPtr(T *obj,bool runAwayFromZombies)
throw() :
_ptr(obj)
{
// HACK: this is used in "handlers" to take ownership of naked pointers,
// an ugly pattern that really ought to be factored out.
if (runAwayFromZombies) {
if ((int)(++obj->__refCount) < 2) {
--obj->__refCount;
_ptr = (T *)0;
}
} else ++obj->__refCount;
}
SharedPtr(const SharedPtr &sp)
throw() :
_ptr(sp._getAndInc())
@@ -105,6 +91,25 @@ public:
return *this;
}
/**
* Set to a naked pointer and increment its reference count
*
* This assumes this SharedPtr is NULL and that ptr is not a 'zombie.' No
* checks are performed.
*
* @param ptr Naked pointer to assign
*/
inline void setToUnsafe(T *ptr)
{
++ptr->__refCount;
_ptr = ptr;
}
/**
* Swap with another pointer 'for free' without ref count overhead
*
* @param with Pointer to swap with
*/
inline void swap(SharedPtr &with)
throw()
{

View File

@@ -45,6 +45,7 @@
#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
#include "Cluster.hpp"
namespace ZeroTier {
@@ -153,25 +154,84 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
MulticastGroup mg(to,0);
if (to.isBroadcast()) {
if (
(etherType == ZT_ETHERTYPE_ARP)&&
(len >= 28)&&
(
(((const unsigned char *)data)[2] == 0x08)&&
(((const unsigned char *)data)[3] == 0x00)&&
(((const unsigned char *)data)[4] == 6)&&
(((const unsigned char *)data)[5] == 4)&&
(((const unsigned char *)data)[7] == 0x01)
)
) {
// Cram IPv4 IP into ADI field to make IPv4 ARP broadcast channel specific and scalable
// Also: enableBroadcast() does not apply to ARP since it's required for IPv4
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)) ) {
/* IPv4 ARP is one of the few special cases that we impose upon what is
* otherwise a straightforward Ethernet switch emulation. Vanilla ARP
* is dumb old broadcast and simply doesn't scale. ZeroTier multicast
* groups have an additional field called ADI (additional distinguishing
* information) which was added specifically for ARP though it could
* be used for other things too. We then take ARP broadcasts and turn
* 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. */
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
} else if (!nconf->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 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(std::vector<InetAddress>::const_iterator sip(nconf->staticIps().begin()),sipend(nconf->staticIps().end());sip!=sipend;++sip) {
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) { // /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(),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
}
}
}
}
}
}
}
/* Learn multicast groups for bridged-in hosts.
@@ -203,7 +263,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
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));
const bool includeCom = ((!toPeer)||(toPeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true)));;
const bool includeCom = ( (nconf->isPrivate()) && (nconf->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());
@@ -271,7 +331,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
SharedPtr<Peer> bridgePeer(RR->topology->getPeer(bridges[b]));
Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
outp.append(network->id());
if ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) {
if ( (nconf->isPrivate()) && (nconf->com()) && ((!bridgePeer)||(bridgePeer->needsOurNetworkMembershipCertificate(network->id(),RR->node->now(),true))) ) {
outp.append((unsigned char)0x01); // 0x01 -- COM included
nconf->com().serialize(outp);
} else {
@@ -294,17 +354,18 @@ void Switch::send(const Packet &packet,bool encrypt,uint64_t nwid)
return;
}
//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,nwid));
}
}
bool Switch::unite(const Address &p1,const Address &p2,bool force)
bool Switch::unite(const Address &p1,const Address &p2)
{
if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
return false;
SharedPtr<Peer> p1p = RR->topology->getPeer(p1);
if (!p1p)
return false;
@@ -314,14 +375,6 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
const uint64_t now = RR->node->now();
{
Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(p1,p2)];
if (((now - luts) < ZT_MIN_UNITE_INTERVAL)&&(!force))
return false;
luts = now;
}
std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope()))
return false;
@@ -382,7 +435,7 @@ void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr
{
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->attemptToContactAt(RR,localAddr,atAddr,now);
peer->sendHELLO(RR,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));
@@ -422,7 +475,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
{ // finish processing any packets waiting on peer's public key / identity
Mutex::Lock _l(_rxQueue_m);
for(std::list< SharedPtr<IncomingPacket> >::iterator rxi(_rxQueue.begin());rxi!=_rxQueue.end();) {
if ((*rxi)->tryDecode(RR))
if ((*rxi)->tryDecode(RR,false))
_rxQueue.erase(rxi++);
else ++rxi;
}
@@ -448,21 +501,21 @@ unsigned long Switch::doTimerTasks(uint64_t now)
Mutex::Lock _l(_contactQueue_m);
for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
if (now >= qi->fireAtTime) {
if ((!qi->peer->alive(now))||(qi->peer->hasActiveDirectPath(now))) {
// Cancel attempt if we've already connected or peer is no longer "alive"
if (qi->peer->hasActiveDirectPath(now)) {
// Cancel if connection has succeeded
_contactQueue.erase(qi++);
continue;
} else {
if (qi->strategyIteration == 0) {
// First strategy: send packet directly to destination
qi->peer->attemptToContactAt(RR,qi->localAddr,qi->inaddr,now);
} else if (qi->strategyIteration <= 4) {
// Strategies 1-4: try escalating ports for symmetric NATs that remap sequentially
qi->peer->sendHELLO(RR,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 < 0xffff) {
tmpaddr.setPort((unsigned int)p);
qi->peer->attemptToContactAt(RR,qi->localAddr,tmpaddr,now);
qi->peer->sendHELLO(RR,qi->localAddr,tmpaddr,now);
} else qi->strategyIteration = 5;
} else {
// All strategies tried, expire entry
@@ -545,7 +598,7 @@ unsigned long Switch::doTimerTasks(uint64_t now)
_LastUniteKey *k = (_LastUniteKey *)0;
uint64_t *v = (uint64_t *)0;
while (i.next(k,v)) {
if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 16))
if ((now - *v) >= (ZT_MIN_UNITE_INTERVAL * 8))
_lastUniteAttempt.erase(*k);
}
}
@@ -567,6 +620,13 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
// It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster) {
RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
return;
}
#endif
// Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRoot();
if (relayTo)
@@ -614,7 +674,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
packet->append(dq.frags[f - 1].payload(),dq.frags[f - 1].payloadLength());
_defragQueue.erase(pid); // dq no longer valid after this
if (!packet->tryDecode(RR)) {
if (!packet->tryDecode(RR,false)) {
Mutex::Lock _l(_rxQueue_m);
_rxQueue.push_back(packet);
}
@@ -626,11 +686,17 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
{
SharedPtr<IncomingPacket> packet(new IncomingPacket(data,len,localAddr,fromAddr,RR->node->now()));
const uint64_t now = RR->node->now();
SharedPtr<IncomingPacket> packet(new IncomingPacket(data,len,localAddr,fromAddr,now));
Address source(packet->source());
Address destination(packet->destination());
// Catch this and toss it -- it would never work, but it could happen if we somehow
// mistakenly guessed an address we're bound to as a destination for another peer.
if (source == RR->identity.address())
return;
//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()) {
@@ -639,13 +705,32 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr
packet->incrementHops();
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
unite(source,destination,false);
if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),now)))) {
Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
luts = now;
unite(source,destination);
}
} else {
// Don't know peer or no direct path -- so relay via root server
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster) {
bool shouldUnite;
{
Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
shouldUnite = ((now - luts) >= ZT_MIN_UNITE_INTERVAL);
if (shouldUnite)
luts = now;
}
RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size(),shouldUnite);
return;
}
#endif
relayTo = RR->topology->getBestRoot(&source,1,true);
if (relayTo)
relayTo->send(RR,packet->data(),packet->size(),RR->node->now());
relayTo->send(RR,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());
@@ -660,7 +745,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr
if (!dq.creationTime) {
// If we have no other fragments yet, create an entry and save the head
dq.creationTime = RR->node->now();
dq.creationTime = now;
dq.frag0 = packet;
dq.totalFragments = 0; // 0 == unknown, waiting for Packet::Fragment
dq.haveFragments = 1; // head is first bit (left to right)
@@ -677,7 +762,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr
packet->append(dq.frags[f - 1].payload(),dq.frags[f - 1].payloadLength());
_defragQueue.erase(pid); // dq no longer valid after this
if (!packet->tryDecode(RR)) {
if (!packet->tryDecode(RR,false)) {
Mutex::Lock _l(_rxQueue_m);
_rxQueue.push_back(packet);
}
@@ -688,7 +773,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr
} // else this is a duplicate head, ignore
} else {
// Packet is unfragmented, so just process it
if (!packet->tryDecode(RR)) {
if (!packet->tryDecode(RR,false)) {
Mutex::Lock _l(_rxQueue_m);
_rxQueue.push_back(packet);
}
@@ -726,17 +811,20 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
return false; // sanity check: unconfigured network? why are we trying to talk to it?
}
RemotePath *viaPath = peer->getBestPath(now);
Path *viaPath = peer->getBestPath(now);
SharedPtr<Peer> relay;
if (!viaPath) {
// See if this network has a preferred relay (if packet has an associated network)
if (nconf) {
unsigned int latency = ~((unsigned int)0);
unsigned int bestq = ~((unsigned int)0);
for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
if (r->first != peer->address()) {
SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
const unsigned int q = rp->relayQuality(now);
if ((rp)&&(q < bestq)) { // SUBTILE: < == don't use these if they are nil quality (unsigned int max), instead use a root
bestq = q;
rp.swap(relay);
}
}
}
}

View File

@@ -117,15 +117,10 @@ public:
* This only works if both peers are known, with known working direct
* links to this peer. The best link for each peer is sent to the other.
*
* A rate limiter is in effect via the _lastUniteAttempt map. If force
* is true, a unite attempt is made even if one has been made less than
* ZT_MIN_UNITE_INTERVAL milliseconds ago.
*
* @param p1 One of two peers (order doesn't matter)
* @param p2 Second of pair
* @param force If true, send now regardless of interval
*/
bool unite(const Address &p1,const Address &p2,bool force);
bool unite(const Address &p1,const Address &p2);
/**
* Attempt NAT traversal to peer at a given physical address

View File

@@ -28,13 +28,21 @@
#include "Constants.hpp"
#include "Topology.hpp"
#include "RuntimeEnvironment.hpp"
#include "Defaults.hpp"
#include "Dictionary.hpp"
#include "Node.hpp"
#include "Network.hpp"
#include "NetworkConfig.hpp"
#include "Buffer.hpp"
namespace ZeroTier {
// 2015-11-16 -- The Fabulous Four (should have named them after Beatles!)
//#define ZT_DEFAULT_WORLD_LENGTH 494
//static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x11,0x70,0xb2,0xfb,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0x80,0x31,0xa4,0x65,0x95,0x45,0x06,0x1c,0xfb,0xc2,0x4e,0x5d,0xe7,0x0a,0x40,0x7a,0x97,0xce,0x36,0xa2,0x3d,0x05,0xca,0x87,0xc7,0x59,0x27,0x5c,0x8b,0x0d,0x4c,0xb4,0xbb,0x26,0x2f,0x77,0x17,0x5e,0xb7,0x4d,0xb8,0xd3,0xb4,0xe9,0x23,0x5d,0xcc,0xa2,0x71,0xa8,0xdf,0xf1,0x23,0xa3,0xb2,0x66,0x74,0xea,0xe5,0xdc,0x8d,0xef,0xd3,0x0a,0xa9,0xac,0xcb,0xda,0x93,0xbd,0x6c,0xcd,0x43,0x1d,0xa7,0x98,0x6a,0xde,0x70,0xc0,0xc6,0x1c,0xaf,0xf0,0xfd,0x7f,0x8a,0xb9,0x76,0x13,0xe1,0xde,0x4f,0xf3,0xd6,0x13,0x04,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x01,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x01,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09};
// 2015-11-20 -- Alice and Bob are live, and we're now IPv6 dual-stack!
#define ZT_DEFAULT_WORLD_LENGTH 792
static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x51,0x26,0x6f,0x7c,0x8a,0xb8,0xb3,0x88,0xa4,0x69,0x22,0x14,0x91,0xaa,0x9a,0xcd,0x66,0xcc,0x76,0x4c,0xde,0xfd,0x56,0x03,0x9f,0x10,0x67,0xae,0x15,0xe6,0x9c,0x6f,0xb4,0x2d,0x7b,0x55,0x33,0x0e,0x3f,0xda,0xac,0x52,0x9c,0x07,0x92,0xfd,0x73,0x40,0xa6,0xaa,0x21,0xab,0xa8,0xa4,0x89,0xfd,0xae,0xa4,0x4a,0x39,0xbf,0x2d,0x00,0x65,0x9a,0xc9,0xc8,0x18,0xeb,0xe8,0x0a,0xf5,0xbc,0xf8,0x3d,0x97,0xcd,0xc3,0xf8,0xe2,0x41,0x16,0x42,0x0f,0xc7,0x76,0x8e,0x07,0xf3,0x7e,0x9e,0x7d,0x1b,0xb3,0x23,0x21,0x79,0xce,0xb9,0xd0,0xcb,0xb5,0x94,0x7b,0x89,0x21,0x57,0x72,0xf6,0x70,0xa1,0xdd,0x67,0x38,0xcf,0x45,0x45,0xc2,0x8d,0x46,0xec,0x00,0x2c,0xe0,0x2a,0x63,0x3f,0x63,0x8d,0x33,0x08,0x51,0x07,0x77,0x81,0x5b,0x32,0x49,0xae,0x87,0x89,0xcf,0x31,0xaa,0x41,0xf1,0x52,0x97,0xdc,0xa2,0x55,0xe1,0x4a,0x6e,0x3c,0x04,0xf0,0x4f,0x8a,0x0e,0xe9,0xca,0xec,0x24,0x30,0x04,0x9d,0x21,0x90,0x39,0xf3,0x00,0x01,0xf0,0x92,0x2a,0x98,0xe3,0xb3,0x4e,0xbc,0xbf,0xf3,0x33,0x26,0x9d,0xc2,0x65,0xd7,0xa0,0x20,0xaa,0xb6,0x9d,0x72,0xbe,0x4d,0x4a,0xcc,0x9c,0x8c,0x92,0x94,0x78,0x57,0x71,0x25,0x6c,0xd1,0xd9,0x42,0xa9,0x0d,0x1b,0xd1,0xd2,0xdc,0xa3,0xea,0x84,0xef,0x7d,0x85,0xaf,0xe6,0x61,0x1f,0xb4,0x3f,0xf0,0xb7,0x41,0x26,0xd9,0x0a,0x6e,0x00,0x0c,0x04,0xbc,0xa6,0x5e,0xb1,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x02,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x7d,0x00,0x01,0x27,0x09,0x04,0x9a,0x42,0xc5,0x21,0x27,0x09,0x06,0x2c,0x0f,0xf8,0x50,0x01,0x54,0x01,0x97,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x27,0x09,0x04,0x9f,0xcb,0x61,0xab,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x08,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,0x00,0x54,0x60,0x01,0x27,0x09,0x04,0xa9,0x39,0x8f,0x68,0x27,0x09,0x06,0x26,0x07,0xf0,0xd0,0x1d,0x01,0x00,0x57,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x27,0x09,0x04,0x6b,0xaa,0xc5,0x0e,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x00,0x01,0x00,0x20,0x00,0x00,0x00,0x00,0x02,0x00,0xe0,0x01,0x27,0x09,0x04,0x80,0xc7,0xc5,0xd9,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0xb7,0x40,0x01,0x27,0x09,0x88,0x41,0x40,0x8a,0x2e,0x00,0xbb,0x1d,0x31,0xf2,0xc3,0x23,0xe2,0x64,0xe9,0xe6,0x41,0x72,0xc1,0xa7,0x4f,0x77,0x89,0x95,0x55,0xed,0x10,0x75,0x1c,0xd5,0x6e,0x86,0x40,0x5c,0xde,0x11,0x8d,0x02,0xdf,0xfe,0x55,0x5d,0x46,0x2c,0xcf,0x6a,0x85,0xb5,0x63,0x1c,0x12,0x35,0x0c,0x8d,0x5d,0xc4,0x09,0xba,0x10,0xb9,0x02,0x5d,0x0f,0x44,0x5c,0xf4,0x49,0xd9,0x2b,0x1c,0x00,0x0c,0x04,0x2d,0x20,0xc6,0x82,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x64,0x00,0x81,0xc3,0x54,0x00,0x00,0xff,0xfe,0x18,0x1d,0x61,0x27,0x09,0x04,0x2e,0x65,0xa0,0xf9,0x27,0x09,0x06,0x2a,0x03,0xb0,0xc0,0x00,0x03,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x6a,0x30,0x01,0x27,0x09,0x04,0x6b,0xbf,0x2e,0xd2,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x68,0x00,0x83,0xa4,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x64,0x27,0x09,0x04,0x2d,0x20,0xf6,0xb3,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x58,0x00,0x8b,0xf8,0x54,0x00,0x00,0xff,0xfe,0x15,0xb3,0x9a,0x27,0x09,0x04,0x2d,0x20,0xf8,0x57,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0x70,0x00,0x9b,0xc9,0x54,0x00,0x00,0xff,0xfe,0x15,0xc4,0xf5,0x27,0x09,0x04,0x9f,0xcb,0x02,0x9a,0x27,0x09,0x06,0x26,0x04,0xa8,0x80,0x0c,0xad,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x26,0x70,0x01,0x27,0x09,0x7e,0x19,0x87,0x6a,0xba,0x00,0x2a,0x6e,0x2b,0x23,0x18,0x93,0x0f,0x60,0xeb,0x09,0x7f,0x70,0xd0,0xf4,0xb0,0x28,0xb2,0xcd,0x6d,0x3d,0x0c,0x63,0xc0,0x14,0xb9,0x03,0x9f,0xf3,0x53,0x90,0xe4,0x11,0x81,0xf2,0x16,0xfb,0x2e,0x6f,0xa8,0xd9,0x5c,0x1e,0xe9,0x66,0x71,0x56,0x41,0x19,0x05,0xc3,0xdc,0xcf,0xea,0x78,0xd8,0xc6,0xdf,0xaf,0xba,0x68,0x81,0x70,0xb3,0xfa,0x00,0x01,0x04,0xc6,0xc7,0x61,0xdc,0x27,0x09,0x8a,0xcf,0x05,0x9f,0xe3,0x00,0x48,0x2f,0x6e,0xe5,0xdf,0xe9,0x02,0x31,0x9b,0x41,0x9d,0xe5,0xbd,0xc7,0x65,0x20,0x9c,0x0e,0xcd,0xa3,0x8c,0x4d,0x6e,0x4f,0xcf,0x0d,0x33,0x65,0x83,0x98,0xb4,0x52,0x7d,0xcd,0x22,0xf9,0x31,0x12,0xfb,0x9b,0xef,0xd0,0x2f,0xd7,0x8b,0xf7,0x26,0x1b,0x33,0x3f,0xc1,0x05,0xd1,0x92,0xa6,0x23,0xca,0x9e,0x50,0xfc,0x60,0xb3,0x74,0xa5,0x00,0x01,0x04,0xa2,0xf3,0x4d,0x6f,0x27,0x09};
Topology::Topology(const RuntimeEnvironment *renv) :
RR(renv),
_amRoot(false)
@@ -43,130 +51,110 @@ Topology::Topology(const RuntimeEnvironment *renv) :
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()) {
// Each Peer serializes itself prefixed by a record length (not including the size of the length itself)
unsigned int reclen = (unsigned int)all[ptr] & 0xff;
reclen <<= 8;
reclen |= (unsigned int)all[ptr + 1] & 0xff;
reclen <<= 8;
reclen |= (unsigned int)all[ptr + 2] & 0xff;
reclen <<= 8;
reclen |= (unsigned int)all[ptr + 3] & 0xff;
if (((ptr + reclen) > alls.size())||(reclen > ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE))
break;
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;
SharedPtr<Peer> p(Peer::deserializeNew(RR->identity,Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE>(all + ptr,reclen),pos));
if (pos != reclen)
break;
deserializeBuf->copyFrom(all + ptr,reclen + 4);
SharedPtr<Peer> p(Peer::deserializeNew(RR->identity,*deserializeBuf,pos));
ptr += pos;
if ((p)&&(p->address() != RR->identity.address())) {
_peers[p->address()] = p;
} else {
if (!p)
break; // stop if invalid records
}
} catch (std::exception &exc) {
break;
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) {
try {
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),(unsigned int)dsWorld.length());
cachedWorld.deserialize(dswtmp,0);
} catch ( ... ) {
cachedWorld = World(); // clear if cached world is invalid
}
}
World defaultWorld;
{
Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
}
if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) {
_setWorld(defaultWorld);
if (dsWorld.length() > 0)
RR->node->dataStoreDelete("world");
} else _setWorld(cachedWorld);
}
Topology::~Topology()
{
Buffer<ZT_PEER_SUGGESTED_SERIALIZATION_BUFFER_SIZE> pbuf;
std::string all;
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);
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 {
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);
}
void Topology::setRootServers(const std::map< Identity,std::vector<InetAddress> > &sn)
{
Mutex::Lock _l(_lock);
if (_roots == sn)
return; // no change
_roots = sn;
_rootAddresses.clear();
_rootPeers.clear();
const uint64_t now = RR->node->now();
for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
if (i->first != RR->identity) { // do not add self as a peer
SharedPtr<Peer> &p = _peers[i->first.address()];
if (!p)
p = SharedPtr<Peer>(new Peer(RR->identity,i->first));
for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
p->addPath(RemotePath(InetAddress(),*j,true),now);
p->use(now);
_rootPeers.push_back(p);
}
_rootAddresses.push_back(i->first.address());
}
std::sort(_rootAddresses.begin(),_rootAddresses.end());
_amRoot = (_roots.find(RR->identity) != _roots.end());
}
void Topology::setRootServers(const Dictionary &sn)
{
std::map< Identity,std::vector<InetAddress> > m;
for(Dictionary::const_iterator d(sn.begin());d!=sn.end();++d) {
if ((d->first.length() == ZT_ADDRESS_LENGTH_HEX)&&(d->second.length() > 0)) {
try {
Dictionary snspec(d->second);
std::vector<InetAddress> &a = m[Identity(snspec.get("id",""))];
std::string udp(snspec.get("udp",std::string()));
if (udp.length() > 0)
a.push_back(InetAddress(udp));
} catch ( ... ) {
TRACE("root server list contained invalid entry for: %s",d->first.c_str());
(*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;
}
this->setRootServers(m);
}
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
{
if (peer->address() == RR->identity.address()) {
TRACE("BUG: addNewPeer() caught and ignored attempt to add peer for self");
throw std::logic_error("cannot add peer for self");
#ifdef ZT_TRACE
if ((!peer)||(peer->address() == RR->identity.address())) {
if (!peer)
fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add NULL peer" ZT_EOL_S);
else fprintf(stderr,"FATAL BUG: addPeer() caught attempt to add peer for self" ZT_EOL_S);
abort();
}
#endif
SharedPtr<Peer> np;
{
Mutex::Lock _l(_lock);
SharedPtr<Peer> &hp = _peers[peer->address()];
if (!hp)
hp = peer;
np = hp;
}
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock);
np->use(RR->node->now());
saveIdentity(np->identity());
SharedPtr<Peer> &p = _peers.set(peer->address(),peer);
p->use(now);
_saveIdentity(p->identity());
return p;
return np;
}
SharedPtr<Peer> Topology::getPeer(const Address &zta)
@@ -176,33 +164,58 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
return SharedPtr<Peer>();
}
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock);
SharedPtr<Peer> &ap = _peers[zta];
if (ap) {
ap->use(now);
return ap;
{
Mutex::Lock _l(_lock);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap) {
(*ap)->use(RR->node->now());
return *ap;
}
}
Identity id(_getIdentity(zta));
if (id) {
try {
ap = SharedPtr<Peer>(new Peer(RR->identity,id));
ap->use(now);
return ap;
} catch ( ... ) {} // invalid identity?
}
_peers.erase(zta);
try {
Identity id(_getIdentity(zta));
if (id) {
SharedPtr<Peer> np(new Peer(RR->identity,id));
{
Mutex::Lock _l(_lock);
SharedPtr<Peer> &ap = _peers[zta];
if (!ap)
ap.swap(np);
ap->use(RR->node->now());
return ap;
}
}
} catch ( ... ) {
fprintf(stderr,"EXCEPTION in getPeer() part 2\n");
abort();
} // invalid identity on disk?
return SharedPtr<Peer>();
}
Identity Topology::getIdentity(const Address &zta)
{
{
Mutex::Lock _l(_lock);
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return (*ap)->identity();
}
return _getIdentity(zta);
}
void Topology::saveIdentity(const Identity &id)
{
if (id) {
char p[128];
Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
RR->node->dataStorePut(p,id.toString(false),false);
}
}
SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
{
SharedPtr<Peer> bestRoot;
const uint64_t now = RR->node->now();
Mutex::Lock _l(_lock);
@@ -212,97 +225,92 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
* causes packets searching for a route to pretty much literally
* circumnavigate the globe rather than bouncing between just two. */
if (_rootAddresses.size() > 1) { // gotta be one other than me for this to work
std::vector<Address>::const_iterator sna(std::find(_rootAddresses.begin(),_rootAddresses.end(),RR->identity.address()));
if (sna != _rootAddresses.end()) { // sanity check -- _amRoot should've been false in this case
for(;;) {
if (++sna == _rootAddresses.end())
sna = _rootAddresses.begin(); // wrap around at end
if (*sna != RR->identity.address()) { // pick one other than us -- starting from me+1 in sorted set order
SharedPtr<Peer> *p = _peers.get(*sna);
if ((p)&&((*p)->hasActiveDirectPath(now))) {
bestRoot = *p;
break;
}
for(unsigned long p=0;p<_rootAddresses.size();++p) {
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))) {
(*nextsn)->use(now);
return *nextsn;
}
}
break;
}
}
} else {
/* If I am not a root server, the best root server is the active one with
* the lowest latency. */
* the lowest quality score. (lower == better) */
unsigned int l,bestLatency = 65536;
uint64_t lds,ldr;
unsigned int bestQualityOverall = ~((unsigned int)0);
unsigned int bestQualityNotAvoid = ~((unsigned int)0);
const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
// First look for a best root by comparing latencies, but exclude
// root servers that have not responded to direct messages in order to
// try to exclude any that are dead or unreachable.
for(std::vector< SharedPtr<Peer> >::const_iterator sn(_rootPeers.begin());sn!=_rootPeers.end();) {
// Skip explicitly avoided relays
for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
bool avoiding = false;
for(unsigned int i=0;i<avoidCount;++i) {
if (avoid[i] == (*sn)->address())
goto keep_searching_for_roots;
}
// Skip possibly comatose or unreachable relays
lds = (*sn)->lastDirectSend();
ldr = (*sn)->lastDirectReceive();
if ((lds)&&(lds > ldr)&&((lds - ldr) > ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD))
goto keep_searching_for_roots;
if ((*sn)->hasActiveDirectPath(now)) {
l = (*sn)->latency();
if (bestRoot) {
if ((l)&&(l < bestLatency)) {
bestLatency = l;
bestRoot = *sn;
}
} else {
if (l)
bestLatency = l;
bestRoot = *sn;
if (avoid[i] == (*r)->address()) {
avoiding = true;
break;
}
}
keep_searching_for_roots:
++sn;
const unsigned int q = (*r)->relayQuality(now);
if (q <= bestQualityOverall) {
bestQualityOverall = q;
bestOverall = &(*r);
}
if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
bestQualityNotAvoid = q;
bestNotAvoid = &(*r);
}
}
if (bestRoot) {
bestRoot->use(now);
return bestRoot;
} else if (strictAvoid)
return SharedPtr<Peer>();
if (bestNotAvoid) {
(*bestNotAvoid)->use(now);
return *bestNotAvoid;
} else if ((!strictAvoid)&&(bestOverall)) {
(*bestOverall)->use(now);
return *bestOverall;
}
// If we have nothing from above, just pick one without avoidance criteria.
for(std::vector< SharedPtr<Peer> >::const_iterator sn=_rootPeers.begin();sn!=_rootPeers.end();++sn) {
if ((*sn)->hasActiveDirectPath(now)) {
unsigned int l = (*sn)->latency();
if (bestRoot) {
if ((l)&&(l < bestLatency)) {
bestLatency = l;
bestRoot = *sn;
}
} else {
if (l)
bestLatency = l;
bestRoot = *sn;
}
}
return SharedPtr<Peer>();
}
bool Topology::isUpstream(const Identity &id) const
{
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) {
SharedPtr<NetworkConfig> nc((*nw)->config2());
if (nc) {
for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nc->relays().begin());r!=nc->relays().end();++r) {
if (r->first == id.address())
return true;
}
}
}
if (bestRoot)
bestRoot->use(now);
return bestRoot;
return false;
}
bool Topology::isRoot(const Identity &id) const
throw()
bool Topology::worldUpdateIfValid(const World &newWorld)
{
Mutex::Lock _l(_lock);
return (_roots.count(id) != 0);
if (_world.shouldBeReplacedBy(newWorld,true)) {
_setWorld(newWorld);
try {
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
newWorld.serialize(dswtmp,false);
RR->node->dataStorePut("world",dswtmp.data(),dswtmp.size(),false);
} catch ( ... ) {
RR->node->dataStoreDelete("world");
}
return true;
}
return false;
}
void Topology::clean(uint64_t now)
@@ -320,24 +328,6 @@ void Topology::clean(uint64_t now)
}
}
bool Topology::authenticateRootTopology(const Dictionary &rt)
{
try {
std::string signer(rt.signingIdentity());
if (!signer.length())
return false;
Identity signerId(signer);
std::map< Address,Identity >::const_iterator authority(ZT_DEFAULTS.rootTopologyAuthorities.find(signerId.address()));
if (authority == ZT_DEFAULTS.rootTopologyAuthorities.end())
return false;
if (signerId != authority->second)
return false;
return rt.verify(authority->second);
} catch ( ... ) {
return false;
}
}
Identity Topology::_getIdentity(const Address &zta)
{
char p[128];
@@ -351,12 +341,27 @@ Identity Topology::_getIdentity(const Address &zta)
return Identity();
}
void Topology::_saveIdentity(const Identity &id)
void Topology::_setWorld(const World &newWorld)
{
if (id) {
char p[128];
Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
RR->node->dataStorePut(p,id.toString(false),false);
// assumed _lock is locked (or in constructor)
_world = newWorld;
_amRoot = false;
_rootAddresses.clear();
_rootPeers.clear();
for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
_rootAddresses.push_back(r->identity.address());
if (r->identity.address() == RR->identity.address()) {
_amRoot = true;
} else {
SharedPtr<Peer> *rp = _peers.get(r->identity.address());
if (rp) {
_rootPeers.push_back(*rp);
} else {
SharedPtr<Peer> newrp(new Peer(RR->identity,r->identity));
_peers.set(r->identity.address(),newrp);
_rootPeers.push_back(newrp);
}
}
}
}

View File

@@ -31,10 +31,10 @@
#include <stdio.h>
#include <string.h>
#include <map>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <utility>
#include "Constants.hpp"
@@ -43,8 +43,8 @@
#include "Peer.hpp"
#include "Mutex.hpp"
#include "InetAddress.hpp"
#include "Dictionary.hpp"
#include "Hashtable.hpp"
#include "World.hpp"
namespace ZeroTier {
@@ -59,21 +59,6 @@ public:
Topology(const RuntimeEnvironment *renv);
~Topology();
/**
* @param sn Root server identities and addresses
*/
void setRootServers(const std::map< Identity,std::vector<InetAddress> > &sn);
/**
* Set up root servers for this network
*
* This performs no signature verification of any kind. The caller must
* check the signature of the root topology dictionary first.
*
* @param sn 'rootservers' key from root-topology Dictionary (deserialized as Dictionary)
*/
void setRootServers(const Dictionary &sn);
/**
* Add a peer to database
*
@@ -94,23 +79,48 @@ public:
SharedPtr<Peer> getPeer(const Address &zta);
/**
* @return Vector of peers that are root servers
* Get a peer only if it is presently in memory (no disk cache)
*
* This also does not update the lastUsed() time for peers, which means
* that it won't prevent them from falling out of RAM. This is currently
* used in the Cluster code to update peer info without forcing all peers
* across the entire cluster to remain in memory cache.
*
* @param zta ZeroTier address
*/
inline std::vector< SharedPtr<Peer> > rootPeers() const
inline SharedPtr<Peer> getPeerNoCache(const Address &zta)
{
Mutex::Lock _l(_lock);
return _rootPeers;
const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
return *ap;
return SharedPtr<Peer>();
}
/**
* Get the identity of a peer
*
* @param zta ZeroTier address of peer
* @return Identity or NULL Identity if not found
*/
Identity getIdentity(const Address &zta);
/**
* Cache an identity
*
* This is done automatically on addPeer(), and so is only useful for
* cluster identity replication.
*
* @param id Identity to cache
*/
void saveIdentity(const Identity &id);
/**
* Get the current favorite root server
*
* @return Root server with lowest latency or NULL if none
*/
inline SharedPtr<Peer> getBestRoot()
{
return getBestRoot((const Address *)0,0,false);
}
inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
/**
* Get the best root server, avoiding root servers listed in an array
@@ -128,10 +138,19 @@ public:
/**
* @param id Identity to check
* @return True if this is a designated root server
* @return True if this is a designated root server in this world
*/
bool isRoot(const Identity &id) const
throw();
inline bool isRoot(const Identity &id) const
{
Mutex::Lock _l(_lock);
return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
}
/**
* @param id Identity to check
* @return True if this is a root server or a network preferred relay from one of our networks
*/
bool isUpstream(const Identity &id) const;
/**
* @return Vector of root server addresses
@@ -142,11 +161,61 @@ public:
return _rootAddresses;
}
/**
* @return Current World (copy)
*/
inline World world() const
{
Mutex::Lock _l(_lock);
return _world;
}
/**
* @return Current world ID
*/
inline uint64_t worldId() const
{
return _world.id(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* @return Current world timestamp
*/
inline uint64_t worldTimestamp() const
{
return _world.timestamp(); // safe to read without lock, and used from within eachPeer() so don't lock
}
/**
* Validate new world and update if newer and signature is okay
*
* @param newWorld Potential new world definition revision
* @return True if an update actually occurred
*/
bool worldUpdateIfValid(const World &newWorld);
/**
* Clean and flush database
*/
void clean(uint64_t now);
/**
* @param now Current time
* @return Number of peers with active direct paths
*/
inline unsigned long countActive(uint64_t now) const
{
unsigned long cnt = 0;
Mutex::Lock _l(_lock);
Hashtable< Address,SharedPtr<Peer> >::Iterator i(const_cast<Topology *>(this)->_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
cnt += (unsigned long)((*p)->hasActiveDirectPath(now));
}
return cnt;
}
/**
* Apply a function or function object to all peers
*
@@ -167,12 +236,19 @@ public:
Hashtable< Address,SharedPtr<Peer> >::Iterator i(_peers);
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p))
f(*this,*p);
while (i.next(a,p)) {
#ifdef ZT_TRACE
if (!(*p)) {
fprintf(stderr,"FATAL BUG: eachPeer() caught NULL peer for %s -- peer pointers in Topology should NEVER be NULL" ZT_EOL_S,a->toString().c_str());
abort();
}
#endif
f(*this,*((const SharedPtr<Peer> *)p));
}
}
/**
* @return All currently active peers by address
* @return All currently active peers by address (unsorted)
*/
inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
{
@@ -181,27 +257,23 @@ public:
}
/**
* Validate a root topology dictionary against the identities specified in Defaults
*
* @param rt Root topology dictionary
* @return True if dictionary signature is valid
* @return True if I am a root server in the current World
*/
static bool authenticateRootTopology(const Dictionary &rt);
inline bool amRoot() const throw() { return _amRoot; }
private:
Identity _getIdentity(const Address &zta);
void _saveIdentity(const Identity &id);
void _setWorld(const World &newWorld);
const RuntimeEnvironment *RR;
const RuntimeEnvironment *const RR;
World _world;
Hashtable< Address,SharedPtr<Peer> > _peers;
std::map< Identity,std::vector<InetAddress> > _roots;
std::vector< Address > _rootAddresses;
std::vector< SharedPtr<Peer> > _rootPeers;
bool _amRoot;
Mutex _lock;
bool _amRoot;
};
} // namespace ZeroTier

View File

@@ -168,20 +168,20 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
exit(1);
}
s20.init(s20key,256,s20key,8);
s20.init(s20key,256,s20key);
}
if (!CryptGenRandom(cryptProvider,(DWORD)bytes,(BYTE *)buf)) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() CryptGenRandom failed!\r\n");
exit(1);
}
s20.encrypt(buf,buf,bytes);
s20.encrypt12(buf,buf,bytes);
#else // not __WINDOWS__
#ifdef __UNIX_LIKE__
static char randomBuf[65536];
static char randomBuf[131072];
static unsigned int randomPtr = sizeof(randomBuf);
static int devURandomFd = -1;
static Mutex globalLock;
@@ -191,7 +191,7 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
if (devURandomFd <= 0) {
devURandomFd = ::open("/dev/urandom",O_RDONLY);
if (devURandomFd <= 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\r\n");
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
}
@@ -199,10 +199,16 @@ void Utils::getSecureRandom(void *buf,unsigned int bytes)
for(unsigned int i=0;i<bytes;++i) {
if (randomPtr >= sizeof(randomBuf)) {
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to read from /dev/urandom\r\n");
exit(1);
return;
for(;;) {
if ((int)::read(devURandomFd,randomBuf,sizeof(randomBuf)) != (int)sizeof(randomBuf)) {
::close(devURandomFd);
devURandomFd = ::open("/dev/urandom",O_RDONLY);
if (devURandomFd <= 0) {
fprintf(stderr,"FATAL ERROR: Utils::getSecureRandom() unable to open /dev/urandom\n");
exit(1);
return;
}
} else break;
}
randomPtr = 0;
}

241
node/World.hpp Normal file
View File

@@ -0,0 +1,241 @@
/*
* ZeroTier One - Network Virtualization Everywhere
* Copyright (C) 2011-2015 ZeroTier, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_WORLD_HPP
#define ZT_WORLD_HPP
#include <vector>
#include <string>
#include "Constants.hpp"
#include "InetAddress.hpp"
#include "Identity.hpp"
#include "Buffer.hpp"
#include "C25519.hpp"
/**
* Maximum number of roots (sanity limit, okay to increase)
*
* A given root can (through multi-homing) be distributed across any number of
* physical endpoints, but having more than one is good to permit total failure
* of one root or its withdrawal due to compromise without taking the whole net
* down.
*/
#define ZT_WORLD_MAX_ROOTS 4
/**
* Maximum number of stable endpoints per root (sanity limit, okay to increase)
*/
#define ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT 32
/**
* The (more than) maximum length of a serialized World
*/
#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
/**
* World ID indicating null / empty World object
*/
#define ZT_WORLD_ID_NULL 0
/**
* World ID for a test network with ephemeral or temporary roots
*/
#define ZT_WORLD_ID_TESTNET 1
/**
* World ID for Earth
*
* This is the ID for the ZeroTier World used on planet Earth. It is unrelated
* to the public network 8056c2e21c000001 of the same name. It was chosen
* from Earth's approximate distance from the sun in kilometers.
*/
#define ZT_WORLD_ID_EARTH 149604618
/**
* World ID for Mars -- for future use by SpaceX or others
*/
#define ZT_WORLD_ID_MARS 227883110
namespace ZeroTier {
/**
* A world definition (formerly known as a root topology)
*
* Think of a World as a single data center. Within this data center a set
* of distributed fault tolerant root servers provide stable anchor points
* for a peer to peer network that provides VLAN service. Updates to a world
* definition can be published by signing them with the previous revision's
* signing key, and should be very infrequent.
*
* The maximum data center size is approximately 2.5 cubic light seconds,
* since many protocols have issues with >5s RTT latencies.
*
* ZeroTier operates a World for Earth capable of encompassing the planet, its
* orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
* world ID for Mars and nearby space is defined but not yet used, and a test
* world ID is provided for testing purposes.
*
* If you absolutely must run your own "unofficial" ZeroTier network, please
* define your world IDs above 0xffffffff (4294967295). Code to make a World
* is in mkworld.cpp in the parent directory and must be edited to change
* settings.
*/
class World
{
public:
struct Root
{
Identity identity;
std::vector<InetAddress> stableEndpoints;
inline bool operator==(const Root &r) const throw() { return ((identity == r.identity)&&(stableEndpoints == r.stableEndpoints)); }
inline bool operator!=(const Root &r) const throw() { return (!(*this == r)); }
inline bool operator<(const Root &r) const throw() { return (identity < r.identity); } // for sorting
};
/**
* Construct an empty / null World
*/
World() :
_id(ZT_WORLD_ID_NULL),
_ts(0) {}
/**
* @return Root servers for this world and their stable endpoints
*/
inline const std::vector<World::Root> &roots() const throw() { return _roots; }
/**
* @return World unique identifier
*/
inline uint64_t id() const throw() { return _id; }
/**
* @return World definition timestamp
*/
inline uint64_t timestamp() const throw() { return _ts; }
/**
* Check whether a world update should replace this one
*
* A new world update is valid if it is for the same world ID, is newer,
* and is signed by the current world's signing key. If this world object
* is null, it can always be updated.
*
* @param update Candidate update
* @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
* @return True if update is newer than current and is properly signed
*/
inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck)
{
if (_id == ZT_WORLD_ID_NULL)
return true;
if ((_id == update._id)&&(_ts < update._ts)) {
if (fullSignatureCheck) {
Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
update.serialize(tmp,true);
return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
} else return true;
}
return false;
}
/**
* @return True if this World is non-empty
*/
inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
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); // version -- only one valid value for now
b.append((uint64_t)_id);
b.append((uint64_t)_ts);
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());
for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
r->identity.serialize(b);
b.append((uint8_t)r->stableEndpoints.size());
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);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
_roots.clear();
if (b[p++] != 0x01)
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(_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)
throw std::invalid_argument("too many roots in World");
for(unsigned int k=0;k<numRoots;++k) {
_roots.push_back(Root());
Root &r = _roots.back();
p += r.identity.deserialize(b,p);
unsigned int numStableEndpoints = b[p++];
if (numStableEndpoints > ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)
throw std::invalid_argument("too many stable endpoints in World/Root");
for(unsigned int kk=0;kk<numStableEndpoints;++kk) {
r.stableEndpoints.push_back(InetAddress());
p += r.stableEndpoints.back().deserialize(b,p);
}
}
return (p - startAt);
}
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 _updateSigningKey;
C25519::Signature _signature;
std::vector<Root> _roots;
};
} // namespace ZeroTier
#endif