summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--node/Constants.hpp5
-rw-r--r--node/Identity.cpp7
-rw-r--r--node/Identity.hpp2
-rw-r--r--node/Packet.cpp1
-rw-r--r--node/Packet.hpp13
-rw-r--r--node/PacketDecoder.cpp159
-rw-r--r--node/PacketDecoder.hpp20
-rw-r--r--node/Peer.cpp2
-rw-r--r--node/Peer.hpp6
-rw-r--r--node/Topology.cpp213
-rw-r--r--node/Topology.hpp68
11 files changed, 120 insertions, 376 deletions
diff --git a/node/Constants.hpp b/node/Constants.hpp
index d29df4e4..29fab0e3 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -199,6 +199,11 @@ error_no_ZT_ARCH_defined;
#define ZT_MAC_FIRST_OCTET 0x32
/**
+ * Length of secret key in bytes
+ */
+#define ZT_PEER_SECRET_KEY_LENGTH 32
+
+/**
* How often Topology::clean() and Network::clean() are called in ms
*/
#define ZT_DB_CLEAN_PERIOD 300000
diff --git a/node/Identity.cpp b/node/Identity.cpp
index 7ef83ade..8a6fc9ad 100644
--- a/node/Identity.cpp
+++ b/node/Identity.cpp
@@ -36,13 +36,12 @@
#include "Salsa20.hpp"
#include "Utils.hpp"
+// Mask for second byte in hashcash criterion -- making it require
+// 13 0 bits at the start of the hash.
#define ZT_IDENTITY_SHA_BYTE1_MASK 0xf8
namespace ZeroTier {
-/*
- * This is the hashcash criterion
- */
struct _Identity_generate_cond
{
_Identity_generate_cond() throw() {}
@@ -80,6 +79,8 @@ void Identity::generate()
bool Identity::locallyValidate() const
{
+ if (_address.isReserved())
+ return false;
char sha512buf[64];
char addrb[5];
_address.copyTo(addrb,5);
diff --git a/node/Identity.hpp b/node/Identity.hpp
index 071b0e30..dfa1c7a9 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -39,7 +39,7 @@
#include "C25519.hpp"
#include "Buffer.hpp"
-#define ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)
+#define ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH (ZT_ADDRESS_LENGTH + 1 + ZT_C25519_PUBLIC_KEY_LEN + 1 + ZT_C25519_PRIVATE_KEY_LEN)
namespace ZeroTier {
diff --git a/node/Packet.cpp b/node/Packet.cpp
index 142bc0c7..c35c8f7e 100644
--- a/node/Packet.cpp
+++ b/node/Packet.cpp
@@ -61,7 +61,6 @@ const char *Packet::errorString(ErrorCode e)
case ERROR_BAD_PROTOCOL_VERSION: return "BAD_PROTOCOL_VERSION";
case ERROR_OBJ_NOT_FOUND: return "OBJECT_NOT_FOUND";
case ERROR_IDENTITY_COLLISION: return "IDENTITY_COLLISION";
- case ERROR_IDENTITY_INVALID: return "IDENTITY_INVALID";
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
case ERROR_NO_MEMBER_CERTIFICATE: return "NO_MEMBER_CERTIFICATE";
}
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 899b2517..57d5a750 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -51,11 +51,13 @@
* 2 - 0.3.0 ... 0.4.5
* * Added signature and originating peer to multicast frame
* * Double size of multicast frame bloom filter
- * 3 - 0.5.0 ...
+ * 3 - 0.5.0 ... 0.6.0
* * Yet another multicast redesign
* * New crypto completely changes key agreement cipher
+ * 4 - 0.6.0 ...
+ * * New identity format based on hashcash design
*/
-#define ZT_PROTO_VERSION 3
+#define ZT_PROTO_VERSION 4
/**
* Maximum hop count allowed by packet structure (3 bits, 0-7)
@@ -620,14 +622,11 @@ public:
/* HELLO pushed an identity whose address is already claimed */
ERROR_IDENTITY_COLLISION = 4,
- /* Identity was not valid */
- ERROR_IDENTITY_INVALID = 5,
-
/* Verb or use case not supported/enabled by this node */
- ERROR_UNSUPPORTED_OPERATION = 6,
+ ERROR_UNSUPPORTED_OPERATION = 5,
/* Message to private network rejected -- no unexpired certificate on file */
- ERROR_NO_MEMBER_CERTIFICATE = 7
+ ERROR_NO_MEMBER_CERTIFICATE = 6
};
/**
diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp
index c024532b..9ae7270d 100644
--- a/node/PacketDecoder.cpp
+++ b/node/PacketDecoder.cpp
@@ -124,73 +124,6 @@ bool PacketDecoder::tryDecode(const RuntimeEnvironment *_r)
}
}
-void PacketDecoder::_CBaddPeerFromHello(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result)
-{
- _CBaddPeerFromHello_Data *req = (_CBaddPeerFromHello_Data *)arg;
- const RuntimeEnvironment *_r = req->renv;
-
- try {
- switch(result) {
- case Topology::PEER_VERIFY_ACCEPTED_NEW:
- case Topology::PEER_VERIFY_ACCEPTED_ALREADY_HAVE:
- case Topology::PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS: {
- _r->sw->doAnythingWaitingForPeer(p);
- Packet outp(req->source,_r->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append(req->helloPacketId);
- outp.append(req->helloTimestamp);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.armor(p->key(),true);
- _r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
- } break;
-
- case Topology::PEER_VERIFY_REJECTED_INVALID_IDENTITY: {
- Packet outp(req->source,_r->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append(req->helloPacketId);
- outp.append((unsigned char)Packet::ERROR_IDENTITY_INVALID);
- outp.armor(p->key(),true);
- _r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
- } break;
-
- case Topology::PEER_VERIFY_REJECTED_DUPLICATE:
- case Topology::PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED: {
- Packet outp(req->source,_r->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append(req->helloPacketId);
- outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
- outp.armor(p->key(),true);
- _r->demarc->send(req->localPort,req->remoteAddress,outp.data(),outp.size(),-1);
- } break;
- }
- } catch ( ... ) {
- TRACE("unexpected exception in addPeer() result callback for peer received via HELLO");
- }
-
- delete req;
-}
-
-void PacketDecoder::_CBaddPeerFromWhois(void *arg,const SharedPtr<Peer> &p,Topology::PeerVerifyResult result)
-{
- const RuntimeEnvironment *_r = (const RuntimeEnvironment *)arg;
- try {
- switch(result) {
- case Topology::PEER_VERIFY_ACCEPTED_NEW:
- case Topology::PEER_VERIFY_ACCEPTED_ALREADY_HAVE:
- case Topology::PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS:
- _r->sw->doAnythingWaitingForPeer(p);
- break;
- default:
- break;
- }
- } catch ( ... ) {
- TRACE("unexpected exception in addPeer() result callback for peer received via OK(WHOIS)");
- }
-}
-
bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer)
{
try {
@@ -205,7 +138,6 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
}
break;
case Packet::ERROR_IDENTITY_COLLISION:
- case Packet::ERROR_IDENTITY_INVALID:
// TODO: if it comes from a supernode, regenerate a new identity
break;
case Packet::ERROR_NO_MEMBER_CERTIFICATE:
@@ -225,67 +157,59 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
bool PacketDecoder::_doHELLO(const RuntimeEnvironment *_r)
{
try {
- //unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+ unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
Identity id(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
- // Initial sniff test for valid addressing and that this is indeed the
- // submitter's identity.
- if ((id.address().isReserved())||(id.address() != source())) {
-#ifdef ZT_TRACE
- if (id.address().isReserved()) {
- TRACE("dropped HELLO from %s(%s): identity has reserved address",source().toString().c_str(),_remoteAddress.toString().c_str());
- } else {
- TRACE("dropped HELLO from %s(%s): identity is not for sender of packet (HELLO is a self-announcement)",source().toString().c_str(),_remoteAddress.toString().c_str());
- }
-#endif
+ if (protoVersion != ZT_PROTO_VERSION) {
+ TRACE("dropped HELLO from %s(%s): protocol version mismatch (%u, expected %u)",source().toString().c_str(),_remoteAddress.toString().c_str(),protoVersion,(unsigned int)ZT_PROTO_VERSION);
return true;
}
- // Is this a HELLO for a peer we already know? If so just update its
- // packet receive stats and send an OK.
- SharedPtr<Peer> existingPeer(_r->topology->getPeer(id.address()));
- if ((existingPeer)&&(existingPeer->identity() == id)) {
- existingPeer->onReceive(_r,_localPort,_remoteAddress,hops(),Packet::VERB_HELLO,Utils::now());
- existingPeer->setRemoteVersion(vMajor,vMinor,vRevision);
-
- Packet outp(source(),_r->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_HELLO);
- outp.append(packetId());
- outp.append(timestamp);
- outp.append((unsigned char)ZT_PROTO_VERSION);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
- outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
- outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- outp.armor(existingPeer->key(),true);
- _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
+ if (!id.locallyValidate()) {
+ TRACE("dropped HELLO from %s(%s): identity invalid",source().toString().c_str(),_remoteAddress.toString().c_str());
return true;
}
- SharedPtr<Peer> candidate(new Peer(_r->identity,id));
- candidate->setPathAddress(_remoteAddress,false);
- candidate->setRemoteVersion(vMajor,vMinor,vRevision);
-
- _CBaddPeerFromHello_Data *arg = new _CBaddPeerFromHello_Data;
- arg->renv = _r;
- arg->source = source();
- arg->remoteAddress = _remoteAddress;
- arg->localPort = _localPort;
- arg->vMajor = vMajor;
- arg->vMinor = vMinor;
- arg->vRevision = vRevision;
- arg->helloPacketId = packetId();
- arg->helloTimestamp = timestamp;
- _r->topology->addPeer(candidate,&PacketDecoder::_CBaddPeerFromHello,arg);
+ SharedPtr<Peer> peer(_r->topology->getPeer(id.address()));
+ if (peer) {
+ if (peer->identity() != id) {
+ unsigned char key[ZT_PEER_SECRET_KEY_LENGTH];
+ if (_r->identity.agree(id,key,ZT_PEER_SECRET_KEY_LENGTH)) {
+ TRACE("rejected HELLO from %s(%s): address already claimed",source().toString().c_str(),_remoteAddress.toString().c_str());
+
+ Packet outp(source(),_r->identity.address(),Packet::VERB_ERROR);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packetId());
+ outp.append((unsigned char)Packet::ERROR_IDENTITY_COLLISION);
+ outp.armor(key,true);
+ _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
+ }
+ return true;
+ }
+ } else peer = _r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,id)));
+
+ peer->onReceive(_r,_localPort,_remoteAddress,hops(),Packet::VERB_HELLO,Utils::now());
+ peer->setRemoteVersion(vMajor,vMinor,vRevision);
+
+ Packet outp(source(),_r->identity.address(),Packet::VERB_OK);
+ outp.append((unsigned char)Packet::VERB_HELLO);
+ outp.append(packetId());
+ outp.append(timestamp);
+ outp.append((unsigned char)ZT_PROTO_VERSION);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
+ outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
+ outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+ outp.armor(peer->key(),true);
+ _r->demarc->send(_localPort,_remoteAddress,outp.data(),outp.size(),-1);
} catch (std::exception &ex) {
TRACE("dropped HELLO from %s(%s): %s",source().toString().c_str(),_remoteAddress.toString().c_str(),ex.what());
} catch ( ... ) {
TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
-
return true;
}
@@ -305,12 +229,13 @@ bool PacketDecoder::_doOK(const RuntimeEnvironment *_r,const SharedPtr<Peer> &pe
peer->setRemoteVersion(vMajor,vMinor,vRevision);
} break;
case Packet::VERB_WHOIS: {
- TRACE("%s(%s): OK(%s)",source().toString().c_str(),_remoteAddress.toString().c_str(),Packet::verbString(inReVerb));
+ // Right now only supernodes are allowed to send OK(WHOIS) to prevent
+ // poisoning attacks. Further decentralization will require some other
+ // kind of trust mechanism.
if (_r->topology->isSupernode(source())) {
- // Right now, only supernodes are queried for WHOIS so we only
- // accept OK(WHOIS) from supernodes. Otherwise peers could
- // potentially cache-poison.
- _r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,Identity(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY))),&PacketDecoder::_CBaddPeerFromWhois,const_cast<void *>((const void *)_r));
+ Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
+ if (id.locallyValidate())
+ _r->sw->doAnythingWaitingForPeer(_r->topology->addPeer(SharedPtr<Peer>(new Peer(_r->identity,id))));
}
} break;
case Packet::VERB_NETWORK_CONFIG_REQUEST: {
diff --git a/node/PacketDecoder.hpp b/node/PacketDecoder.hpp
index 3028296e..dfdb12a3 100644
--- a/node/PacketDecoder.hpp
+++ b/node/PacketDecoder.hpp
@@ -109,26 +109,6 @@ public:
inline uint64_t receiveTime() const throw() { return _receiveTime; }
private:
- struct _CBaddPeerFromHello_Data
- {
- const RuntimeEnvironment *renv;
- Address source;
- InetAddress remoteAddress;
- Demarc::Port localPort;
- unsigned int vMajor,vMinor,vRevision;
- uint64_t helloPacketId;
- uint64_t helloTimestamp;
- };
- static void _CBaddPeerFromHello(
- void *arg, // _CBaddPeerFromHello_Data
- const SharedPtr<Peer> &p,
- Topology::PeerVerifyResult result);
-
- static void _CBaddPeerFromWhois(
- void *arg, // RuntimeEnvironment
- const SharedPtr<Peer> &p,
- Topology::PeerVerifyResult result);
-
// These are called internally to handle packet contents once it has
// been authenticated, decrypted, decompressed, and classified.
bool _doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer> &peer);
diff --git a/node/Peer.cpp b/node/Peer.cpp
index ec9edfa6..57396375 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -55,7 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_vRevision(0),
_dirty(true)
{
- if (!myIdentity.agree(peerIdentity,_key,sizeof(_key)))
+ if (!myIdentity.agree(peerIdentity,_key,ZT_PEER_SECRET_KEY_LENGTH))
throw std::runtime_error("new peer identity key agreement failed");
}
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 96e8d49a..2106a787 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -34,10 +34,10 @@
#include <utility>
#include <stdexcept>
+#include "Constants.hpp"
#include "Address.hpp"
#include "Utils.hpp"
#include "Identity.hpp"
-#include "Constants.hpp"
#include "Logger.hpp"
#include "Demarc.hpp"
#include "RuntimeEnvironment.hpp"
@@ -52,7 +52,7 @@
* Max length of serialized peer record
*/
#define ZT_PEER_MAX_SERIALIZED_LENGTH ( \
- 32 + \
+ ZT_PEER_SECRET_KEY_LENGTH + \
ZT_IDENTITY_MAX_BINARY_SERIALIZED_LENGTH + \
( ( \
(sizeof(uint64_t) * 4) + \
@@ -532,7 +532,7 @@ private:
bool fixed; // do not learn address from received packets
};
- unsigned char _key[32]; // shared secret key agreed upon between identities
+ unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH];
Identity _id;
WanPath _ipv4p;
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 128a24d2..8acbb027 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -54,22 +54,12 @@ Topology::Topology(const RuntimeEnvironment *renv,const char *dbpath)
}
Utils::lockDownFile(dbpath,false); // node.db caches secrets
-
- _thread = Thread::start(this);
}
Topology::~Topology()
{
- {
- Mutex::Lock _l(_peerDeepVerifyJobs_m);
- _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
- _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::CLEAN_CACHE;
- _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
- _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::EXIT_THREAD;
- }
- _peerDeepVerifyJobs_c.signal();
- Thread::join(_thread);
- KISSDB_close(&_dbm);
+ // Flush last changes to disk
+ clean();
}
void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn)
@@ -83,10 +73,8 @@ void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> >
for(std::map< Identity,std::vector<InetAddress> >::const_iterator i(sn.begin());i!=sn.end();++i) {
if (i->first != _r->identity) {
SharedPtr<Peer> p(getPeer(i->first.address()));
- if ((!p)||(p->identity() != i->first)) {
- p = SharedPtr<Peer>(new Peer(_r->identity,i->first));
- _reallyAddPeer(p);
- }
+ if (!p)
+ p = addPeer(SharedPtr<Peer>(new Peer(_r->identity,i->first)));
for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
p->setPathAddress(*j,true);
_supernodePeers.push_back(p);
@@ -97,22 +85,33 @@ void Topology::setSupernodes(const std::map< Identity,std::vector<InetAddress> >
_amSupernode = (_supernodes.find(_r->identity) != _supernodes.end());
}
-void Topology::addPeer(const SharedPtr<Peer> &candidate,void (*callback)(void *,const SharedPtr<Peer> &,Topology::PeerVerifyResult),void *arg)
+SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
{
- if (candidate->address() != _r->identity.address()) {
- Mutex::Lock _l(_peerDeepVerifyJobs_m);
- _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
- _PeerDeepVerifyJob &job = _peerDeepVerifyJobs.back();
- job.callback = callback;
- job.arg = arg;
- job.candidate = candidate;
- job.type = _PeerDeepVerifyJob::VERIFY_PEER;
- _peerDeepVerifyJobs_c.signal();
- } else {
- TRACE("BUG: addPeer() caught and ignored attempt to add peer for self");
- if (callback)
- callback(arg,candidate,PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED);
+ if (peer->address() == _r->identity.address()) {
+ TRACE("BUG: addNewPeer() caught and ignored attempt to add peer for self");
+ throw std::logic_error("cannot add peer for self");
+ }
+
+ SharedPtr<Peer> actualPeer;
+ {
+ Mutex::Lock _l(_activePeers_m);
+ actualPeer = _activePeers.insert(std::pair< Address,SharedPtr<Peer> >(peer->address(),peer)).first->second;
}
+
+ uint64_t atmp[ZT_ADDRESS_LENGTH];
+ actualPeer->address().copyTo(atmp,ZT_ADDRESS_LENGTH);
+
+ Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
+ actualPeer->serialize(b);
+ b.zeroUnused();
+
+ _dbm_m.lock();
+ if (KISSDB_put(&_dbm,atmp,b.data())) {
+ TRACE("error writing %s to peerdb",actualPeer->address().toString().c_str());
+ } else actualPeer->getAndResetDirty();
+ _dbm_m.unlock();
+
+ return actualPeer;
}
SharedPtr<Peer> Topology::getPeer(const Address &zta)
@@ -212,143 +211,29 @@ skip_and_try_next_supernode:
void Topology::clean()
{
- {
- Mutex::Lock _l(_peerDeepVerifyJobs_m);
- _peerDeepVerifyJobs.push_back(_PeerDeepVerifyJob());
- _peerDeepVerifyJobs.back().type = _PeerDeepVerifyJob::CLEAN_CACHE;
- }
- _peerDeepVerifyJobs_c.signal();
-}
-
-void Topology::threadMain()
- throw()
-{
- for(;;) {
- _peerDeepVerifyJobs_m.lock();
- if (_peerDeepVerifyJobs.empty()) {
- _peerDeepVerifyJobs_m.unlock();
- _peerDeepVerifyJobs_c.wait();
- continue;
- }
- _PeerDeepVerifyJob job(_peerDeepVerifyJobs.front());
- _peerDeepVerifyJobs.pop_front();
- unsigned long queueRemaining = (unsigned long)_peerDeepVerifyJobs.size();
- _peerDeepVerifyJobs_m.unlock();
-
- switch(job.type) {
- case _PeerDeepVerifyJob::VERIFY_PEER:
- /* TODO: We should really verify peers every time completely if this
- * is a supernode, perhaps deferring the expensive part for new
- * addresses. An attempt at claim jumping should also trigger a
- * short duration ban of the originating IP address in most cases,
- * since this means either malicious intent or broken software. */
- TRACE("verifying peer: %s",job.candidate->identity().address().toString().c_str());
-
- if ((job.candidate->identity())&&(!job.candidate->identity().address().isReserved())&&(job.candidate->identity().locallyValidate())) {
- // Peer passes sniff test, so check to see if we've already got
- // one with the same address.
-
- SharedPtr<Peer> existingPeer(getPeer(job.candidate->identity().address()));
-
- if (existingPeer) {
- if (existingPeer->identity() == job.candidate->identity()) {
- // It's an *exact* duplicate, so return the existing peer
- if (job.callback)
- job.callback(job.arg,existingPeer,PEER_VERIFY_ACCEPTED_ALREADY_HAVE);
- } else if (queueRemaining > 3) {
- /* Prevents a CPU hog DOS attack, while allowing a very unlikely kind of
- * DOS attack where someone knows someone else's address prior to their
- * registering it and claim-jumps them and then floods with bad identities
- * to hold their claim. Of the two, the latter would be infeasable
- * without already having cracked the target's machine in which case
- * the attacker has their private key anyway and can really steal their
- * identity. So why bother.*/
- TRACE("%s is duplicate, load too high, old won",job.candidate->identity().address().toString().c_str());
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED);
- } else {
- // It's different so deeply validate it first, then the
- // existing claimant, and toss the imposter. If both verify, the
- // one we already have wins.
-
- if (!job.candidate->identity().locallyValidate()) {
- LOG("Topology: IMPOSTER %s rejected",job.candidate->identity().address().toString().c_str());
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_INVALID_IDENTITY);
- } else if (!existingPeer->identity().locallyValidate()) {
- LOG("Topology: previous IMPOSTER %s displaced by valid identity!",job.candidate->identity().address().toString().c_str());
- _reallyAddPeer(job.candidate);
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS);
- } else {
- LOG("Topology: tie between apparently valid claims on %s, oldest won",job.candidate->identity().address().toString().c_str());
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_DUPLICATE);
- }
- }
- } else {
- TRACE("%s accepted as new",job.candidate->identity().address().toString().c_str());
- _reallyAddPeer(job.candidate);
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_ACCEPTED_NEW);
- }
- } else {
- TRACE("%s rejected, identity failed initial checks",job.candidate->identity().address().toString().c_str());
- if (job.callback)
- job.callback(job.arg,job.candidate,PEER_VERIFY_REJECTED_INVALID_IDENTITY);
+ TRACE("cleaning caches and flushing modified peers to disk...");
+
+ Mutex::Lock _l(_activePeers_m);
+ for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) {
+ if (p->second->getAndResetDirty()) {
+ try {
+ uint64_t atmp[ZT_ADDRESS_LENGTH];
+ p->second->identity().address().copyTo(atmp,ZT_ADDRESS_LENGTH);
+
+ Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
+ p->second->serialize(b);
+ b.zeroUnused();
+
+ _dbm_m.lock();
+ if (KISSDB_put(&_dbm,atmp,b.data())) {
+ TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str());
}
- break;
- case _PeerDeepVerifyJob::CLEAN_CACHE:
- TRACE("cleaning caches and flushing modified peers to disk...");
- {
- Mutex::Lock _l(_activePeers_m);
- for(std::map< Address,SharedPtr<Peer> >::iterator p(_activePeers.begin());p!=_activePeers.end();++p) {
- if (p->second->getAndResetDirty()) {
- try {
- uint64_t atmp[ZT_ADDRESS_LENGTH];
- p->second->identity().address().copyTo(atmp,ZT_ADDRESS_LENGTH);
- Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
- p->second->serialize(b);
- b.zeroUnused();
- _dbm_m.lock();
- if (KISSDB_put(&_dbm,atmp,b.data())) {
- TRACE("error writing %s to peer.db",p->second->identity().address().toString().c_str());
- }
- _dbm_m.unlock();
- } catch ( ... ) {
- TRACE("unexpected exception flushing %s to peer.db",p->second->identity().address().toString().c_str());
- }
- }
- }
- }
- break;
- case _PeerDeepVerifyJob::EXIT_THREAD:
- TRACE("thread terminating...");
- return;
+ _dbm_m.unlock();
+ } catch ( ... ) {
+ TRACE("unexpected exception flushing %s to peer.db",p->second->identity().address().toString().c_str());
+ }
}
}
}
-void Topology::_reallyAddPeer(const SharedPtr<Peer> &p)
-{
- {
- Mutex::Lock _l(_activePeers_m);
- _activePeers[p->identity().address()] = p;
- }
- try {
- uint64_t atmp[ZT_ADDRESS_LENGTH];
- p->address().copyTo(atmp,ZT_ADDRESS_LENGTH);
- Buffer<ZT_PEER_MAX_SERIALIZED_LENGTH> b;
- p->serialize(b);
- b.zeroUnused();
- _dbm_m.lock();
- if (KISSDB_put(&_dbm,atmp,b.data())) {
- TRACE("error writing %s to peerdb",p->address().toString().c_str());
- } else p->getAndResetDirty();
- _dbm_m.unlock();
- } catch ( ... ) {
- TRACE("unexpected exception flushing to peerdb");
- }
-}
-
} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
index ecafeef8..637cbb36 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -33,18 +33,14 @@
#include <map>
#include <set>
-#include <list>
#include <vector>
#include <stdexcept>
+#include "Constants.hpp"
#include "Address.hpp"
#include "Peer.hpp"
#include "Mutex.hpp"
-#include "Condition.hpp"
#include "InetAddress.hpp"
-#include "Constants.hpp"
-#include "Thread.hpp"
-#include "MulticastGroup.hpp"
#include "Utils.hpp"
#include "../ext/kissdb/kissdb.h"
@@ -59,19 +55,6 @@ class RuntimeEnvironment;
class Topology
{
public:
- /**
- * Result of peer add/verify
- */
- enum PeerVerifyResult
- {
- PEER_VERIFY_ACCEPTED_NEW, /* new peer */
- PEER_VERIFY_ACCEPTED_ALREADY_HAVE, /* we already knew ye */
- PEER_VERIFY_ACCEPTED_DISPLACED_INVALID_ADDRESS, /* you booted out an impostor */
- PEER_VERIFY_REJECTED_INVALID_IDENTITY, /* identity is invalid or validation failed */
- PEER_VERIFY_REJECTED_DUPLICATE, /* someone equally valid already has your address */
- PEER_VERIFY_REJECTED_DUPLICATE_TRIAGED /* you look duplicate and I'm too busy to deep verify */
- };
-
Topology(const RuntimeEnvironment *renv,const char *dbpath)
throw(std::runtime_error);
@@ -85,22 +68,15 @@ public:
void setSupernodes(const std::map< Identity,std::vector<InetAddress> > &sn);
/**
- * Add a peer to this network
- *
- * Verification and adding actually occurs in the background, since in
- * rare cases it can be somewhat CPU-intensive. The callback will be
- * called (from the background thread) when add is complete.
- *
- * The peer given to the callback may not be the same object provided
- * as a candidate if the candidate was an exact duplicate of a peer we
- * already have.
+ * Add a peer to database
*
- * @param candidate New candidate peer to be added
- * @param callback Callback to call when peer verification is complete
- * @param arg First argument to callback
- * @return Verification result or PEER_VERIFY__IN_PROGRESS if occurring in background
+ * This will not replace existing peers. In that case the existing peer
+ * record is returned.
+ *
+ * @param peer Peer to add
+ * @return New or existing peer (should replace 'peer')
*/
- void addPeer(const SharedPtr<Peer> &candidate,void (*callback)(void *,const SharedPtr<Peer> &,PeerVerifyResult),void *arg);
+ SharedPtr<Peer> addPeer(const SharedPtr<Peer> &peer);
/**
* Get a peer from its address
@@ -169,7 +145,7 @@ public:
inline bool amSupernode() const { return _amSupernode; }
/**
- * Clean and flush database now (runs in the background)
+ * Clean and flush database
*/
void clean();
@@ -296,38 +272,12 @@ public:
std::vector< SharedPtr<Peer> > &_v;
};
- /**
- * Thread main method; do not call elsewhere
- */
- void threadMain()
- throw();
-
private:
- void _reallyAddPeer(const SharedPtr<Peer> &p);
-
- // A job for the background deep verify thread (also does cache cleaning, flushing, etc.)
- struct _PeerDeepVerifyJob
- {
- void (*callback)(void *,const SharedPtr<Peer> &,Topology::PeerVerifyResult);
- void *arg;
- SharedPtr<Peer> candidate;
- enum {
- VERIFY_PEER,
- CLEAN_CACHE,
- EXIT_THREAD
- } type;
- };
-
const RuntimeEnvironment *const _r;
- Thread _thread;
std::map< Address,SharedPtr<Peer> > _activePeers;
Mutex _activePeers_m;
- std::list< _PeerDeepVerifyJob > _peerDeepVerifyJobs;
- Mutex _peerDeepVerifyJobs_m;
- Condition _peerDeepVerifyJobs_c;
-
std::map< Identity,std::vector<InetAddress> > _supernodes;
std::set< Address > _supernodeAddresses;
std::vector< SharedPtr<Peer> > _supernodePeers;