summaryrefslogtreecommitdiff
path: root/node
diff options
context:
space:
mode:
authorGrant Limberg <glimberg@gmail.com>2015-11-02 18:30:54 -0800
committerGrant Limberg <glimberg@gmail.com>2015-11-02 18:30:54 -0800
commita19e82fcbc2203f0d84a0e744d344e0796bc0c33 (patch)
tree2f8cfc56a03cf6e614991c83a309b5fce5a48e48 /node
parent0ffcfa307e537347f181e7b22047f252d0cdc414 (diff)
parent4e9d4304761f93a1764d3ec2d2b0c38140decad8 (diff)
downloadinfinitytier-a19e82fcbc2203f0d84a0e744d344e0796bc0c33.tar.gz
infinitytier-a19e82fcbc2203f0d84a0e744d344e0796bc0c33.zip
Merge branch 'edge' into windows-ui
Diffstat (limited to 'node')
-rw-r--r--node/AntiRecursion.hpp66
-rw-r--r--node/Cluster.cpp414
-rw-r--r--node/Cluster.hpp94
-rw-r--r--node/Constants.hpp70
-rw-r--r--node/Hashtable.hpp2
-rw-r--r--node/IncomingPacket.cpp117
-rw-r--r--node/InetAddress.hpp38
-rw-r--r--node/Multicaster.cpp222
-rw-r--r--node/Network.cpp1
-rw-r--r--node/NetworkConfig.cpp3
-rw-r--r--node/Node.cpp33
-rw-r--r--node/Node.hpp6
-rw-r--r--node/Packet.hpp10
-rw-r--r--node/Path.cpp45
-rw-r--r--node/Path.hpp144
-rw-r--r--node/Peer.cpp203
-rw-r--r--node/Peer.hpp136
-rw-r--r--node/RemotePath.hpp161
-rw-r--r--node/SelfAwareness.cpp49
-rw-r--r--node/Switch.cpp59
-rw-r--r--node/Switch.hpp8
-rw-r--r--node/Topology.cpp176
-rw-r--r--node/Topology.hpp49
23 files changed, 1108 insertions, 998 deletions
diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp
index c5aa92d8..8629d19a 100644
--- a/node/AntiRecursion.hpp
+++ b/node/AntiRecursion.hpp
@@ -35,28 +35,28 @@
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));
_ptr = 0;
@@ -68,13 +68,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 +91,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
diff --git a/node/Cluster.cpp b/node/Cluster.cpp
index 9d25593a..e9e31ede 100644
--- a/node/Cluster.cpp
+++ b/node/Cluster.cpp
@@ -44,9 +44,9 @@
#include "CertificateOfMembership.hpp"
#include "Salsa20.hpp"
#include "Poly1305.hpp"
-#include "Packet.hpp"
#include "Identity.hpp"
-#include "Peer.hpp"
+#include "Topology.hpp"
+#include "Packet.hpp"
#include "Switch.hpp"
#include "Node.hpp"
@@ -82,7 +82,11 @@ Cluster::Cluster(
_z(z),
_id(id),
_zeroTierPhysicalEndpoints(zeroTierPhysicalEndpoints),
- _members(new _Member[ZT_CLUSTER_MAX_MEMBERS])
+ _members(new _Member[ZT_CLUSTER_MAX_MEMBERS]),
+ _peerAffinities(65536),
+ _lastCleanedPeerAffinities(0),
+ _lastCheckedPeersForAnnounce(0),
+ _lastFlushed(0)
{
uint16_t stmp[ZT_SHA512_DIGEST_LEN / sizeof(uint16_t)];
@@ -200,36 +204,40 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
}
#endif
}
- m.lastReceivedAliveAnnouncement = RR->node->now();
#ifdef ZT_TRACE
- 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());
+ 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 STATE_MESSAGE_HAVE_PEER: {
- try {
- Identity id;
- ptr += id.deserialize(dmsg,ptr);
- if (id) {
- RR->topology->saveIdentity(id);
-
- { // Add or update peer affinity entry
- _PeerAffinity pa(id.address(),fromMemberId,RR->node->now());
- Mutex::Lock _l2(_peerAffinities_m);
- std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n))
- if ((i != _peerAffinities.end())&&(i->key == pa.key)) {
- i->timestamp = pa.timestamp;
- } else {
- _peerAffinities.push_back(pa);
- std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now
- }
- }
-
- TRACE("[%u] has %s",(unsigned int)fromMemberId,id.address().toString().c_str());
- }
- } catch ( ... ) {
- // ignore invalid identities
- }
+ const uint64_t now = RR->node->now();
+ Identity id;
+ InetAddress physicalAddress;
+ ptr += id.deserialize(dmsg,ptr);
+ ptr += physicalAddress.deserialize(dmsg,ptr);
+ if (id) {
+ // Forget any paths that we have to this peer at its address
+ if (physicalAddress) {
+ SharedPtr<Peer> myPeerRecord(RR->topology->getPeerNoCache(id.address(),now));
+ if (myPeerRecord)
+ myPeerRecord->removePathByAddress(physicalAddress);
+ }
+
+ // Always save identity to update file time
+ RR->topology->saveIdentity(id);
+
+ // Set peer affinity to its new home
+ {
+ Mutex::Lock _l2(_peerAffinities_m);
+ _PA &pa = _peerAffinities[id.address()];
+ pa.ts = now;
+ pa.mid = fromMemberId;
+ }
+ TRACE("[%u] has %s @ %s",(unsigned int)fromMemberId,id.address().toString().c_str(),physicalAddress.toString().c_str());
+ }
} break;
case STATE_MESSAGE_MULTICAST_LIKE: {
@@ -238,113 +246,102 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
const MAC mac(dmsg.field(ptr,6),6); ptr += 6;
const uint32_t adi = dmsg.at<uint32_t>(ptr); ptr += 4;
RR->mc->add(RR->node->now(),nwid,MulticastGroup(mac,adi),address);
- TRACE("[%u] %s likes %s/%u on %.16llu",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid);
+ TRACE("[%u] %s likes %s/%.8x on %.16llx",(unsigned int)fromMemberId,address.toString().c_str(),mac.toString().c_str(),(unsigned int)adi,nwid);
} break;
case STATE_MESSAGE_COM: {
+ /* not currently used so not decoded yet
CertificateOfMembership com;
ptr += com.deserialize(dmsg,ptr);
if (com) {
TRACE("[%u] COM for %s on %.16llu rev %llu",(unsigned int)fromMemberId,com.issuedTo().toString().c_str(),com.networkId(),com.revision());
}
+ */
} break;
- case STATE_MESSAGE_RELAY: {
+ case STATE_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);
- const unsigned int packetLen = dmsg.at<uint16_t>(ptr); ptr += 2;
- const void *packet = (const void *)dmsg.field(ptr,packetLen); ptr += packetLen;
-
- if (packetLen >= ZT_PROTO_MIN_FRAGMENT_LENGTH) { // ignore anything too short to contain a dest address
- const Address destinationAddress(reinterpret_cast<const char *>(packet) + 8,ZT_ADDRESS_LENGTH);
- TRACE("[%u] relay %u bytes to %s (%u remote paths included)",(unsigned int)fromMemberId,packetLen,destinationAddress.toString().c_str(),numRemotePeerPaths);
-
- SharedPtr<Peer> destinationPeer(RR->topology->getPeer(destinationAddress));
- if (destinationPeer) {
- if (
- (destinationPeer->send(RR,packet,packetLen,RR->node->now()))&&
- (numRemotePeerPaths > 0)&&
- (packetLen >= 18)&&
- (reinterpret_cast<const unsigned char *>(packet)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR)
- ) {
- // If remote peer paths were sent with this relayed packet, we do
- // RENDEZVOUS. It's handled here for cluster-relayed packets since
- // we don't have both Peer records so this is a different path.
-
- const Address remotePeerAddress(reinterpret_cast<const char *>(packet) + 13,ZT_ADDRESS_LENGTH);
-
- InetAddress bestDestV4,bestDestV6;
- destinationPeer->getBestActiveAddresses(RR->node->now(),bestDestV4,bestDestV6);
- 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 rendezvousForDest(destinationAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- rendezvousForDest.append((uint8_t)0);
- remotePeerAddress.appendTo(rendezvousForDest);
-
- Buffer<2048> rendezvousForOtherEnd;
- remotePeerAddress.appendTo(rendezvousForOtherEnd);
- rendezvousForOtherEnd.append((uint8_t)Packet::VERB_RENDEZVOUS);
- const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForOtherEnd.size();
- rendezvousForOtherEnd.addSize(2); // space for actual packet payload length
- rendezvousForOtherEnd.append((uint8_t)0); // flags == 0
- destinationAddress.appendTo(rendezvousForOtherEnd);
-
- bool haveMatch = false;
- if ((bestDestV6)&&(bestRemoteV6)) {
- haveMatch = true;
-
- rendezvousForDest.append((uint16_t)bestRemoteV6.port());
- rendezvousForDest.append((uint8_t)16);
- rendezvousForDest.append(bestRemoteV6.rawIpData(),16);
-
- rendezvousForOtherEnd.append((uint16_t)bestDestV6.port());
- rendezvousForOtherEnd.append((uint8_t)16);
- rendezvousForOtherEnd.append(bestDestV6.rawIpData(),16);
- rendezvousForOtherEnd.setAt<uint16_t>(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 16));
- } else if ((bestDestV4)&&(bestRemoteV4)) {
- haveMatch = true;
-
- rendezvousForDest.append((uint16_t)bestRemoteV4.port());
- rendezvousForDest.append((uint8_t)4);
- rendezvousForDest.append(bestRemoteV4.rawIpData(),4);
-
- rendezvousForOtherEnd.append((uint16_t)bestDestV4.port());
- rendezvousForOtherEnd.append((uint8_t)4);
- rendezvousForOtherEnd.append(bestDestV4.rawIpData(),4);
- rendezvousForOtherEnd.setAt<uint16_t>(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4));
- }
-
- if (haveMatch) {
- _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForOtherEnd.data(),rendezvousForOtherEnd.size());
- RR->sw->send(rendezvousForDest,true,0);
- }
+
+ 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,now));
+ 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);
+ const unsigned int rendezvousForOtherEndPayloadSizePtr = rendezvousForRemote.size();
+ 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>(rendezvousForOtherEndPayloadSizePtr,(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>(rendezvousForOtherEndPayloadSizePtr,(uint16_t)(9 + 4));
+ }
+
+ if (haveMatch) {
+ _send(fromMemberId,STATE_MESSAGE_PROXY_SEND,rendezvousForRemote.data(),rendezvousForRemote.size());
+ _flush(fromMemberId); // we want this to go ASAP, since with port restricted cone NATs success can be timing-sensitive
+ RR->sw->send(rendezvousForLocal,true,0);
+ }
}
} break;
case STATE_MESSAGE_PROXY_SEND: {
- const Address rcpt(dmsg.field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+ 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);
+ 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;
@@ -363,81 +360,78 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
}
}
-bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len)
+bool Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite)
{
if (len > 16384) // sanity check
return false;
- uint64_t mostRecentTimestamp = 0;
- uint16_t canHasPeer = 0;
+ const uint64_t now = RR->node->now();
+ unsigned int canHasPeer = 0;
{ // Anyone got this peer?
Mutex::Lock _l2(_peerAffinities_m);
- std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),_PeerAffinity(toPeerAddress,0,0))); // O(log(n))
- while ((i != _peerAffinities.end())&&(i->address() == toPeerAddress)) {
- uint16_t mid = i->clusterMemberId();
- if ((mid != _id)&&(i->timestamp > mostRecentTimestamp)) {
- mostRecentTimestamp = i->timestamp;
- canHasPeer = mid;
- }
- }
+ _PA *pa = _peerAffinities.get(toPeerAddress);
+ if ((pa)&&(pa->mid != _id)&&((now - pa->ts) < ZT_PEER_ACTIVITY_TIMEOUT))
+ canHasPeer = pa->mid;
+ else return false;
}
- const uint64_t now = RR->node->now();
- if ((now - mostRecentTimestamp) < ZT_PEER_ACTIVITY_TIMEOUT) {
- Buffer<16384> buf;
-
+ Buffer<1024> buf;
+ if (unite) {
InetAddress v4,v6;
if (fromPeerAddress) {
- SharedPtr<Peer> fromPeer(RR->topology->getPeer(fromPeerAddress));
+ SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress,now));
if (fromPeer)
fromPeer->getBestActiveAddresses(now,v4,v6);
}
- buf.append((uint8_t)( (v4) ? ((v6) ? 2 : 1) : ((v6) ? 1 : 0) ));
+ uint8_t addrCount = 0;
if (v4)
- v4.serialize(buf);
+ ++addrCount;
if (v6)
- v6.serialize(buf);
- buf.append((uint16_t)len);
- buf.append(data,len);
-
- {
- Mutex::Lock _l2(_members[canHasPeer].lock);
- _send(canHasPeer,STATE_MESSAGE_RELAY,buf.data(),buf.size());
+ ++addrCount;
+ if (addrCount) {
+ toPeerAddress.appendTo(buf);
+ fromPeerAddress.appendTo(buf);
+ buf.append(addrCount);
+ if (v4)
+ v4.serialize(buf);
+ if (v6)
+ v6.serialize(buf);
}
-
- TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer);
- return true;
- } else {
- TRACE("sendViaCluster(): unable to relay %u bytes from %s to %s since no cluster members seem to have it!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str());
- return false;
}
+ {
+ Mutex::Lock _l2(_members[canHasPeer].lock);
+ if (buf.size() > 0)
+ _send(canHasPeer,STATE_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
+ if (_members[canHasPeer].zeroTierPhysicalEndpoints.size() > 0)
+ RR->node->putPacket(InetAddress(),_members[canHasPeer].zeroTierPhysicalEndpoints.front(),data,len);
+ }
+
+ TRACE("sendViaCluster(): relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)canHasPeer);
+
+ return true;
}
-void Cluster::replicateHavePeer(const Identity &peerId)
+void Cluster::replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress)
{
- { // Use peer affinity table to track our own last announce time for peers
- _PeerAffinity pa(peerId.address(),_id,RR->node->now());
+ const uint64_t now = RR->node->now();
+ {
Mutex::Lock _l2(_peerAffinities_m);
- std::vector<_PeerAffinity>::iterator i(std::lower_bound(_peerAffinities.begin(),_peerAffinities.end(),pa)); // O(log(n))
- if ((i != _peerAffinities.end())&&(i->key == pa.key)) {
- if ((pa.timestamp - i->timestamp) >= ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) {
- i->timestamp = pa.timestamp;
- // continue to announcement
- } else {
- // we've already announced this peer recently, so skip
- return;
- }
+ _PA &pa = _peerAffinities[peerId.address()];
+ if (pa.mid != _id) {
+ pa.ts = now;
+ pa.mid = _id;
+ } else if ((now - pa.ts) < ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD) {
+ return;
} else {
- _peerAffinities.push_back(pa);
- std::sort(_peerAffinities.begin(),_peerAffinities.end()); // probably a more efficient way to insert but okay for now
- // continue to announcement
+ pa.ts = now;
}
}
// announcement
Buffer<4096> buf;
peerId.serialize(buf,false);
+ physicalAddress.serialize(buf);
{
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
@@ -449,7 +443,7 @@ void Cluster::replicateHavePeer(const Identity &peerId)
void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,const MulticastGroup &group)
{
- Buffer<2048> buf;
+ Buffer<1024> buf;
buf.append((uint64_t)nwid);
peerAddress.appendTo(buf);
group.mac().appendTo(buf);
@@ -466,7 +460,7 @@ void Cluster::replicateMulticastLike(uint64_t nwid,const Address &peerAddress,co
void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembership &com)
{
- Buffer<2048> buf;
+ Buffer<4096> buf;
com.serialize(buf);
TRACE("replicating %s COM for %.16llx to all members",com.issuedTo().toString().c_str(),com.networkId());
{
@@ -478,11 +472,47 @@ void Cluster::replicateCertificateOfNetworkMembership(const CertificateOfMembers
}
}
+struct _ClusterAnnouncePeers
+{
+ _ClusterAnnouncePeers(const uint64_t now_,Cluster *parent_) : now(now_),parent(parent_) {}
+ const uint64_t now;
+ Cluster *const parent;
+ inline void operator()(const Topology &t,const SharedPtr<Peer> &peer) const
+ {
+ Path *p = peer->getBestPath(now);
+ if (p)
+ parent->replicateHavePeer(peer->identity(),p->address());
+ }
+};
void Cluster::doPeriodicTasks()
{
const uint64_t now = RR->node->now();
- {
+ // Erase old peer affinity entries just to control table size
+ if ((now - _lastCleanedPeerAffinities) >= (ZT_PEER_ACTIVITY_TIMEOUT * 5)) {
+ _lastCleanedPeerAffinities = now;
+ Address *k = (Address *)0;
+ _PA *v = (_PA *)0;
+ Mutex::Lock _l(_peerAffinities_m);
+ Hashtable< Address,_PA >::Iterator i(_peerAffinities);
+ while (i.next(k,v)) {
+ if ((now - v->ts) >= (ZT_PEER_ACTIVITY_TIMEOUT * 5))
+ _peerAffinities.erase(*k);
+ }
+ }
+
+ // Announce peers that we have active direct paths to -- note that we forget paths
+ // that other cluster members claim they have, which prevents us from fighting
+ // with other cluster members (route flapping) over specific paths.
+ if ((now - _lastCheckedPeersForAnnounce) >= (ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD / 4)) {
+ _lastCheckedPeersForAnnounce = now;
+ _ClusterAnnouncePeers func(now,this);
+ RR->topology->eachPeer<_ClusterAnnouncePeers &>(func);
+ }
+
+ // Flush outgoing packet send queue every doPeriodicTasks()
+ 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);
@@ -564,26 +594,22 @@ void Cluster::removeMember(uint16_t memberId)
_memberIds = newMemberIds;
}
-bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload)
+bool Cluster::findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload)
{
- if (!peerPhysicalAddress) // sanity check
- return false;
-
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 available for %s",peerPhysicalAddress.toIpString().c_str());
+ 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; // initial "best" is for peer to stay put
+ std::vector<InetAddress> best;
const double currentDistance = _dist3d(_x,_y,_z,px,py,pz);
double bestDistance = (offload ? 2147483648.0 : currentDistance);
unsigned int bestMember = _id;
- TRACE("%s is at %d,%d,%d -- looking for anyone closer than %d,%d,%d (%fkm)",peerPhysicalAddress.toString().c_str(),px,py,pz,_x,_y,_z,bestDistance);
{
Mutex::Lock _l(_memberIds_m);
for(std::vector<uint16_t>::const_iterator mid(_memberIds.begin());mid!=_memberIds.end();++mid) {
@@ -592,7 +618,7 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy
// 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) ) {
- double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
+ const double mdist = _dist3d(m.x,m.y,m.z,px,py,pz);
if (mdist < bestDistance) {
bestDistance = mdist;
bestMember = *mid;
@@ -602,36 +628,16 @@ bool Cluster::redirectPeer(const Address &peerAddress,const InetAddress &peerPhy
}
}
- if (best.size() > 0) {
- TRACE("%s seems closer to %u at %fkm, suggesting redirect...",peerAddress.toString().c_str(),bestMember,bestDistance);
-
- /* if (peer->remoteVersionProtocol() >= 5) {
- // If it's a newer peer send VERB_PUSH_DIRECT_PATHS which is more idiomatic
- } else { */
- // Otherwise send VERB_RENDEZVOUS for ourselves, which will trick peers into trying other endpoints for us even if they're too old for PUSH_DIRECT_PATHS
- for(std::vector<InetAddress>::const_iterator a(best.begin());a!=best.end();++a) {
- if ((a->ss_family == AF_INET)||(a->ss_family == AF_INET6)) {
- Packet outp(peerAddress,RR->identity.address(),Packet::VERB_RENDEZVOUS);
- outp.append((uint8_t)0); // no flags
- RR->identity.address().appendTo(outp); // HACK: rendezvous with ourselves! with really old peers this will only work if I'm a root server!
- outp.append((uint16_t)a->port());
- if (a->ss_family == AF_INET) {
- outp.append((uint8_t)4);
- outp.append(a->rawIpData(),4);
- } else {
- outp.append((uint8_t)16);
- outp.append(a->rawIpData(),16);
- }
- RR->sw->send(outp,true,0);
- }
- }
- //}
-
- return true;
- } else {
- TRACE("peer %s is at [%d,%d,%d], distance to us is %f and this seems to be the best",peerAddress.toString().c_str(),px,py,pz,currentDistance);
- return false;
+ // 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;
@@ -653,7 +659,7 @@ void Cluster::status(ZT_ClusterStatus &status) const
ms[_id]->x = _x;
ms[_id]->y = _y;
ms[_id]->z = _z;
- ms[_id]->peers = RR->topology->countAlive();
+ ms[_id]->peers = RR->topology->countActive();
for(std::vector<InetAddress>::const_iterator ep(_zeroTierPhysicalEndpoints.begin());ep!=_zeroTierPhysicalEndpoints.end();++ep) {
if (ms[_id]->numZeroTierPhysicalEndpoints >= ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES) // sanity check
break;
@@ -686,10 +692,12 @@ void Cluster::status(ZT_ClusterStatus &status) const
{
Mutex::Lock _l2(_peerAffinities_m);
- for(std::vector<_PeerAffinity>::const_iterator pi(_peerAffinities.begin());pi!=_peerAffinities.end();++pi) {
- unsigned int mid = pi->clusterMemberId();
- if ((ms[mid])&&(mid != _id)&&((now - pi->timestamp) < ZT_PEER_ACTIVITY_TIMEOUT))
- ++ms[mid]->peers;
+ Address *k = (Address *)0;
+ _PA *v = (_PA *)0;
+ Hashtable< Address,_PA >::Iterator i(const_cast<Cluster *>(this)->_peerAffinities);
+ while (i.next(k,v)) {
+ if ( (ms[v->mid]) && (v->mid != _id) && ((now - v->ts) < ZT_PEER_ACTIVITY_TIMEOUT) )
+ ++ms[v->mid]->peers;
}
}
}
diff --git a/node/Cluster.hpp b/node/Cluster.hpp
index be346659..ee220999 100644
--- a/node/Cluster.hpp
+++ b/node/Cluster.hpp
@@ -46,18 +46,26 @@
/**
* 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 10000
/**
* How often should we announce that we have a peer?
*/
-#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD 60000
+#define ZT_CLUSTER_HAVE_PEER_ANNOUNCE_PERIOD ZT_PEER_DIRECT_PING_DELAY
/**
* Desired period between doPeriodicTasks() in milliseconds
*/
-#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 100
+#define ZT_CLUSTER_PERIODIC_TASK_PERIOD 250
+
+/**
+ * How often to flush outgoing message queues (maximum interval)
+ */
+#define ZT_CLUSTER_FLUSH_PERIOD 500
namespace ZeroTier {
@@ -117,6 +125,10 @@ public:
/**
* Cluster member has this peer:
* <[...] binary serialized peer identity>
+ * <[...] binary serialized peer remote physical address>
+ *
+ * Clusters send this message when they learn a path to a peer. The
+ * replicated physical address is the one learned.
*/
STATE_MESSAGE_HAVE_PEER = 2,
@@ -136,13 +148,18 @@ public:
STATE_MESSAGE_COM = 4,
/**
- * Relay a packet to a peer:
- * <[1] 8-bit number of sending peer active path addresses>
- * <[...] series of serialized InetAddresses of sending peer's paths>
- * <[2] 16-bit packet length>
- * <[...] packet or packet fragment>
+ * 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.
*/
- STATE_MESSAGE_RELAY = 5,
+ STATE_MESSAGE_PROXY_UNITE = 5,
/**
* Request that a cluster member send a packet to a locally-known peer:
@@ -158,7 +175,20 @@ public:
* while PROXY_SEND is used to implement proxy sending (which right
* now is only used to send RENDEZVOUS).
*/
- STATE_MESSAGE_PROXY_SEND = 6
+ STATE_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!
+ */
+ STATE_MESSAGE_NETWORK_CONFIG = 7
};
/**
@@ -198,16 +228,18 @@ public:
* @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
* @return True if this data was sent via another cluster member, false if none have this peer
*/
- bool sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len);
+ bool sendViaCluster(const Address &fromPeerAddress,const Address &toPeerAddress,const void *data,unsigned int len,bool unite);
/**
* Advertise to the cluster that we have this peer
*
* @param peerId Identity of peer that we have
+ * @param physicalAddress Physical address of peer (from our POV)
*/
- void replicateHavePeer(const Identity &peerId);
+ void replicateHavePeer(const Identity &peerId,const InetAddress &physicalAddress);
/**
* Advertise a multicast LIKE to the cluster
@@ -245,14 +277,15 @@ public:
void removeMember(uint16_t memberId);
/**
- * Redirect this peer to a better cluster member if needed
+ * Find a better cluster endpoint for this peer (if any)
*
- * @param peerAddress Peer to (possibly) redirect
+ * @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 peer was redirected
+ * @return True if redirectTo was set to a new address, false if redirectTo was not modified
*/
- bool redirectPeer(const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
+ bool findBetterEndpoint(InetAddress &redirectTo,const Address &peerAddress,const InetAddress &peerPhysicalAddress,bool offload);
/**
* Fill out ZT_ClusterStatus structure (from core API)
@@ -265,7 +298,7 @@ private:
void _send(uint16_t memberId,StateMessageType type,const void *msg,unsigned int len);
void _flush(uint16_t memberId);
- // These are initialized in the constructor and remain static
+ // 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;
@@ -278,6 +311,7 @@ private:
const int32_t _z;
const uint16_t _id;
const std::vector<InetAddress> _zeroTierPhysicalEndpoints;
+ // end immutable fields
struct _Member
{
@@ -310,31 +344,23 @@ private:
_Member() { this->clear(); }
~_Member() { Utils::burn(key,sizeof(key)); }
};
-
- _Member *const _members; // cluster IDs can be from 0 to 65535 (16-bit)
+ _Member *const _members;
std::vector<uint16_t> _memberIds;
Mutex _memberIds_m;
- // Record tracking which members have which peers and how recently they claimed this -- also used to track our last claimed time
- struct _PeerAffinity
+ struct _PA
{
- _PeerAffinity(const Address &a,uint16_t mid,uint64_t ts) :
- key((a.toInt() << 16) | (uint64_t)mid),
- timestamp(ts) {}
-
- uint64_t key;
- uint64_t timestamp;
-
- inline Address address() const throw() { return Address(key >> 16); }
- inline uint16_t clusterMemberId() const throw() { return (uint16_t)(key & 0xffff); }
-
- inline bool operator<(const _PeerAffinity &pi) const throw() { return (key < pi.key); }
+ _PA() : ts(0),mid(0xffff) {}
+ uint64_t ts;
+ uint16_t mid;
};
-
- // A memory-efficient packed map of _PeerAffinity records searchable with std::binary_search() and std::lower_bound()
- std::vector<_PeerAffinity> _peerAffinities;
+ Hashtable< Address,_PA > _peerAffinities;
Mutex _peerAffinities_m;
+
+ uint64_t _lastCleanedPeerAffinities;
+ uint64_t _lastCheckedPeersForAnnounce;
+ uint64_t _lastFlushed;
};
} // namespace ZeroTier
diff --git a/node/Constants.hpp b/node/Constants.hpp
index e45602f7..552688a6 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -173,13 +173,8 @@
/**
* 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 -- do not change
@@ -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
@@ -269,28 +264,17 @@
/**
* Delay between ordinary case pings of direct links
*/
-#define ZT_PEER_DIRECT_PING_DELAY 120000
-
-/**
- * Delay between requests for updated network autoconf information
- */
-#define ZT_NETWORK_AUTOCONF_DELAY 60000
+#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 + (ZT_PING_CHECK_INVERVAL * 3))
+#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 4) + ZT_PING_CHECK_INVERVAL)
/**
- * 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.
+ * Delay between requests for updated network autoconf information
*/
-#define ZT_PEER_RELAY_CONVERSATION_LATENCY_THRESHOLD 10000
+#define ZT_NETWORK_AUTOCONF_DELAY 60000
/**
* 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
@@ -310,24 +294,9 @@
#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 120000
-
-/**
- * Minimum interval between direct path pushes from a given peer or we will ignore them
- */
-#define ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL 2500
+#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 1000
/**
* How long (max) to remember network certificates of membership?
@@ -353,9 +322,28 @@
#define ZT_MAX_BRIDGE_SPAM 16
/**
- * Maximum number of endpoints to contact per address type (to limit pushes like GitHub issue #235)
+ * 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_ENDPOINTS_PER_TYPE 8
+#define ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY 1
/**
* A test pseudo-network-ID that can be joined
diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp
index 1d8d9e5d..e3512fef 100644
--- a/node/Hashtable.hpp
+++ b/node/Hashtable.hpp
@@ -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;
}
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index c8526dfb..32229ba6 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -44,6 +44,8 @@
#include "SHA512.hpp"
#include "World.hpp"
#include "Cluster.hpp"
+#include "Node.hpp"
+#include "AntiRecursion.hpp"
namespace ZeroTier {
@@ -121,7 +123,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_OBJ_NOT_FOUND:
if (inReVerb == Packet::VERB_WHOIS) {
- if (RR->topology->isRoot(peer->identity()))
+ if (RR->topology->isUpstream(peer->identity()))
RR->sw->cancelWhoisRequest(Address(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH));
} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
@@ -154,6 +156,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
nconf->com().serialize(outp);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
}
@@ -201,13 +204,13 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
const uint64_t timestamp = at<uint64_t>(ZT_PROTO_VERB_HELLO_IDX_TIMESTAMP);
Identity id;
- InetAddress destAddr;
+ InetAddress externalSurfaceAddress;
uint64_t worldId = ZT_WORLD_ID_NULL;
uint64_t worldTimestamp = 0;
{
unsigned int ptr = ZT_PROTO_VERB_HELLO_IDX_IDENTITY + id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY);
if (ptr < size()) // ZeroTier One < 1.0.3 did not include physical destination address info
- ptr += destAddr.deserialize(*this,ptr);
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
if ((ptr + 16) <= size()) { // older versions also did not include World IDs or timestamps
worldId = at<uint64_t>(ptr); ptr += 8;
worldTimestamp = at<uint64_t>(ptr);
@@ -280,11 +283,8 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
// VALID -- if we made it here, packet passed identity and authenticity checks!
- peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP);
- peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision);
-
- if (destAddr)
- RR->sa->iam(id.address(),_remoteAddress,destAddr,RR->topology->isRoot(id),RR->node->now());
+ if (externalSurfaceAddress)
+ RR->sa->iam(id.address(),_remoteAddress,externalSurfaceAddress,RR->topology->isRoot(id),RR->node->now());
Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_HELLO);
@@ -294,7 +294,36 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
- _remoteAddress.serialize(outp);
+ if (protoVersion >= 5) {
+ _remoteAddress.serialize(outp);
+ } else {
+ /* LEGACY COMPATIBILITY HACK:
+ *
+ * For a while now (since 1.0.3), ZeroTier has recognized changes in
+ * its network environment empirically by examining its external network
+ * address as reported by trusted peers. In versions prior to 1.1.0
+ * (protocol version < 5), they did this by saving a snapshot of this
+ * information (in SelfAwareness.hpp) keyed by reporting device ID and
+ * address type.
+ *
+ * This causes problems when clustering is combined with symmetric NAT.
+ * Symmetric NAT remaps ports, so different endpoints in a cluster will
+ * report back different exterior addresses. Since the old code keys
+ * this by device ID and not sending physical address and compares the
+ * entire address including port, it constantly thinks its external
+ * surface is changing and resets connections when talking to a cluster.
+ *
+ * In new code we key by sending physical address and device and we also
+ * take the more conservative position of only interpreting changes in
+ * IP address (neglecting port) as a change in network topology that
+ * necessitates a reset. But we can make older clients work here by
+ * nulling out the port field. Since this info is only used for empirical
+ * detection of link changes, it doesn't break anything else.
+ */
+ InetAddress tmpa(_remoteAddress);
+ tmpa.setPort(0);
+ tmpa.serialize(outp);
+ }
if ((worldId != ZT_WORLD_ID_NULL)&&(RR->topology->worldTimestamp() > worldTimestamp)&&(worldId == RR->topology->worldId())) {
World w(RR->topology->world());
@@ -307,7 +336,11 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
}
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
+
+ peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision);
+ peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -331,12 +364,17 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO__OK__IDX_MINOR_VERSION];
const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO__OK__IDX_REVISION);
+ if (vProto < ZT_PROTO_VERSION_MIN) {
+ TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str());
+ return true;
+ }
+
const bool trusted = RR->topology->isRoot(peer->identity());
- InetAddress destAddr;
+ InetAddress externalSurfaceAddress;
unsigned int ptr = ZT_PROTO_VERB_HELLO__OK__IDX_REVISION + 2;
if (ptr < size()) // ZeroTier One < 1.0.3 did not include this field
- ptr += destAddr.deserialize(*this,ptr);
+ ptr += externalSurfaceAddress.deserialize(*this,ptr);
if ((trusted)&&((ptr + 2) <= size())) { // older versions also did not include this field, and right now we only use if from a root
World worldUpdate;
const unsigned int worldLen = at<uint16_t>(ptr); ptr += 2;
@@ -347,18 +385,13 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
}
}
- if (vProto < ZT_PROTO_VERSION_MIN) {
- TRACE("%s(%s): OK(HELLO) dropped, protocol version too old",source().toString().c_str(),_remoteAddress.toString().c_str());
- return true;
- }
-
- TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((destAddr) ? destAddr.toString().c_str() : "(none)"));
+ TRACE("%s(%s): OK(HELLO), version %u.%u.%u, latency %u, reported external address %s",source().toString().c_str(),_remoteAddress.toString().c_str(),vMajor,vMinor,vRevision,latency,((externalSurfaceAddress) ? externalSurfaceAddress.toString().c_str() : "(none)"));
peer->addDirectLatencyMeasurment(latency);
peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
- if (destAddr)
- RR->sa->iam(peer->address(),_remoteAddress,destAddr,trusted,RR->node->now());
+ if (externalSurfaceAddress)
+ RR->sa->iam(peer->address(),_remoteAddress,externalSurfaceAddress,trusted,RR->node->now());
} break;
case Packet::VERB_WHOIS: {
@@ -442,6 +475,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
outp.append(packetId());
queried.serialize(outp,false);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
} else {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
@@ -450,6 +484,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
outp.append(payload(),ZT_ADDRESS_LENGTH);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
} else {
@@ -607,6 +642,7 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
outp.append((unsigned char)Packet::VERB_ECHO);
outp.append((uint64_t)pid);
outp.append(field(ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD),size() - ZT_PACKET_IDX_PAYLOAD);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP);
} catch ( ... ) {
@@ -622,7 +658,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
// Iterate through 18-byte network,MAC,ADI tuples
for(unsigned int ptr=ZT_PACKET_IDX_PAYLOAD;ptr<size();ptr+=18) {
- const uint32_t nwid(at<uint64_t>(ptr));
+ const uint64_t nwid = at<uint64_t>(ptr);
const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
RR->mc->add(now,nwid,group,peer->address());
#ifdef ZT_ENABLE_CLUSTER
@@ -692,6 +728,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check
TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length());
} else {
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
}
@@ -704,6 +741,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
outp.append((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
outp.append(nwid);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
} break;
@@ -714,6 +752,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
outp.append((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
outp.append(nwid);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
} break;
@@ -736,6 +775,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
outp.append(nwid);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
} catch ( ... ) {
@@ -780,6 +820,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
outp.append((uint32_t)mg.adi());
if (RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit)) {
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
}
@@ -872,6 +913,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
}
@@ -888,17 +930,19 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
{
try {
const uint64_t now = RR->node->now();
- if ((now - peer->lastDirectPathPushReceived()) >= ZT_DIRECT_PATH_PUSH_MIN_RECEIVE_INTERVAL) {
- TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): too frequent!",source().toString().c_str(),_remoteAddress.toString().c_str());
+
+ // First, subject this to a rate limit
+ if (!peer->shouldRespondToDirectPathPush(now)) {
+ TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_remoteAddress.toString().c_str());
return true;
}
- peer->setLastDirectPathPushReceived(now);
- const RemotePath *currentBest = peer->getBestPath(now);
+ // Second, limit addresses by scope and type
+ uint8_t countPerScope[ZT_INETADDRESS_MAX_SCOPE+1][2]; // [][0] is v4, [][1] is v6
+ memset(countPerScope,0,sizeof(countPerScope));
unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
- unsigned int v4Count = 0,v6Count = 0;
while (count--) { // if ptr overflows Buffer will throw
// TODO: some flags are not yet implemented
@@ -913,20 +957,22 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
case 4: {
InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- if (v4Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) {
- if ((!currentBest)||(currentBest->address() != a))
- peer->attemptToContactAt(RR,_localAddress,a,RR->node->now());
+ if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
+ peer->attemptToContactAt(RR,_localAddress,a,now);
+ } else {
+ TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
}
} break;
case 6: {
InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) ) {
- TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
- if (v6Count++ < ZT_PUSH_DIRECT_PATHS_MAX_ENDPOINTS_PER_TYPE) {
- if ((!currentBest)||(currentBest->address() != a))
- peer->attemptToContactAt(RR,_localAddress,a,RR->node->now());
+ if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
+ TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
+ peer->attemptToContactAt(RR,_localAddress,a,now);
+ } else {
+ TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
}
} break;
@@ -1042,7 +1088,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
remainingHopsPtr += ZT_ADDRESS_LENGTH;
SharedPtr<Peer> nhp(RR->topology->getPeer(nextHop[h]));
if (nhp) {
- RemotePath *const rp = nhp->getBestPath(now);
+ Path *const rp = nhp->getBestPath(now);
if (rp)
nextHopBestPathAddress[h] = rp->address();
}
@@ -1178,6 +1224,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const
outp.append((uint16_t)sizeof(result));
outp.append(result,sizeof(result));
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
} else {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
@@ -1185,6 +1232,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const
outp.append(pid);
outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
} break;
@@ -1290,6 +1338,7 @@ void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,cons
outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
outp.append(nwid);
outp.armor(peer->key(),true);
+ RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index ecafcf51..c4d5cfda 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -43,12 +43,17 @@
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
{
@@ -320,7 +326,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()
@@ -333,27 +339,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;
}
diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp
index 6a8d6379..01e6b799 100644
--- a/node/Multicaster.cpp
+++ b/node/Multicaster.cpp
@@ -37,6 +37,7 @@
#include "Peer.hpp"
#include "C25519.hpp"
#include "CertificateOfMembership.hpp"
+#include "Node.hpp"
namespace ZeroTier {
@@ -174,129 +175,130 @@ void Multicaster::send(
unsigned long idxbuf[8194];
unsigned long *indexes = idxbuf;
- 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()];
-
- // 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;
+ 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()];
+
+ // 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;
+ 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); // optimization: don't use dedup log if it's a one-pass send
+ 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.sendOnly(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.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;
-
- 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());
-
- 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());
+ } else {
+ unsigned int gatherLimit = (limit - (unsigned int)gs.members.size()) + 1;
+
+ if ((gs.members.empty())||((now - gs.lastExplicitGather) >= ZT_MULTICAST_EXPLICIT_GATHER_DELAY)) {
+ gs.lastExplicitGather = now;
+ SharedPtr<Peer> r(RR->topology->getBestRoot());
+ if (r) {
+ TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
+
+ 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())&&(r->needsOurNetworkMembershipCertificate(nwid,now,true)))
+ com = &(nconf->com());
+ }
}
- }
- 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);
+ Packet outp(r->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(r->key(),true);
+ r->send(RR,outp.data(),outp.size(),now);
+ }
+ gatherLimit = 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;
+ 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)
diff --git a/node/Network.cpp b/node/Network.cpp
index cd30e386..afbe1074 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -37,6 +37,7 @@
#include "Packet.hpp"
#include "Buffer.hpp"
#include "NetworkController.hpp"
+#include "Node.hpp"
#include "../version.h"
diff --git a/node/NetworkConfig.cpp b/node/NetworkConfig.cpp
index cd32600f..35e23837 100644
--- a/node/NetworkConfig.cpp
+++ b/node/NetworkConfig.cpp
@@ -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;
}
diff --git a/node/Node.cpp b/node/Node.cpp
index 2b298903..82cb7ddb 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -263,7 +263,7 @@ public:
}
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
- } else if (p->alive(_now)) {
+ } 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);
}
@@ -305,24 +305,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 ( ... ) {
@@ -448,10 +437,10 @@ 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();
@@ -499,11 +488,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;
@@ -900,10 +889,10 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr)
} catch ( ... ) {}
}
-int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric, enum ZT_LocalInterfaceAddressTrust trust)
+int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr)
{
try {
- return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust);
+ return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr);
} catch ( ... ) {
return 0;
}
diff --git a/node/Node.hpp b/node/Node.hpp
index 4094a79e..9b85b832 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -105,7 +105,7 @@ 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 *));
@@ -207,7 +207,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;
@@ -285,7 +285,7 @@ private:
std::vector< ZT_CircuitTest * > _circuitTests;
Mutex _circuitTests_m;
- std::vector<Path> _directPaths;
+ std::vector<InetAddress> _directPaths;
Mutex _directPaths_m;
Mutex _backgroundTasksLock;
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 0e8426b6..63c49ce3 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -57,10 +57,11 @@
* + New crypto completely changes key agreement cipher
* 4 - 0.6.0 ... 1.0.6
* + New identity format based on hashcash design
- * 5 - 1.0.6 ... CURRENT
+ * 5 - 1.1.0 ... CURRENT
* + Supports circuit test, proof of work, and echo
- * + Supports in-band world (root definition) updates
- * + Otherwise backward compatible with 4
+ * + 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 5
@@ -924,9 +925,6 @@ public:
* 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
diff --git a/node/Path.cpp b/node/Path.cpp
new file mode 100644
index 00000000..e2475751
--- /dev/null
+++ b/node/Path.cpp
@@ -0,0 +1,45 @@
+/*
+ * 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 "Path.hpp"
+#include "AntiRecursion.hpp"
+#include "RuntimeEnvironment.hpp"
+#include "Node.hpp"
+
+namespace ZeroTier {
+
+bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+{
+ if (RR->node->putPacket(_localAddress,address(),data,len)) {
+ sent(now);
+ RR->antiRec->logOutgoingZT(data,len);
+ return true;
+ }
+ return false;
+}
+
+} // namespace ZeroTier
diff --git a/node/Path.hpp b/node/Path.hpp
index 6fa3c52e..39a18c43 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -28,12 +28,19 @@
#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"
namespace ZeroTier {
+class RuntimeEnvironment;
+
/**
* Base class for paths
*
@@ -42,45 +49,93 @@ namespace ZeroTier {
class Path
{
public:
+ Path() :
+ _lastSend(0),
+ _lastReceived(0),
+ _addr(),
+ _localAddress(),
+ _ipScope(InetAddress::IP_SCOPE_NONE)
+ {
+ }
+
+ Path(const InetAddress &localAddress,const InetAddress &addr) :
+ _lastSend(0),
+ _lastReceived(0),
+ _addr(addr),
+ _localAddress(localAddress),
+ _ipScope(addr.ipScope())
+ {
+ }
+
+ inline Path &operator=(const Path &p)
+ throw()
+ {
+ if (this != &p)
+ memcpy(this,&p,sizeof(Path));
+ return *this;
+ }
+
/**
- * 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.
+ * Called when a packet is sent to this remote path
*
- * Nearly all paths will be normal trust. The other levels are for high
- * performance local SDN use only.
+ * This is called automatically by Path::send().
*
- * These values MUST match ZT_LocalInterfaceAddressTrust in ZeroTierOne.h
+ * @param t Time of send
*/
- enum Trust // NOTE: max 255
+ inline void sent(uint64_t t)
+ throw()
{
- TRUST_NORMAL = 0,
- TRUST_PRIVACY = 10,
- TRUST_ULTIMATE = 20
- };
+ _lastSend = t;
+ }
- Path() :
- _addr(),
- _ipScope(InetAddress::IP_SCOPE_NONE),
- _trust(TRUST_NORMAL)
+ /**
+ * Called when a packet is received from this remote path
+ *
+ * @param t Time of receive
+ */
+ inline void received(uint64_t t)
+ throw()
{
+ _lastReceived = t;
}
- Path(const InetAddress &addr,int metric,Trust trust) :
- _addr(addr),
- _ipScope(addr.ipScope()),
- _trust(trust)
+ /**
+ * @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
*/
inline const InetAddress &address() const throw() { return _addr; }
@@ -105,11 +160,6 @@ public:
}
/**
- * @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()
@@ -157,10 +207,36 @@ public:
return false;
}
-protected:
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b) const
+ {
+ b.append((uint8_t)1); // version
+ b.append((uint64_t)_lastSend);
+ b.append((uint64_t)_lastReceived);
+ _addr.serialize(b);
+ _localAddress.serialize(b);
+ }
+
+ 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 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);
+ _ipScope = _addr.ipScope();
+ return (p - startAt);
+ }
+
+private:
+ uint64_t _lastSend;
+ uint64_t _lastReceived;
InetAddress _addr;
+ InetAddress _localAddress;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
- Trust _trust;
};
} // namespace ZeroTier
diff --git a/node/Peer.cpp b/node/Peer.cpp
index fa7b3aa4..9d0d78e5 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -35,6 +35,7 @@
#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Cluster.hpp"
+#include "Packet.hpp"
#include <algorithm>
@@ -54,7 +55,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_lastAnnouncedTo(0),
_lastPathConfirmationSent(0),
_lastDirectPathPushSent(0),
- _lastDirectPathPushReceived(0),
+ _lastDirectPathPushReceive(0),
_lastPathSort(0),
_vProto(0),
_vMajor(0),
@@ -63,6 +64,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
_id(peerIdentity),
_numPaths(0),
_latency(0),
+ _directPathPushCutoffCount(0),
_networkComs(4),
_lastPushedComs(4)
{
@@ -80,85 +82,130 @@ void Peer::received(
uint64_t inRePacketId,
Packet::Verb inReVerb)
{
+#ifdef ZT_ENABLE_CLUSTER
+ InetAddress redirectTo;
+ 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.
+ 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());
+ }
+ }
+ }
+#endif
+
const uint64_t now = RR->node->now();
bool needMulticastGroupAnnounce = false;
bool pathIsConfirmed = false;
- {
+ { // begin _lock
Mutex::Lock _l(_lock);
_lastReceive = now;
+ if ((verb == Packet::VERB_FRAME)||(verb == Packet::VERB_EXT_FRAME))
+ _lastUnicastFrame = now;
+ else if (verb == Packet::VERB_MULTICAST_FRAME)
+ _lastMulticastFrame = now;
- if (!hops) {
- /* 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;
- }
+#ifdef ZT_ENABLE_CLUSTER
+ // If we think this peer belongs elsewhere, don't learn this path or
+ // do other connection init stuff.
+ if (redirectTo)
+ return;
+#endif
+
+ if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
+ _lastAnnouncedTo = now;
+ needMulticastGroupAnnounce = true;
+ }
+
+ 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);
+ pathIsConfirmed = true;
+ break;
}
+ }
- if (!pathIsConfirmed) {
- if ((verb == Packet::VERB_OK)&&((inReVerb == Packet::VERB_HELLO)||(inReVerb == Packet::VERB_ECHO))) {
-
- // Learn paths if they've been confirmed via a HELLO or an ECHO
- RemotePath *slot = (RemotePath *)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 = RemotePath(localAddr,remoteAddr);
- slot->received(now);
- _numPaths = np;
- pathIsConfirmed = true;
- _sortPaths(now);
- }
+ if (!pathIsConfirmed) {
+ if ((verb == Packet::VERB_OK)||(RR->topology->amRoot())) {
+ Path *slot = (Path *)0;
+ if (np < ZT_MAX_PEER_NETWORK_PATHS) {
+ slot = &(_paths[np++]);
} 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);
+ 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);
+ _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 ((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;
- }
+ } // end _lock
#ifdef ZT_ENABLE_CLUSTER
- if ((pathIsConfirmed)&&(RR->cluster)) {
- // Either shuttle this peer off somewhere else or report to other members that we have it
- if (!RR->cluster->redirectPeer(_id.address(),remoteAddr,false))
- RR->cluster->replicateHavePeer(_id);
- }
+ if ((RR->cluster)&&(pathIsConfirmed))
+ RR->cluster->replicateHavePeer(_id,remoteAddr);
#endif
if (needMulticastGroupAnnounce) {
@@ -190,7 +237,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &lo
bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily)
{
- RemotePath *p = (RemotePath *)0;
+ Path *p = (Path *)0;
Mutex::Lock _l(_lock);
if (inetAddressFamily != 0) {
@@ -201,16 +248,16 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet
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());
+ //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());
attemptToContactAt(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());
+ //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());
+ //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;
}
@@ -218,7 +265,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet
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
@@ -231,23 +278,23 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_
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
@@ -255,7 +302,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:
@@ -267,6 +314,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;
@@ -277,13 +325,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;
@@ -456,7 +505,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) |
@@ -476,7 +525,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)
@@ -488,10 +537,10 @@ RemotePath *Peer::_getBestPath(const uint64_t now)
if (_paths[0].active(now))
return &(_paths[0]);
}
- return (RemotePath *)0;
+ return (Path *)0;
}
-RemotePath *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
+Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
{
// assumes _lock is locked
if ((now - _lastPathSort) >= ZT_PEER_PATH_SORT_INTERVAL)
@@ -503,7 +552,7 @@ RemotePath *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
}
_sortPaths(now);
}
- return (RemotePath *)0;
+ return (Path *)0;
}
} // namespace ZeroTier
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 0aca70b4..a70d9868 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -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"
@@ -135,7 +135,7 @@ public:
* @param now Current time
* @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;
}
/**
@@ -191,14 +191,14 @@ 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]);
@@ -206,32 +206,6 @@ public:
}
/**
- * @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
*/
inline uint64_t lastReceive() const throw() { return _lastReceive; }
@@ -257,20 +231,38 @@ 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
*
@@ -286,11 +278,6 @@ public:
}
/**
- * @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 direct path
*/
@@ -408,19 +395,49 @@ public:
bool needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool updateLastPushedTime);
/**
- * @return Time last direct path push was received
+ * Perform periodic cleaning operations
*/
- inline uint64_t lastDirectPathPushReceived() const throw() { return _lastDirectPathPushReceived; }
+ void clean(const RuntimeEnvironment *RR,uint64_t now);
/**
- * @param t New time of last direct path push received
+ * Remove all paths with this remote address
+ *
+ * @param addr Remote address to remove
*/
- inline void setLastDirectPathPushReceived(uint64_t t) throw() { _lastDirectPathPushReceived = t; }
+ 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;
+ }
/**
- * Perform periodic cleaning operations
+ * 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
*/
- void clean(const RuntimeEnvironment *RR,uint64_t now);
+ 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
@@ -461,13 +478,14 @@ public:
b.append((uint64_t)_lastAnnouncedTo);
b.append((uint64_t)_lastPathConfirmationSent);
b.append((uint64_t)_lastDirectPathPushSent);
- b.append((uint64_t)_lastDirectPathPushReceived);
+ b.append((uint64_t)_lastDirectPathPushReceive);
b.append((uint64_t)_lastPathSort);
b.append((uint16_t)_vProto);
b.append((uint16_t)_vMajor);
b.append((uint16_t)_vMinor);
b.append((uint16_t)_vRevision);
b.append((uint32_t)_latency);
+ b.append((uint16_t)_directPathPushCutoffCount);
b.append((uint16_t)_numPaths);
for(unsigned int i=0;i<_numPaths;++i)
@@ -531,13 +549,14 @@ public:
np->_lastAnnouncedTo = b.template at<uint64_t>(p); p += 8;
np->_lastPathConfirmationSent = b.template at<uint64_t>(p); p += 8;
np->_lastDirectPathPushSent = b.template at<uint64_t>(p); p += 8;
- np->_lastDirectPathPushReceived = b.template at<uint64_t>(p); p += 8;
+ np->_lastDirectPathPushReceive = b.template at<uint64_t>(p); p += 8;
np->_lastPathSort = b.template at<uint64_t>(p); p += 8;
np->_vProto = b.template at<uint16_t>(p); p += 2;
np->_vMajor = b.template at<uint16_t>(p); p += 2;
np->_vMinor = b.template at<uint16_t>(p); p += 2;
np->_vRevision = b.template at<uint16_t>(p); p += 2;
np->_latency = b.template at<uint32_t>(p); p += 4;
+ np->_directPathPushCutoffCount = b.template at<uint16_t>(p); p += 2;
const unsigned int numPaths = b.template at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numPaths;++i) {
@@ -545,7 +564,7 @@ public:
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);
}
}
@@ -569,8 +588,8 @@ public:
private:
void _sortPaths(const uint64_t now);
- RemotePath *_getBestPath(const uint64_t now);
- RemotePath *_getBestPath(const uint64_t now,int inetAddressFamily);
+ 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
@@ -581,16 +600,17 @@ private:
uint64_t _lastAnnouncedTo;
uint64_t _lastPathConfirmationSent;
uint64_t _lastDirectPathPushSent;
- uint64_t _lastDirectPathPushReceived;
+ 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
{
diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp
deleted file mode 100644
index 8b37621a..00000000
--- a/node/RemotePath.hpp
+++ /dev/null
@@ -1,161 +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"
-
-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) :
- Path(addr,0,TRUST_NORMAL),
- _lastSend(0),
- _lastReceived(0),
- _localAddress(localAddress),
- _flags(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; }
-
- /**
- * 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 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
- */
- 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
diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp
index 1b70f17c..ce75eb03 100644
--- a/node/SelfAwareness.cpp
+++ b/node/SelfAwareness.cpp
@@ -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,35 +79,35 @@ 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)];
- 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 in this scope that were not reported by this remote address to prevent 'thrashing'
+ // 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.
{
@@ -118,26 +120,21 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi
}
}
+ // 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;
}
}
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 0edaa96d..b7a9c522 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -295,17 +295,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;
@@ -315,14 +316,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;
@@ -449,8 +442,8 @@ 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 {
@@ -546,7 +539,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);
}
}
@@ -569,7 +562,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
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())))
+ if ((RR->cluster)&&(RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false)))
return; // sent by way of another member of this cluster
#endif
@@ -632,11 +625,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()) {
@@ -645,17 +644,18 @@ 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)))) {
+ if (_shouldTryUnite(now,source,destination))
+ unite(source,destination);
} else {
#ifdef ZT_ENABLE_CLUSTER
- if ((RR->cluster)&&(RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size())))
+ if ((RR->cluster)&&(RR->cluster->sendViaCluster(source,destination,packet->data(),packet->size(),_shouldTryUnite(now,source,destination))))
return; // sent by way of another member of this cluster
#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());
@@ -670,7 +670,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)
@@ -736,17 +736,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);
+ }
}
}
}
@@ -798,4 +801,14 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
return false;
}
+bool Switch::_shouldTryUnite(const uint64_t now,const Address &p1,const Address &p2)
+{
+ Mutex::Lock _l(_lastUniteAttempt_m);
+ uint64_t &luts = _lastUniteAttempt[_LastUniteKey(p1,p2)];
+ if ((now - luts) < ZT_MIN_UNITE_INTERVAL)
+ return false;
+ luts = now;
+ return true;
+}
+
} // namespace ZeroTier
diff --git a/node/Switch.hpp b/node/Switch.hpp
index 3bdc0c47..42e87ca5 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -127,15 +127,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
@@ -185,6 +180,7 @@ private:
void _handleRemotePacketHead(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
+ bool _shouldTryUnite(const uint64_t now,const Address &p1,const Address &p2);
const RuntimeEnvironment *const RR;
uint64_t _lastBeaconResponse;
diff --git a/node/Topology.cpp b/node/Topology.cpp
index e56d1f47..bea97ab9 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -35,8 +35,12 @@
namespace ZeroTier {
-#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,0x4f,0xdf,0xbf,0xfc,0xbb,0x6c,0x7e,0x15,0x67,0x85,0x1b,0xb4,0x65,0x04,0x01,0xaf,0x56,0xbf,0xe7,0x63,0x9d,0x77,0xef,0xa4,0x1e,0x61,0x53,0x88,0xcb,0x8d,0x78,0xe5,0x47,0x38,0x98,0x5a,0x6c,0x8a,0xdd,0xe6,0x9c,0x65,0xdf,0x1a,0x80,0x63,0xce,0x2e,0x4d,0x48,0x24,0x3d,0x68,0x87,0x96,0x13,0x89,0xba,0x25,0x6f,0xc9,0xb0,0x9f,0x20,0xc5,0x4c,0x51,0x7b,0x30,0xb7,0x5f,0xba,0xca,0xa4,0xc5,0x48,0xa3,0x15,0xab,0x2f,0x1d,0x64,0xe8,0x04,0x42,0xb3,0x1c,0x51,0x8b,0x2a,0x04,0x01,0xf8,0xe1,0x81,0xaf,0x60,0x2f,0x70,0x3e,0xcd,0x0b,0x21,0x38,0x19,0x62,0x02,0xbd,0x0e,0x33,0x1d,0x0a,0x7b,0xf1,0xec,0xad,0xef,0x54,0xb3,0x7b,0x17,0x84,0xaa,0xda,0x0a,0x85,0x5d,0x0b,0x1c,0x05,0x83,0xb9,0x0e,0x3e,0xe3,0xb4,0xd1,0x8b,0x5b,0x64,0xf7,0xcf,0xe1,0xff,0x5d,0xc2,0x2a,0xcf,0x60,0x7b,0x09,0xb4,0xa3,0x86,0x3c,0x5a,0x7e,0x31,0xa0,0xc7,0xb4,0x86,0xe3,0x41,0x33,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};
+// Old root servers
+//#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,0x4f,0xdf,0xbf,0xfc,0xbb,0x6c,0x7e,0x15,0x67,0x85,0x1b,0xb4,0x65,0x04,0x01,0xaf,0x56,0xbf,0xe7,0x63,0x9d,0x77,0xef,0xa4,0x1e,0x61,0x53,0x88,0xcb,0x8d,0x78,0xe5,0x47,0x38,0x98,0x5a,0x6c,0x8a,0xdd,0xe6,0x9c,0x65,0xdf,0x1a,0x80,0x63,0xce,0x2e,0x4d,0x48,0x24,0x3d,0x68,0x87,0x96,0x13,0x89,0xba,0x25,0x6f,0xc9,0xb0,0x9f,0x20,0xc5,0x4c,0x51,0x7b,0x30,0xb7,0x5f,0xba,0xca,0xa4,0xc5,0x48,0xa3,0x15,0xab,0x2f,0x1d,0x64,0xe8,0x04,0x42,0xb3,0x1c,0x51,0x8b,0x2a,0x04,0x01,0xf8,0xe1,0x81,0xaf,0x60,0x2f,0x70,0x3e,0xcd,0x0b,0x21,0x38,0x19,0x62,0x02,0xbd,0x0e,0x33,0x1d,0x0a,0x7b,0xf1,0xec,0xad,0xef,0x54,0xb3,0x7b,0x17,0x84,0xaa,0xda,0x0a,0x85,0x5d,0x0b,0x1c,0x05,0x83,0xb9,0x0e,0x3e,0xe3,0xb4,0xd1,0x8b,0x5b,0x64,0xf7,0xcf,0xe1,0xff,0x5d,0xc2,0x2a,0xcf,0x60,0x7b,0x09,0xb4,0xa3,0x86,0x3c,0x5a,0x7e,0x31,0xa0,0xc7,0xb4,0x86,0xe3,0x41,0x33,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};
+
+#define ZT_DEFAULT_WORLD_LENGTH 582
+static const unsigned char ZT_DEFAULT_WORLD[ZT_DEFAULT_WORLD_LENGTH] = {0x01,0x00,0x00,0x00,0x00,0x08,0xea,0xc9,0x0a,0x00,0x00,0x01,0x50,0xa6,0x54,0xe4,0x8e,0x72,0xb0,0x3b,0xbe,0x73,0xda,0xbd,0xfb,0x85,0x77,0x9f,0xc9,0x2e,0x17,0xc8,0x11,0x6e,0xda,0x61,0x80,0xd1,0x41,0xcb,0x7c,0x2d,0x2b,0xa4,0x34,0x75,0x19,0x64,0x20,0x80,0x0a,0x22,0x32,0xf2,0x01,0x6c,0xfe,0x79,0xa6,0x7d,0xec,0x10,0x7e,0x03,0xf3,0xa2,0xa0,0x19,0xc8,0x7c,0xfd,0x6c,0x56,0x52,0xa8,0xfb,0xdc,0xfb,0x93,0x81,0x3e,0xe4,0xe9,0x51,0xc1,0xe1,0x39,0x50,0xcd,0x17,0x82,0x9d,0x74,0xf1,0xa9,0x5b,0x03,0x14,0x2c,0xa7,0xc0,0x7f,0x21,0x8b,0xad,0xdd,0xa5,0x04,0x26,0x35,0xa6,0xab,0xc1,0x49,0x64,0x2c,0xda,0x65,0x52,0x77,0xf3,0xf0,0x70,0x00,0xcd,0xc3,0xff,0x3b,0x19,0x77,0x4c,0xab,0xb6,0x35,0xbb,0x77,0xcf,0x54,0xe5,0x6d,0x01,0x9d,0x43,0x92,0x0a,0x6d,0x00,0x23,0x8e,0x0a,0x3d,0xba,0x36,0xc3,0xa1,0xa4,0xad,0x13,0x8f,0x46,0xff,0xcc,0x8f,0x9e,0xc2,0x3c,0x06,0xf8,0x3b,0xf3,0xa2,0x5f,0x71,0xcc,0x07,0x35,0x7f,0x02,0xd6,0xdd,0xca,0x6a,0xb5,0x00,0x4e,0x76,0x12,0x07,0xd8,0xb4,0x20,0x0b,0xe4,0x4f,0x47,0x8e,0x3d,0xa1,0x48,0xc1,0x60,0x99,0x11,0x0e,0xe7,0x1b,0x64,0x58,0x6d,0xda,0x11,0x8e,0x40,0x22,0xab,0x63,0x68,0x2c,0xe1,0x37,0xda,0x8b,0xa8,0x17,0xfc,0x7f,0x73,0xaa,0x31,0x63,0xf2,0xe3,0x33,0x93,0x3e,0x29,0x94,0xc4,0x6b,0x4f,0x41,0x19,0x30,0x7b,0xe8,0x85,0x5a,0x72,0x00,0x0a,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,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,0x68,0xee,0xb6,0x53,0x27,0x09,0x06,0x20,0x01,0x19,0xf0,0xac,0x00,0x08,0x09,0x54,0x00,0x00,0xff,0xfe,0x15,0xf3,0xf4,0x27,0x09,0x04,0x80,0xc7,0xb6,0x09,0x27,0x09,0x06,0x24,0x00,0x61,0x80,0x00,0x00,0x00,0xd0,0x00,0x00,0x00,0x00,0x00,0x1b,0x10,0x01,0x27,0x09,0x16,0xeb,0xbd,0x6c,0x5d,0x00,0x47,0xd3,0x9b,0xca,0x9d,0x0a,0x5c,0xf7,0x01,0x48,0xe3,0x9f,0x6c,0x45,0x19,0x9e,0x17,0xe0,0xe3,0x2e,0x4e,0x46,0xca,0xc0,0x1a,0xe5,0xbc,0xb2,0x12,0x24,0x13,0x7b,0x09,0x7f,0x40,0xbd,0xd9,0x82,0xa9,0x21,0xc3,0xaa,0xbd,0xcb,0x9a,0xda,0x8b,0x4f,0x2b,0xb0,0x59,0x37,0x53,0xbf,0xdb,0x21,0xcf,0x12,0xea,0xc2,0x8c,0x8d,0x90,0x42,0x00,0x0a,0x04,0x2d,0x21,0x04,0x43,0x27,0x09,0x06,0x26,0x00,0x3c,0x00,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0xb7,0x04,0x27,0x09,0x04,0x8b,0xa2,0x9d,0xf3,0x27,0x09,0x06,0x2a,0x01,0x7e,0x01,0x00,0x00,0x00,0x00,0xf0,0x3c,0x91,0xff,0xfe,0x67,0x3f,0xfd,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};
Topology::Topology(const RuntimeEnvironment *renv) :
RR(renv),
@@ -61,7 +65,7 @@ Topology::Topology(const RuntimeEnvironment *renv) :
if (!p)
break; // stop if invalid records
if (p->address() != RR->identity.address())
- _peers[p->address()] = p;
+ _peers.set(p->address(),p);
} catch ( ... ) {
break; // stop if invalid records
}
@@ -116,10 +120,14 @@ Topology::~Topology()
SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
{
- if (peer->address() == RR->identity.address()) {
- TRACE("BUG: addPeer() 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;
{
@@ -129,6 +137,7 @@ SharedPtr<Peer> Topology::addPeer(const SharedPtr<Peer> &peer)
hp = peer;
np = hp;
}
+
np->use(RR->node->now());
saveIdentity(np->identity());
@@ -142,26 +151,33 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
return SharedPtr<Peer>();
}
- Mutex::Lock _l(_lock);
-
- SharedPtr<Peer> &ap = _peers[zta];
-
- if (ap) {
- ap->use(RR->node->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(RR->node->now());
- return ap;
- } catch ( ... ) {} // invalid identity?
- }
+ 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?
- // If we get here it means we read an invalid cache identity or had some other error
- _peers.erase(zta);
return SharedPtr<Peer>();
}
@@ -169,9 +185,9 @@ Identity Topology::getIdentity(const Address &zta)
{
{
Mutex::Lock _l(_lock);
- SharedPtr<Peer> &ap = _peers[zta];
+ const SharedPtr<Peer> *const ap = _peers.get(zta);
if (ap)
- return ap->identity();
+ return (*ap)->identity();
}
return _getIdentity(zta);
}
@@ -187,7 +203,6 @@ void Topology::saveIdentity(const Identity &id)
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);
@@ -197,90 +212,58 @@ 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 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;
- }
- }
+ if (bestNotAvoid) {
+ (*bestNotAvoid)->use(now);
+ return *bestNotAvoid;
+ } else if ((!strictAvoid)&&(bestOverall)) {
+ (*bestOverall)->use(now);
+ return *bestOverall;
}
+
}
- if (bestRoot)
- bestRoot->use(now);
- return bestRoot;
+ return SharedPtr<Peer>();
}
bool Topology::isUpstream(const Identity &id) const
@@ -332,7 +315,7 @@ void Topology::clean(uint64_t now)
}
}
-unsigned long Topology::countAlive() const
+unsigned long Topology::countActive() const
{
const uint64_t now = RR->node->now();
unsigned long cnt = 0;
@@ -341,8 +324,7 @@ unsigned long Topology::countAlive() const
Address *a = (Address *)0;
SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
while (i.next(a,p)) {
- if ((*p)->alive(now))
- ++cnt;
+ cnt += (unsigned long)((*p)->hasActiveDirectPath(now));
}
return cnt;
}
diff --git a/node/Topology.hpp b/node/Topology.hpp
index ee9827b9..a0c28b0f 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -79,6 +79,26 @@ public:
SharedPtr<Peer> getPeer(const Address &zta);
/**
+ * 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
+ * @param now Current time
+ */
+ inline SharedPtr<Peer> getPeerNoCache(const Address &zta,const uint64_t now)
+ {
+ Mutex::Lock _l(_lock);
+ 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
@@ -97,23 +117,11 @@ public:
void saveIdentity(const Identity &id);
/**
- * @return Vector of peers that are root servers
- */
- inline std::vector< SharedPtr<Peer> > rootPeers() const
- {
- Mutex::Lock _l(_lock);
- return _rootPeers;
- }
-
- /**
* 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
@@ -193,9 +201,9 @@ public:
void clean(uint64_t now);
/**
- * @return Number of 'alive' peers
+ * @return Number of peers with active direct paths
*/
- unsigned long countAlive() const;
+ unsigned long countActive() const;
/**
* Apply a function or function object to all peers
@@ -217,8 +225,15 @@ 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));
+ }
}
/**