summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--attic/big-http-test/2015-11-10_01_50000.out.xz (renamed from tests/http/2015-11-10_01_50000.out.xz)bin730360 -> 730360 bytes
-rw-r--r--attic/big-http-test/2015-11-10_02_50000.out.xz (renamed from tests/http/2015-11-10_02_50000.out.xz)bin2373664 -> 2373664 bytes
-rw-r--r--attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz (renamed from tests/http/2015-11-10_03_12500_ec2-east-only.out.xz)bin802932 -> 802932 bytes
-rw-r--r--attic/big-http-test/Dockerfile (renamed from tests/http/Dockerfile)0
-rw-r--r--attic/big-http-test/README.md (renamed from tests/http/README.md)0
-rw-r--r--attic/big-http-test/agent.js (renamed from tests/http/agent.js)0
-rwxr-xr-xattic/big-http-test/big-test-kill.sh (renamed from tests/http/big-test-kill.sh)0
-rwxr-xr-xattic/big-http-test/big-test-start.sh (renamed from tests/http/big-test-start.sh)0
-rw-r--r--attic/big-http-test/crunch-results.js (renamed from tests/http/crunch-results.js)0
-rwxr-xr-xattic/big-http-test/docker-main.sh (renamed from tests/http/docker-main.sh)0
-rw-r--r--attic/big-http-test/nodesource-el.repo (renamed from tests/http/nodesource-el.repo)0
-rw-r--r--attic/big-http-test/package.json (renamed from tests/http/package.json)0
-rw-r--r--attic/big-http-test/server.js (renamed from tests/http/server.js)0
-rw-r--r--include/ZeroTierOne.h64
-rw-r--r--node/AntiRecursion.hpp133
-rw-r--r--node/Constants.hpp12
-rw-r--r--node/IncomingPacket.cpp74
-rw-r--r--node/InetAddress.cpp24
-rw-r--r--node/InetAddress.hpp8
-rw-r--r--node/Node.cpp40
-rw-r--r--node/Node.hpp9
-rw-r--r--node/Packet.hpp31
-rw-r--r--node/Path.cpp2
-rw-r--r--node/Path.hpp59
-rw-r--r--node/Peer.cpp93
-rw-r--r--node/Peer.hpp34
-rw-r--r--node/RuntimeEnvironment.hpp3
-rw-r--r--node/SelfAwareness.cpp8
-rw-r--r--node/Switch.cpp40
-rw-r--r--node/Switch.hpp9
-rw-r--r--node/Topology.cpp8
-rw-r--r--service/OneService.cpp37
-rw-r--r--version.h2
33 files changed, 354 insertions, 336 deletions
diff --git a/tests/http/2015-11-10_01_50000.out.xz b/attic/big-http-test/2015-11-10_01_50000.out.xz
index d3e2a666..d3e2a666 100644
--- a/tests/http/2015-11-10_01_50000.out.xz
+++ b/attic/big-http-test/2015-11-10_01_50000.out.xz
Binary files differ
diff --git a/tests/http/2015-11-10_02_50000.out.xz b/attic/big-http-test/2015-11-10_02_50000.out.xz
index 0154da79..0154da79 100644
--- a/tests/http/2015-11-10_02_50000.out.xz
+++ b/attic/big-http-test/2015-11-10_02_50000.out.xz
Binary files differ
diff --git a/tests/http/2015-11-10_03_12500_ec2-east-only.out.xz b/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
index 3ae3555e..3ae3555e 100644
--- a/tests/http/2015-11-10_03_12500_ec2-east-only.out.xz
+++ b/attic/big-http-test/2015-11-10_03_12500_ec2-east-only.out.xz
Binary files differ
diff --git a/tests/http/Dockerfile b/attic/big-http-test/Dockerfile
index e19b3fee..e19b3fee 100644
--- a/tests/http/Dockerfile
+++ b/attic/big-http-test/Dockerfile
diff --git a/tests/http/README.md b/attic/big-http-test/README.md
index 23a95605..23a95605 100644
--- a/tests/http/README.md
+++ b/attic/big-http-test/README.md
diff --git a/tests/http/agent.js b/attic/big-http-test/agent.js
index 9ab2e019..9ab2e019 100644
--- a/tests/http/agent.js
+++ b/attic/big-http-test/agent.js
diff --git a/tests/http/big-test-kill.sh b/attic/big-http-test/big-test-kill.sh
index fa7f3cc4..fa7f3cc4 100755
--- a/tests/http/big-test-kill.sh
+++ b/attic/big-http-test/big-test-kill.sh
diff --git a/tests/http/big-test-start.sh b/attic/big-http-test/big-test-start.sh
index 2411eeda..2411eeda 100755
--- a/tests/http/big-test-start.sh
+++ b/attic/big-http-test/big-test-start.sh
diff --git a/tests/http/crunch-results.js b/attic/big-http-test/crunch-results.js
index 50e5c49a..50e5c49a 100644
--- a/tests/http/crunch-results.js
+++ b/attic/big-http-test/crunch-results.js
diff --git a/tests/http/docker-main.sh b/attic/big-http-test/docker-main.sh
index 29cdced9..29cdced9 100755
--- a/tests/http/docker-main.sh
+++ b/attic/big-http-test/docker-main.sh
diff --git a/tests/http/nodesource-el.repo b/attic/big-http-test/nodesource-el.repo
index b785d3d0..b785d3d0 100644
--- a/tests/http/nodesource-el.repo
+++ b/attic/big-http-test/nodesource-el.repo
diff --git a/tests/http/package.json b/attic/big-http-test/package.json
index 173a6f99..173a6f99 100644
--- a/tests/http/package.json
+++ b/attic/big-http-test/package.json
diff --git a/tests/http/server.js b/attic/big-http-test/server.js
index 629784da..629784da 100644
--- a/tests/http/server.js
+++ b/attic/big-http-test/server.js
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index 377af63f..e087904f 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -1097,13 +1097,42 @@ typedef int (*ZT_DataStorePutFunction)(
* delivery. It only means that the packet appears to have been sent.
*/
typedef int (*ZT_WirePacketSendFunction)(
- ZT_Node *, /* Node */
- void *, /* User ptr */
- const struct sockaddr_storage *, /* Local address */
- const struct sockaddr_storage *, /* Remote address */
- const void *, /* Packet data */
- unsigned int, /* Packet length */
- unsigned int); /* TTL or 0 to use default */
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ const struct sockaddr_storage *, /* Local address */
+ const struct sockaddr_storage *, /* Remote address */
+ const void *, /* Packet data */
+ unsigned int, /* Packet length */
+ unsigned int); /* TTL or 0 to use default */
+
+/**
+ * Function to check whether a path should be used for ZeroTier traffic
+ *
+ * Paramters:
+ * (1) Node
+ * (2) User pointer
+ * (3) Local interface address
+ * (4) Remote address
+ *
+ * This function must return nonzero (true) if the path should be used.
+ *
+ * If no path check function is specified, ZeroTier will still exclude paths
+ * that overlap with ZeroTier-assigned and managed IP address blocks. But the
+ * use of a path check function is recommended to ensure that recursion does
+ * not occur in cases where addresses are assigned by the OS or managed by
+ * an out of band mechanism like DHCP. The path check function should examine
+ * all configured ZeroTier interfaces and check to ensure that the supplied
+ * addresses will not result in ZeroTier traffic being sent over a ZeroTier
+ * interface (recursion).
+ *
+ * Obviously this is not required in configurations where this can't happen,
+ * such as network containers or embedded.
+ */
+typedef int (*ZT_PathCheckFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
+ const struct sockaddr_storage *, /* Local address */
+ const struct sockaddr_storage *); /* Remote address */
/****************************************************************************/
/* C Node API */
@@ -1121,6 +1150,7 @@ typedef int (*ZT_WirePacketSendFunction)(
* @param dataStoreGetFunction Function called to get objects from persistent storage
* @param dataStorePutFunction Function called to put objects in persistent storage
* @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change
+ * @param pathCheckFunction A function to check whether a path should be used for ZeroTier traffic, or NULL to allow any path
* @param eventCallback Function to receive status updates and non-fatal error notices
* @return OK (0) or error code if a fatal error condition has occurred
*/
@@ -1133,6 +1163,7 @@ enum ZT_ResultCode ZT_Node_new(
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+ ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback);
/**
@@ -1334,18 +1365,21 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
/**
* Add a local interface address
*
- * Take care that these are never ZeroTier interface addresses, otherwise
- * strange things might happen or they simply won't work.
+ * This is used to make ZeroTier aware of those local interface addresses
+ * that you wish to use for ZeroTier communication. This is optional, and if
+ * it is not used ZeroTier will rely upon upstream peers (and roots) to
+ * perform empirical address discovery and NAT traversal. But the use of this
+ * method is recommended as it improves peer discovery when both peers are
+ * on the same LAN.
*
- * Addresses can also be added here if they are the result of a UPnP or
- * NAT-PMP port mapping or other discovery or mapping means.
+ * It is the responsibility of the caller to take care that these are never
+ * ZeroTier interface addresses, whether these are assigned by ZeroTier or
+ * are otherwise assigned to an interface managed by this ZeroTier instance.
+ * This can cause recursion or other undesirable behavior.
*
* This returns a boolean indicating whether or not the address was
* accepted. ZeroTier will only communicate over certain address types
- * and (for IP) address classes. Thus it's safe to just dump your OS's
- * entire remote IP list (excluding ZeroTier interface IPs) into here
- * and let ZeroTier determine which addresses it will use. It will
- * reject bad, empty, and unusable addresses.
+ * and (for IP) address classes.
*
* @param addr Local interface address
* @return Boolean: non-zero if address was accepted and added
diff --git a/node/AntiRecursion.hpp b/node/AntiRecursion.hpp
deleted file mode 100644
index 4d9df465..00000000
--- a/node/AntiRecursion.hpp
+++ /dev/null
@@ -1,133 +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_ANTIRECURSION_HPP
-#define ZT_ANTIRECURSION_HPP
-
-#include <string.h>
-#include <stdlib.h>
-
-#include "Constants.hpp"
-
-namespace ZeroTier {
-
-/**
- * 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 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.
- *
- * This is highly optimized code since it's checked for every packet.
- */
-class AntiRecursion
-{
-public:
- AntiRecursion()
- {
- for(int i=0;i<ZT_ANTIRECURSION_HISTORY_SIZE;++i) {
- _history[i].tail[0] = 0;
- _history[i].tail[1] = 0;
- _history[i].tail[2] = 0;
- _history[i].tail[3] = 0;
- }
- _ptr = 0;
- }
-
- /**
- * Add an outgoing ZeroTier packet to the circular log
- *
- * @param data ZT packet data
- * @param len Length of packet
- */
- inline void logOutgoingZT(const void *const data,const unsigned int len)
- {
- 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
- }
-
- /**
- * Check an ethernet frame from a local tap against anti-recursion history
- *
- * @param data Raw frame data
- * @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 *const data,const unsigned int len) const
- {
- 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 { uint64_t tail[4]; };
- _ArItem _history[ZT_ANTIRECURSION_HISTORY_SIZE];
- volatile unsigned long _ptr;
-};
-
-} // namespace ZeroTier
-
-#endif
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 7368a634..a2ba1c1a 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -264,7 +264,7 @@
/**
* Delay between ordinary case pings of direct links
*/
-#define ZT_PEER_DIRECT_PING_DELAY 60000
+#define ZT_PEER_DIRECT_PING_DELAY 90000
/**
* Timeout for overall peer activity (measured from last receive)
@@ -272,6 +272,16 @@
#define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 4) + ZT_PING_CHECK_INVERVAL)
/**
+ * No answer timeout to trigger dead path detection
+ */
+#define ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT 2500
+
+/**
+ * Probation threshold after which a path becomes dead
+ */
+#define ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION 3
+
+/**
* Delay between requests for updated network autoconf information
*/
#define ZT_NETWORK_AUTOCONF_DELAY 60000
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 781ba202..c63d70b7 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -45,7 +45,6 @@
#include "World.hpp"
#include "Cluster.hpp"
#include "Node.hpp"
-#include "AntiRecursion.hpp"
#include "DeferredPackets.hpp"
namespace ZeroTier {
@@ -86,7 +85,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
switch(v) {
//case Packet::VERB_NOP:
default: // ignore unknown verbs, but if they pass auth check they are "received"
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),v,0,Packet::VERB_NOP);
return true;
case Packet::VERB_HELLO: return _doHELLO(RR,peer);
@@ -163,7 +162,6 @@ 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());
}
}
@@ -185,7 +183,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
default: break;
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb);
} catch ( ... ) {
TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -279,7 +277,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
}
// Check packet integrity and authentication
- SharedPtr<Peer> newPeer(new Peer(RR->identity,id));
+ SharedPtr<Peer> newPeer(new Peer(RR,RR->identity,id));
if (!dearmor(newPeer->key())) {
TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_remoteAddress.toString().c_str());
return true;
@@ -345,11 +343,10 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
}
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); // important for this to go first so received() knows the version
- peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP);
+ peer->received(_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());
}
@@ -410,7 +407,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
// roots. In the future it should be done if we query less trusted
// sources.
//if (id.locallyValidate())
- RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR->identity,id))));
+ RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
}
} break;
@@ -450,7 +447,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
// OK(MULTICAST_FRAME) includes certificate of membership update
CertificateOfMembership com;
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
- peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com);
+ peer->validateAndSetNetworkMembershipCertificate(nwid,com);
}
if ((flags & 0x02) != 0) {
@@ -465,7 +462,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
default: break;
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb);
} catch ( ... ) {
TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -483,7 +480,6 @@ 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 {
#ifdef ZT_ENABLE_CLUSTER
@@ -494,7 +490,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
} else {
TRACE("dropped WHOIS from %s(%s): missing or invalid address",source().toString().c_str(),_remoteAddress.toString().c_str());
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -511,10 +507,12 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP);
+
InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
TRACE("RENDEZVOUS from %s says %s might be at %s, starting NAT-t",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP);
- RR->sw->rendezvous(withPeer,_localAddress,atAddr);
+ if (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,atAddr))
+ RR->sw->rendezvous(withPeer,_localAddress,atAddr);
} else {
TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -553,7 +551,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
RR->node->putFrame(network->id(),MAC(peer->address(),network->id()),network->mac(),etherType,0,field(ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP);
} else {
TRACE("dropped FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
}
@@ -575,7 +573,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
if ((flags & 0x01) != 0) {
CertificateOfMembership com;
comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(RR,network->id(),com);
+ peer->validateAndSetNetworkMembershipCertificate(network->id(),com);
}
if (!network->isAllowed(peer)) {
@@ -624,7 +622,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
RR->node->putFrame(network->id(),from,to,etherType,0,field(comLen + ZT_PROTO_VERB_EXT_FRAME_IDX_PAYLOAD,payloadLen),payloadLen);
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP);
} else {
TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
}
@@ -644,9 +642,8 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
if (size() > ZT_PACKET_IDX_PAYLOAD)
outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
outp.armor(peer->key(),true);
- 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);
+ peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -665,7 +662,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
RR->mc->add(now,nwid,group,peer->address());
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -680,10 +677,10 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment
unsigned int ptr = ZT_PACKET_IDX_PAYLOAD;
while (ptr < size()) {
ptr += com.deserialize(*this,ptr);
- peer->validateAndSetNetworkMembershipCertificate(RR,com.networkId(),com);
+ peer->validateAndSetNetworkMembershipCertificate(com.networkId(),com);
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped NETWORK_MEMBERSHIP_CERTIFICATE from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -700,7 +697,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
const unsigned int h = hops();
const uint64_t pid = packetId();
- peer->received(RR,_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,h,pid,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP);
if (RR->localNetworkController) {
Dictionary netconf;
@@ -722,7 +719,6 @@ 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());
}
}
@@ -735,7 +731,6 @@ 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;
@@ -746,7 +741,6 @@ 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;
@@ -769,7 +763,6 @@ 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 ( ... ) {
@@ -789,7 +782,7 @@ bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,cons
nw->requestConfiguration();
ptr += 8;
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -815,7 +808,6 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
if (gatheredLocally) {
outp.armor(peer->key(),true);
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
}
@@ -825,7 +817,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
#endif
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -846,7 +838,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
if ((flags & 0x01) != 0) {
CertificateOfMembership com;
offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
- peer->validateAndSetNetworkMembershipCertificate(RR,nwid,com);
+ peer->validateAndSetNetworkMembershipCertificate(nwid,com);
}
// Check membership after we've read any included COM, since
@@ -909,13 +901,12 @@ 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());
}
}
} // else ignore -- not a member of this network
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -952,10 +943,10 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
switch(addrType) {
case 4: {
InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
- if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) ) {
+ if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
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->sendHELLO(RR,_localAddress,a,now);
+ peer->sendHELLO(_localAddress,a,now);
} else {
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
@@ -963,10 +954,10 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
} break;
case 6: {
InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
- if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) ) {
+ if ( ((flags & 0x01) == 0) && (Path::isAddressValidForPath(a)) && (!peer->hasActivePathTo(now,a)) && (RR->node->shouldUsePathForZeroTierTraffic(_localAddress,a)) ) {
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->sendHELLO(RR,_localAddress,a,now);
+ peer->sendHELLO(_localAddress,a,now);
} else {
TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
}
@@ -976,7 +967,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
ptr += addrLen;
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -1143,7 +1134,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
}
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP);
} catch ( ... ) {
TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -1220,7 +1211,6 @@ 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);
@@ -1228,7 +1218,6 @@ 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;
@@ -1238,7 +1227,7 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const
break;
}
- peer->received(RR,_localAddress,_remoteAddress,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP);
+ peer->received(_localAddress,_remoteAddress,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP);
} else {
TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}
@@ -1334,7 +1323,6 @@ 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.cpp b/node/InetAddress.cpp
index f35eb9c3..1b3a8064 100644
--- a/node/InetAddress.cpp
+++ b/node/InetAddress.cpp
@@ -280,6 +280,30 @@ InetAddress InetAddress::network() const
return r;
}
+bool InetAddress::containsAddress(const InetAddress &addr) const
+{
+ if (addr.ss_family == ss_family) {
+ switch(ss_family) {
+ case AF_INET: {
+ const unsigned int bits = netmaskBits();
+ return ( (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) == (Utils::ntoh((uint32_t)reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits)) );
+ }
+ case AF_INET6: {
+ const InetAddress mask(netmask());
+ const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&mask)->sin6_addr.s6_addr);
+ const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_addr.s6_addr);
+ const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+ for(unsigned int i=0;i<16;++i) {
+ if ((a[i] & m[i]) != b[i])
+ return false;
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool InetAddress::isNetwork() const
throw()
{
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index 2573e694..201271f7 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -326,6 +326,14 @@ struct InetAddress : public sockaddr_storage
InetAddress network() const;
/**
+ * Test whether this IP/netmask contains this address
+ *
+ * @param addr Address to check
+ * @return True if this IP/netmask (route) contains this address
+ */
+ bool containsAddress(const InetAddress &addr) const;
+
+ /**
* @return True if this is an IPv4 address
*/
inline bool isV4() const throw() { return (ss_family == AF_INET); }
diff --git a/node/Node.cpp b/node/Node.cpp
index f077424b..f65aa843 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -39,7 +39,6 @@
#include "NetworkController.hpp"
#include "Switch.hpp"
#include "Multicaster.hpp"
-#include "AntiRecursion.hpp"
#include "Topology.hpp"
#include "Buffer.hpp"
#include "Packet.hpp"
@@ -65,6 +64,7 @@ Node::Node(
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+ ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback) :
_RR(this),
RR(&_RR),
@@ -74,6 +74,7 @@ Node::Node(
_wirePacketSendFunction(wirePacketSendFunction),
_virtualNetworkFrameFunction(virtualNetworkFrameFunction),
_virtualNetworkConfigFunction(virtualNetworkConfigFunction),
+ _pathCheckFunction(pathCheckFunction),
_eventCallback(eventCallback),
_networks(),
_networks_m(),
@@ -114,7 +115,6 @@ Node::Node(
try {
RR->sw = new Switch(RR);
RR->mc = new Multicaster(RR);
- RR->antiRec = new AntiRecursion();
RR->topology = new Topology(RR);
RR->sa = new SelfAwareness(RR);
RR->dp = new DeferredPackets(RR);
@@ -122,7 +122,6 @@ Node::Node(
delete RR->dp;
delete RR->sa;
delete RR->topology;
- delete RR->antiRec;
delete RR->mc;
delete RR->sw;
throw;
@@ -141,7 +140,6 @@ Node::~Node()
delete RR->dp;
delete RR->sa;
delete RR->topology;
- delete RR->antiRec;
delete RR->mc;
delete RR->sw;
#ifdef ZT_ENABLE_CLUSTER
@@ -244,20 +242,20 @@ public:
// "Upstream" devices are roots and relays and get special treatment -- they stay alive
// forever and we try to keep (if available) both IPv4 and IPv6 channels open to them.
bool needToContactIndirect = true;
- if (p->doPingAndKeepalive(RR,_now,AF_INET)) {
+ if (p->doPingAndKeepalive(_now,AF_INET)) {
needToContactIndirect = false;
} else {
if (stableEndpoint4) {
needToContactIndirect = false;
- p->sendHELLO(RR,InetAddress(),stableEndpoint4,_now);
+ p->sendHELLO(InetAddress(),stableEndpoint4,_now);
}
}
- if (p->doPingAndKeepalive(RR,_now,AF_INET6)) {
+ if (p->doPingAndKeepalive(_now,AF_INET6)) {
needToContactIndirect = false;
} else {
if (stableEndpoint6) {
needToContactIndirect = false;
- p->sendHELLO(RR,InetAddress(),stableEndpoint6,_now);
+ p->sendHELLO(InetAddress(),stableEndpoint6,_now);
}
}
@@ -273,7 +271,7 @@ public:
lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
} else if (p->activelyTransferringFrames(_now)) {
// Normal nodes get their preferred link kept alive if the node has generated frame traffic recently
- p->doPingAndKeepalive(RR,_now,0);
+ p->doPingAndKeepalive(_now,0);
}
}
@@ -675,6 +673,27 @@ std::string Node::dataStoreGet(const char *name)
return r;
}
+bool Node::shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress)
+{
+ {
+ Mutex::Lock _l(_networks_m);
+ for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
+ SharedPtr<NetworkConfig> nc(i->second->config2());
+ if (nc) {
+ for(std::vector<InetAddress>::const_iterator a(nc->staticIps().begin());a!=nc->staticIps().end();++a) {
+ if (a->containsAddress(remoteAddress)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if (_pathCheckFunction)
+ return (_pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0);
+ else return true;
+}
+
#ifdef ZT_TRACE
void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
{
@@ -747,11 +766,12 @@ enum ZT_ResultCode ZT_Node_new(
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+ ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback)
{
*node = (ZT_Node *)0;
try {
- *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback));
+ *node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,pathCheckFunction,eventCallback));
return ZT_RESULT_OK;
} catch (std::bad_alloc &exc) {
return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
diff --git a/node/Node.hpp b/node/Node.hpp
index 15295139..b6b32363 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -71,6 +71,7 @@ public:
ZT_WirePacketSendFunction wirePacketSendFunction,
ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+ ZT_PathCheckFunction pathCheckFunction,
ZT_EventCallback eventCallback);
~Node();
@@ -189,6 +190,13 @@ public:
len);
}
+ /**
+ * @param localAddress Local address
+ * @param remoteAddress Remote address
+ * @return True if path should be used
+ */
+ bool shouldUsePathForZeroTierTraffic(const InetAddress &localAddress,const InetAddress &remoteAddress);
+
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
@@ -288,6 +296,7 @@ private:
ZT_WirePacketSendFunction _wirePacketSendFunction;
ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
+ ZT_PathCheckFunction _pathCheckFunction;
ZT_EventCallback _eventCallback;
std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 6c1b2984..5a30841a 100644
--- a/node/Packet.hpp
+++ b/node/Packet.hpp
@@ -102,15 +102,6 @@
#define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
/**
- * Cipher suite: PFS negotiated ephemeral cipher suite and authentication
- *
- * This message is encrypted with the latest negotiated ephemeral (PFS)
- * key pair and cipher suite. If authentication fails, VERB_SET_EPHEMERAL_KEY
- * may be sent to renegotiate ephemeral keys.
- */
-#define ZT_PROTO_CIPHER_SUITE__EPHEMERAL 7
-
-/**
* DEPRECATED payload encrypted flag, will be removed for re-use soon.
*
* This has been replaced by the two-bit cipher suite selection field where
@@ -235,17 +226,6 @@
*/
#define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
-// Ephemeral key record flags
-#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 // future use
-
-// Ephemeral key record symmetric cipher types
-#define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_SALSA2012_POLY1305 0x01
-#define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_AES256_GCM 0x02
-
-// Ephemeral key record public key types
-#define ZT_PROTO_EPHEMERAL_KEY_PK_C25519 0x01
-#define ZT_PROTO_EPHEMERAL_KEY_PK_NISTP256 0x02
-
// Field incides for parsing verbs -------------------------------------------
// Some verbs have variable-length fields. Those aren't fully defined here
@@ -675,20 +655,11 @@ public:
/**
* ECHO request (a.k.a. ping):
- * <[...] arbitrary payload to be echoed back>
+ * <[...] arbitrary payload>
*
* This generates OK with a copy of the transmitted payload. No ERROR
* is generated. Response to ECHO requests is optional and ECHO may be
* ignored if a node detects a possible flood.
- *
- * There is a de-facto standard for ECHO payload. No payload indicates an
- * ECHO used for path confirmation. Otherwise the first byte contains
- * flags, in which currently the only flag is 0x01 for a user-requested
- * echo. For user-requested echoes the result may be reported back through
- * the API. Otherwise the payload is for internal use.
- *
- * Support for fragmented echo packets is optional and their use is not
- * recommended.
*/
VERB_ECHO = 8,
diff --git a/node/Path.cpp b/node/Path.cpp
index e2475751..6f595189 100644
--- a/node/Path.cpp
+++ b/node/Path.cpp
@@ -26,7 +26,6 @@
*/
#include "Path.hpp"
-#include "AntiRecursion.hpp"
#include "RuntimeEnvironment.hpp"
#include "Node.hpp"
@@ -36,7 +35,6 @@ bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,u
{
if (RR->node->putPacket(_localAddress,address(),data,len)) {
sent(now);
- RR->antiRec->logOutgoingZT(data,len);
return true;
}
return false;
diff --git a/node/Path.hpp b/node/Path.hpp
index c6de6612..7e0a8ed1 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -66,6 +66,8 @@ class Path
public:
Path() :
_lastSend(0),
+ _lastPing(0),
+ _lastKeepalive(0),
_lastReceived(0),
_addr(),
_localAddress(),
@@ -76,6 +78,8 @@ public:
Path(const InetAddress &localAddress,const InetAddress &addr) :
_lastSend(0),
+ _lastPing(0),
+ _lastKeepalive(0),
_lastReceived(0),
_addr(addr),
_localAddress(localAddress),
@@ -101,11 +105,29 @@ public:
inline void sent(uint64_t t) { _lastSend = t; }
/**
+ * Called when we've sent a ping or echo
+ *
+ * @param t Time of send
+ */
+ inline void pinged(uint64_t t) { _lastPing = t; }
+
+ /**
+ * Called when we send a NAT keepalive
+ *
+ * @param t Time of send
+ */
+ inline void sentKeepalive(uint64_t t) { _lastKeepalive = t; }
+
+ /**
* Called when a packet is received from this remote path
*
* @param t Time of receive
*/
- inline void received(uint64_t t) { _lastReceived = t; }
+ inline void received(uint64_t t)
+ {
+ _lastReceived = t;
+ _probation = 0;
+ }
/**
* @param now Current time
@@ -114,7 +136,7 @@ public:
inline bool active(uint64_t now) const
throw()
{
- return ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT);
+ return (((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT)&&(_probation < ZT_PEER_DEAD_PATH_DETECTION_MAX_PROBATION));
}
/**
@@ -139,6 +161,16 @@ public:
inline uint64_t lastSend() const throw() { return _lastSend; }
/**
+ * @return Time we last pinged or dead path checked this link
+ */
+ inline uint64_t lastPing() const throw() { return _lastPing; }
+
+ /**
+ * @return Time of last keepalive
+ */
+ inline uint64_t lastKeepalive() const throw() { return _lastKeepalive; }
+
+ /**
* @return Time of last receive from this path
*/
inline uint64_t lastReceived() const throw() { return _lastReceived; }
@@ -240,28 +272,44 @@ public:
inline bool isClusterSuboptimal() const { return ((_flags & ZT_PATH_FLAG_CLUSTER_SUBOPTIMAL) != 0); }
#endif
+ /**
+ * @return Current path probation count (for dead path detect)
+ */
+ inline unsigned int probation() const { return _probation; }
+
+ /**
+ * Increase this path's probation violation count (for dead path detect)
+ */
+ inline void increaseProbation() { ++_probation; }
+
template<unsigned int C>
inline void serialize(Buffer<C> &b) const
{
- b.append((uint8_t)0); // version
+ b.append((uint8_t)2); // version
b.append((uint64_t)_lastSend);
+ b.append((uint64_t)_lastPing);
+ b.append((uint64_t)_lastKeepalive);
b.append((uint64_t)_lastReceived);
_addr.serialize(b);
_localAddress.serialize(b);
b.append((uint16_t)_flags);
+ b.append((uint16_t)_probation);
}
template<unsigned int C>
inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
{
unsigned int p = startAt;
- if (b[p++] != 0)
+ if (b[p++] != 2)
throw std::invalid_argument("invalid serialized Path");
_lastSend = b.template at<uint64_t>(p); p += 8;
+ _lastPing = b.template at<uint64_t>(p); p += 8;
+ _lastKeepalive = b.template at<uint64_t>(p); p += 8;
_lastReceived = b.template at<uint64_t>(p); p += 8;
p += _addr.deserialize(b,p);
p += _localAddress.deserialize(b,p);
_flags = b.template at<uint16_t>(p); p += 2;
+ _probation = b.template at<uint16_t>(p); p += 2;
_ipScope = _addr.ipScope();
return (p - startAt);
}
@@ -271,10 +319,13 @@ public:
private:
uint64_t _lastSend;
+ uint64_t _lastPing;
+ uint64_t _lastKeepalive;
uint64_t _lastReceived;
InetAddress _addr;
InetAddress _localAddress;
unsigned int _flags;
+ unsigned int _probation;
InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
};
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 340f0c10..c75a3e46 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -32,7 +32,6 @@
#include "Node.hpp"
#include "Switch.hpp"
#include "Network.hpp"
-#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Cluster.hpp"
#include "Packet.hpp"
@@ -46,8 +45,8 @@ namespace ZeroTier {
// Used to send varying values for NAT keepalive
static uint32_t _natKeepaliveBuf = 0;
-Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
- throw(std::runtime_error) :
+Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity) :
+ RR(renv),
_lastUsed(0),
_lastReceive(0),
_lastUnicastFrame(0),
@@ -72,7 +71,6 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
}
void Peer::received(
- const RuntimeEnvironment *RR,
const InetAddress &localAddr,
const InetAddress &remoteAddr,
unsigned int hops,
@@ -105,7 +103,6 @@ void Peer::received(
}
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.
@@ -121,7 +118,6 @@ void Peer::received(
outp.append(redirectTo.rawIpData(),16);
}
outp.armor(_key,true);
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
}
suboptimalPath = true;
@@ -160,7 +156,7 @@ void Peer::received(
}
}
- if (!pathIsConfirmed) {
+ if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
if (verb == Packet::VERB_OK) {
Path *slot = (Path *)0;
@@ -169,7 +165,10 @@ void Peer::received(
} else {
uint64_t slotLRmin = 0xffffffffffffffffULL;
for(unsigned int p=0;p<ZT_MAX_PEER_NETWORK_PATHS;++p) {
- if (_paths[p].lastReceived() <= slotLRmin) {
+ if (!_paths[p].active(now)) {
+ slot = &(_paths[p]);
+ break;
+ } else if (_paths[p].lastReceived() <= slotLRmin) {
slotLRmin = _paths[p].lastReceived();
slot = &(_paths[p]);
}
@@ -199,7 +198,7 @@ void Peer::received(
outp.armor(_key,true);
RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
} else {
- sendHELLO(RR,localAddr,remoteAddr,now);
+ sendHELLO(localAddr,remoteAddr,now);
}
}
@@ -214,7 +213,7 @@ void Peer::received(
}
}
-void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
+void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl)
{
// _lock not required here since _id is immutable and nothing else is accessed
@@ -230,11 +229,10 @@ void Peer::sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,c
outp.append((uint64_t)RR->topology->worldTimestamp());
outp.armor(_key,false); // HELLO is sent in the clear
- RR->antiRec->logOutgoingZT(outp.data(),outp.size());
RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
}
-bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily)
+bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
{
Path *p = (Path *)0;
@@ -248,13 +246,14 @@ 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());
- sendHELLO(RR,p->localAddress(),p->address(),now);
+ sendHELLO(p->localAddress(),p->address(),now);
p->sent(now);
- } else if (((now - p->lastSend()) >= ZT_NAT_KEEPALIVE_DELAY)&&(!p->reliable())) {
+ p->pinged(now);
+ } else if ( ((now - std::max(p->lastSend(),p->lastKeepalive())) >= ZT_NAT_KEEPALIVE_DELAY) && (!p->reliable()) ) {
//TRACE("NAT keepalive %s(%s) after %llums/%llums send/receive inactivity",_id.address().toString().c_str(),p->address().toString().c_str(),now - p->lastSend(),now - p->lastReceived());
_natKeepaliveBuf += (uint32_t)((now * 0x9e3779b1) >> 1); // tumble this around to send constantly varying (meaningless) payloads
RR->node->putPacket(p->localAddress(),p->address(),&_natKeepaliveBuf,sizeof(_natKeepaliveBuf));
- p->sent(now);
+ p->sentKeepalive(now);
} else {
//TRACE("no PING or NAT keepalive: addr==%s reliable==%d %llums/%llums send/receive inactivity",p->address().toString().c_str(),(int)p->reliable(),now - p->lastSend(),now - p->lastReceived());
}
@@ -264,7 +263,7 @@ bool Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inet
return false;
}
-void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force)
+void Peer::pushDirectPaths(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
@@ -332,7 +331,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,
}
}
-bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now)
+bool Peer::resetWithinScope(InetAddress::IpScope scope,uint64_t now)
{
Mutex::Lock _l(_lock);
unsigned int np = _numPaths;
@@ -340,7 +339,9 @@ bool Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope sc
unsigned int y = 0;
while (x < np) {
if (_paths[x].address().ipScope() == scope) {
- sendHELLO(RR,_paths[x].localAddress(),_paths[x].address(),now);
+ // Resetting a path means sending a HELLO and then forgetting it. If we
+ // get OK(HELLO) then it will be re-learned.
+ sendHELLO(_paths[x].localAddress(),_paths[x].address(),now);
} else {
_paths[y++] = _paths[x];
}
@@ -383,7 +384,7 @@ bool Peer::networkMembershipCertificatesAgree(uint64_t nwid,const CertificateOfM
return false;
}
-bool Peer::validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment *RR,uint64_t nwid,const CertificateOfMembership &com)
+bool Peer::validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com)
{
// Sanity checks
if ((!com)||(com.issuedTo() != _id.address()))
@@ -448,7 +449,7 @@ bool Peer::needsOurNetworkMembershipCertificate(uint64_t nwid,uint64_t now,bool
return ((now - tmp) >= (ZT_NETWORK_AUTOCONF_DELAY / 2));
}
-void Peer::clean(const RuntimeEnvironment *RR,uint64_t now)
+void Peer::clean(uint64_t now)
{
Mutex::Lock _l(_lock);
@@ -485,6 +486,54 @@ void Peer::clean(const RuntimeEnvironment *RR,uint64_t now)
}
}
+bool Peer::_checkPath(Path &p,const uint64_t now)
+{
+ // assumes _lock is locked
+
+ if (!p.active(now))
+ return false;
+
+ /* Dead path detection: if we have sent something to this peer and have not
+ * yet received a reply, double check this path. The majority of outbound
+ * packets including Ethernet frames do generate some kind of reply either
+ * immediately or at some point in the near future. This will occasionally
+ * (every NO_ANSWER_TIMEOUT ms) check paths unnecessarily if traffic that
+ * does not generate a response is being sent such as multicast announcements
+ * or frames belonging to unidirectional UDP protocols, but the cost is very
+ * tiny and the benefit in reliability is very large. This takes care of many
+ * failure modes including crap NATs that forget links and spurious changes
+ * to physical network topology that cannot be otherwise detected.
+ *
+ * Each time we do this we increment a probation counter in the path. This
+ * counter is reset on any packet receive over this path. If it reaches the
+ * MAX_PROBATION threshold the path is considred dead. */
+
+ if (
+ (p.lastSend() > p.lastReceived()) &&
+ ((p.lastSend() - p.lastReceived()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
+ ((now - p.lastPing()) >= ZT_PEER_DEAD_PATH_DETECTION_NO_ANSWER_TIMEOUT) &&
+ (!RR->topology->amRoot())
+ ) {
+ TRACE("%s(%s) does not seem to be answering in a timely manner, checking if dead (probation == %u)",_id.address().toString().c_str(),p.address().toString().c_str(),p.probation());
+
+ if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {
+ // 1.1.1 and newer nodes support ECHO, which is smaller -- but 1.1.0 has a bug so use HELLO there too
+ Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
+ outp.armor(_key,true);
+ p.send(RR,outp.data(),outp.size(),now);
+ p.pinged(now);
+ } else {
+ sendHELLO(p.localAddress(),p.address(),now);
+ p.sent(now);
+ p.pinged(now);
+ }
+
+ p.increaseProbation();
+ }
+
+ return true;
+}
+
Path *Peer::_getBestPath(const uint64_t now)
{
// assumes _lock is locked
@@ -492,7 +541,7 @@ Path *Peer::_getBestPath(const uint64_t now)
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
- if ((score >= bestPathScore)&&(_paths[i].active(now))) {
+ if ((score >= bestPathScore)&&(_checkPath(_paths[i],now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}
@@ -507,7 +556,7 @@ Path *Peer::_getBestPath(const uint64_t now,int inetAddressFamily)
uint64_t bestPathScore = 0;
for(unsigned int i=0;i<_numPaths;++i) {
const uint64_t score = _paths[i].score();
- if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_paths[i].active(now))) {
+ if (((int)_paths[i].address().ss_family == inetAddressFamily)&&(score >= bestPathScore)&&(_checkPath(_paths[i],now))) {
bestPathScore = score;
bestPath = &(_paths[i]);
}
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 86635d77..079640db 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -75,12 +75,12 @@ public:
/**
* Construct a new peer
*
+ * @param renv Runtime environment
* @param myIdentity Identity of THIS node (for key agreement)
* @param peerIdentity Identity of peer
* @throws std::runtime_error Key agreement with peer's identity failed
*/
- Peer(const Identity &myIdentity,const Identity &peerIdentity)
- throw(std::runtime_error);
+ Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Identity &peerIdentity);
/**
* @return Time peer record was last used in any way
@@ -120,7 +120,6 @@ public:
* @param inReVerb Verb in reply to (for OK/ERROR, default: VERB_NOP)
*/
void received(
- const RuntimeEnvironment *RR,
const InetAddress &localAddr,
const InetAddress &remoteAddr,
unsigned int hops,
@@ -144,13 +143,12 @@ public:
/**
* Send via best path
*
- * @param RR Runtime environment
* @param data Packet data
* @param len Packet length
* @param now Current time
* @return Path used on success or NULL on failure
*/
- inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+ inline Path *send(const void *data,unsigned int len,uint64_t now)
{
Path *const bestPath = getBestPath(now);
if (bestPath) {
@@ -166,33 +164,30 @@ public:
* This does not update any statistics. It's used to send initial HELLOs
* for NAT traversal and path verification.
*
- * @param RR Runtime environment
* @param localAddr Local address
* @param atAddress Destination address
* @param now Current time
* @param ttl Desired IP TTL (default: 0 to leave alone)
*/
- void sendHELLO(const RuntimeEnvironment *RR,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
+ void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int ttl = 0);
/**
* Send pings or keepalives depending on configured timeouts
*
- * @param RR Runtime environment
* @param now Current time
* @param inetAddressFamily Keep this address family alive, or 0 to simply pick current best ignoring family
* @return True if at least one direct path seems alive
*/
- bool doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now,int inetAddressFamily);
+ bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
/**
* Push direct paths back to self if we haven't done so in the configured timeout
*
- * @param RR Runtime environment
* @param path Remote path to use to send the push
* @param now Current time
* @param force If true, push regardless of rate limit
*/
- void pushDirectPaths(const RuntimeEnvironment *RR,Path *path,uint64_t now,bool force);
+ void pushDirectPaths(Path *path,uint64_t now,bool force);
/**
* @return All known direct paths to this peer
@@ -324,12 +319,11 @@ public:
/**
* Reset paths within a given scope
*
- * @param RR Runtime environment
* @param scope IP scope of paths to reset
* @param now Current time
* @return True if at least one path was forgotten
*/
- bool resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now);
+ bool resetWithinScope(InetAddress::IpScope scope,uint64_t now);
/**
* @return 256-bit secret symmetric encryption key
@@ -383,11 +377,10 @@ public:
/**
* Check the validity of the COM and add/update if valid and new
*
- * @param RR Runtime Environment
* @param nwid Network ID
* @param com Externally supplied COM
*/
- bool validateAndSetNetworkMembershipCertificate(const RuntimeEnvironment *RR,uint64_t nwid,const CertificateOfMembership &com);
+ bool validateAndSetNetworkMembershipCertificate(uint64_t nwid,const CertificateOfMembership &com);
/**
* @param nwid Network ID
@@ -399,8 +392,10 @@ public:
/**
* Perform periodic cleaning operations
+ *
+ * @param now Current time
*/
- void clean(const RuntimeEnvironment *RR,uint64_t now);
+ void clean(uint64_t now);
/**
* Update direct path push stats and return true if we should respond
@@ -503,13 +498,14 @@ public:
/**
* Create a new Peer from a serialized instance
*
+ * @param renv Runtime environment
* @param myIdentity This node's identity
* @param b Buffer containing serialized Peer data
* @param p Pointer to current position in buffer, will be updated in place as buffer is read (value/result)
* @return New instance of Peer or NULL if serialized data was corrupt or otherwise invalid (may also throw an exception via Buffer)
*/
template<unsigned int C>
- static inline SharedPtr<Peer> deserializeNew(const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
+ static inline SharedPtr<Peer> deserializeNew(const RuntimeEnvironment *renv,const Identity &myIdentity,const Buffer<C> &b,unsigned int &p)
{
const unsigned int recSize = b.template at<uint32_t>(p); p += 4;
if ((p + recSize) > b.size())
@@ -523,7 +519,7 @@ public:
if (!npid)
return SharedPtr<Peer>();
- SharedPtr<Peer> np(new Peer(myIdentity,npid));
+ SharedPtr<Peer> np(new Peer(renv,myIdentity,npid));
np->_lastUsed = b.template at<uint64_t>(p); p += 8;
np->_lastReceive = b.template at<uint64_t>(p); p += 8;
@@ -569,11 +565,13 @@ public:
}
private:
+ bool _checkPath(Path &p,const uint64_t now);
Path *_getBestPath(const uint64_t now);
Path *_getBestPath(const uint64_t now,int inetAddressFamily);
unsigned char _key[ZT_PEER_SECRET_KEY_LENGTH]; // computed with key agreement, not serialized
+ const RuntimeEnvironment *RR;
uint64_t _lastUsed;
uint64_t _lastReceive; // direct or indirect
uint64_t _lastUnicastFrame;
diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp
index 4846c9df..c9990c4a 100644
--- a/node/RuntimeEnvironment.hpp
+++ b/node/RuntimeEnvironment.hpp
@@ -41,7 +41,6 @@ class Switch;
class Topology;
class Node;
class Multicaster;
-class AntiRecursion;
class NetworkController;
class SelfAwareness;
class Cluster;
@@ -59,7 +58,6 @@ public:
,localNetworkController((NetworkController *)0)
,sw((Switch *)0)
,mc((Multicaster *)0)
- ,antiRec((AntiRecursion *)0)
,topology((Topology *)0)
,sa((SelfAwareness *)0)
,dp((DeferredPackets *)0)
@@ -91,7 +89,6 @@ public:
Switch *sw;
Multicaster *mc;
- AntiRecursion *antiRec;
Topology *topology;
SelfAwareness *sa;
DeferredPackets *dp;
diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp
index ce75eb03..424a3cad 100644
--- a/node/SelfAwareness.cpp
+++ b/node/SelfAwareness.cpp
@@ -46,21 +46,19 @@ namespace ZeroTier {
class _ResetWithinScope
{
public:
- _ResetWithinScope(const RuntimeEnvironment *renv,uint64_t now,InetAddress::IpScope scope) :
- RR(renv),
+ _ResetWithinScope(uint64_t now,InetAddress::IpScope scope) :
_now(now),
_scope(scope) {}
inline void operator()(Topology &t,const SharedPtr<Peer> &p)
{
- if (p->resetWithinScope(RR,_scope,_now))
+ if (p->resetWithinScope(_scope,_now))
peersReset.push_back(p);
}
std::vector< SharedPtr<Peer> > peersReset;
private:
- const RuntimeEnvironment *RR;
uint64_t _now;
InetAddress::IpScope _scope;
};
@@ -121,7 +119,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi
}
// Reset all paths within this scope
- _ResetWithinScope rset(RR,now,(InetAddress::IpScope)scope);
+ _ResetWithinScope rset(now,(InetAddress::IpScope)scope);
RR->topology->eachPeer<_ResetWithinScope &>(rset);
// Send a NOP to all peers for whom we forgot a path. This will cause direct
diff --git a/node/Switch.cpp b/node/Switch.cpp
index a06de17e..c17b8d1b 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -42,7 +42,6 @@
#include "InetAddress.hpp"
#include "Topology.hpp"
#include "Peer.hpp"
-#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
#include "Cluster.hpp"
@@ -90,6 +89,8 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
if (beaconAddr == RR->identity.address())
return;
+ if (!RR->node->shouldUsePathForZeroTierTraffic(localAddr,fromAddr))
+ return;
SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
if (peer) { // we'll only respond to beacons from known peers
const uint64_t now = RR->node->now();
@@ -124,15 +125,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
if (to == network->mac())
return;
- /* Check anti-recursion module to ensure that this is not ZeroTier talking over its own links.
- * Note: even when we introduce a more purposeful binding of the main UDP port, this can
- * still happen because Windows likes to send broadcasts over interfaces that have little
- * to do with their intended target audience. :P */
- if (!RR->antiRec->checkEthernetFrame(data,len)) {
- TRACE("%.16llx: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->id(),etherTypeName(etherType),len);
- return;
- }
-
// Check to make sure this protocol is allowed on this network
if (!nconf->permitsEtherType(etherType)) {
TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
@@ -408,7 +400,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
outp.append(cg.first.rawIpData(),4);
}
outp.armor(p1p->key(),true);
- p1p->send(RR,outp.data(),outp.size(),now);
+ p1p->send(outp.data(),outp.size(),now);
} else {
// Tell p2 where to find p1.
Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
@@ -423,7 +415,7 @@ bool Switch::unite(const Address &p1,const Address &p2)
outp.append(cg.second.rawIpData(),4);
}
outp.armor(p2p->key(),true);
- p2p->send(RR,outp.data(),outp.size(),now);
+ p2p->send(outp.data(),outp.size(),now);
}
++alt; // counts up and also flips LSB
}
@@ -435,7 +427,7 @@ void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &localAddr
{
TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
const uint64_t now = RR->node->now();
- peer->sendHELLO(RR,localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
+ peer->sendHELLO(localAddr,atAddr,now,2); // first attempt: send low-TTL packet to 'open' local NAT
{
Mutex::Lock _l(_contactQueue_m);
_contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,localAddr,atAddr));
@@ -459,12 +451,6 @@ void Switch::requestWhois(const Address &addr)
_sendWhoisRequest(addr,(const Address *)0,0);
}
-void Switch::cancelWhoisRequest(const Address &addr)
-{
- Mutex::Lock _l(_outstandingWhoisRequests_m);
- _outstandingWhoisRequests.erase(addr);
-}
-
void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
{
{ // cancel pending WHOIS since we now know this peer
@@ -508,14 +494,14 @@ unsigned long Switch::doTimerTasks(uint64_t now)
} else {
if (qi->strategyIteration == 0) {
// First strategy: send packet directly to destination
- qi->peer->sendHELLO(RR,qi->localAddr,qi->inaddr,now);
+ qi->peer->sendHELLO(qi->localAddr,qi->inaddr,now);
} else if (qi->strategyIteration <= 3) {
// Strategies 1-3: try escalating ports for symmetric NATs that remap sequentially
InetAddress tmpaddr(qi->inaddr);
int p = (int)qi->inaddr.port() + qi->strategyIteration;
if (p < 0xffff) {
tmpaddr.setPort((unsigned int)p);
- qi->peer->sendHELLO(RR,qi->localAddr,tmpaddr,now);
+ qi->peer->sendHELLO(qi->localAddr,tmpaddr,now);
} else qi->strategyIteration = 5;
} else {
// All strategies tried, expire entry
@@ -619,7 +605,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
// It wouldn't hurt anything, just redundant and unnecessary.
SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
- if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
+ if ((!relayTo)||(!relayTo->send(fragment.data(),fragment.size(),RR->node->now()))) {
#ifdef ZT_ENABLE_CLUSTER
if (RR->cluster) {
RR->cluster->sendViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
@@ -630,7 +616,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &localAddr,const Inet
// Don't know peer or no direct path -- so relay via root server
relayTo = RR->topology->getBestRoot();
if (relayTo)
- relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now());
+ relayTo->send(fragment.data(),fragment.size(),RR->node->now());
}
} else {
TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
@@ -705,7 +691,7 @@ 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(),now)))) {
+ if ((relayTo)&&((relayTo->send(packet->data(),packet->size(),now)))) {
Mutex::Lock _l(_lastUniteAttempt_m);
uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
@@ -730,7 +716,7 @@ void Switch::_handleRemotePacketHead(const InetAddress &localAddr,const InetAddr
relayTo = RR->topology->getBestRoot(&source,1,true);
if (relayTo)
- relayTo->send(RR,packet->data(),packet->size(),now);
+ relayTo->send(packet->data(),packet->size(),now);
}
} else {
TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@@ -787,7 +773,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
Packet outp(root->address(),RR->identity.address(),Packet::VERB_WHOIS);
addr.appendTo(outp);
outp.armor(root->key(),true);
- if (root->send(RR,outp.data(),outp.size(),RR->node->now()))
+ if (root->send(outp.data(),outp.size(),RR->node->now()))
return root->address();
}
return Address();
@@ -841,7 +827,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
if ((network)&&(relay)&&(network->isAllowed(peer))) {
// Push hints for direct connectivity to this peer if we are relaying
- peer->pushDirectPaths(RR,viaPath,now,false);
+ peer->pushDirectPaths(viaPath,now,false);
}
Packet tmp(packet);
diff --git a/node/Switch.hpp b/node/Switch.hpp
index 1964d1ee..a63ec1bb 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -139,13 +139,6 @@ public:
void requestWhois(const Address &addr);
/**
- * Cancel WHOIS for an address
- *
- * @param addr Address to cancel
- */
- void cancelWhoisRequest(const Address &addr);
-
- /**
* Run any processes that are waiting for this peer's identity
*
* Called when we learn of a peer's identity from HELLO, OK(WHOIS), etc.
@@ -174,7 +167,7 @@ private:
const RuntimeEnvironment *const RR;
uint64_t _lastBeaconResponse;
- // Outsanding WHOIS requests and how many retries they've undergone
+ // Outstanding WHOIS requests and how many retries they've undergone
struct WhoisRequest
{
WhoisRequest() : lastSent(0),retries(0) {}
diff --git a/node/Topology.cpp b/node/Topology.cpp
index cc18708a..e0592ea5 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -67,7 +67,7 @@ Topology::Topology(const RuntimeEnvironment *renv) :
);
unsigned int pos = 0;
deserializeBuf->copyFrom(all + ptr,reclen + 4);
- SharedPtr<Peer> p(Peer::deserializeNew(RR->identity,*deserializeBuf,pos));
+ SharedPtr<Peer> p(Peer::deserializeNew(RR,RR->identity,*deserializeBuf,pos));
ptr += pos;
if (!p)
break; // stop if invalid records
@@ -180,7 +180,7 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
try {
Identity id(_getIdentity(zta));
if (id) {
- SharedPtr<Peer> np(new Peer(RR->identity,id));
+ SharedPtr<Peer> np(new Peer(RR,RR->identity,id));
{
Mutex::Lock _l(_lock);
SharedPtr<Peer> &ap = _peers[zta];
@@ -327,7 +327,7 @@ void Topology::clean(uint64_t now)
if (((now - (*p)->lastUsed()) >= ZT_PEER_IN_MEMORY_EXPIRATION)&&(std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())) {
_peers.erase(*a);
} else {
- (*p)->clean(RR,now);
+ (*p)->clean(now);
}
}
}
@@ -361,7 +361,7 @@ void Topology::_setWorld(const World &newWorld)
if (rp) {
_rootPeers.push_back(*rp);
} else {
- SharedPtr<Peer> newrp(new Peer(RR->identity,r->identity));
+ SharedPtr<Peer> newrp(new Peer(RR,RR->identity,r->identity));
_peers.set(r->identity.address(),newrp);
_rootPeers.push_back(newrp);
}
diff --git a/service/OneService.cpp b/service/OneService.cpp
index 6e4ba904..84ebdd87 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -397,6 +397,7 @@ static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,
static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure);
static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
#ifdef ZT_ENABLE_CLUSTER
static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
@@ -592,6 +593,7 @@ public:
SnodeWirePacketSendFunction,
SnodeVirtualNetworkFrameFunction,
SnodeVirtualNetworkConfigFunction,
+ SnodePathCheckFunction,
SnodeEventCallback);
#ifdef ZT_USE_MINIUPNPC
@@ -763,6 +765,13 @@ public:
if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
+ _node->clearLocalInterfaceAddresses();
+#ifdef ZT_USE_MINIUPNPC
+ std::vector<InetAddress> mappedAddresses(_portMapper->get());
+ for(std::vector<InetAddress>::const_iterator ext(mappedAddresses.begin());ext!=mappedAddresses.end();++ext)
+ _node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)));
+#endif
+
#ifdef __UNIX_LIKE__
std::vector<std::string> ztDevices;
{
@@ -770,15 +779,6 @@ public:
for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
ztDevices.push_back(t->second->deviceName());
}
-
- _node->clearLocalInterfaceAddresses();
-
-#ifdef ZT_USE_MINIUPNPC
- std::vector<InetAddress> mappedAddresses(_portMapper->get());
- for(std::vector<InetAddress>::const_iterator ext(mappedAddresses.begin());ext!=mappedAddresses.end();++ext)
- _node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*ext)));
-#endif
-
struct ifaddrs *ifatbl = (struct ifaddrs *)0;
if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
struct ifaddrs *ifa = ifatbl;
@@ -810,7 +810,6 @@ public:
for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
ztDevices.push_back(t->second->luid());
}
-
char aabuf[16384];
ULONG aalen = sizeof(aabuf);
if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
@@ -1396,6 +1395,22 @@ public:
t->second->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
}
+ inline int nodePathCheckFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
+ {
+ Mutex::Lock _l(_taps_m);
+ for(std::map< uint64_t,EthernetTap * >::const_iterator t(_taps.begin());t!=_taps.end();++t) {
+ if (t->second) {
+ std::vector<InetAddress> ips(t->second->ips());
+ for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
+ if (i->containsAddress(*(reinterpret_cast<const InetAddress *>(remoteAddr)))) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+ }
+
inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{
_node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
@@ -1530,6 +1545,8 @@ static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct soc
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,uint64_t nwid,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,sourceMac,destMac,etherType,vlanId,data,len); }
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(localAddr,remoteAddr); }
#ifdef ZT_ENABLE_CLUSTER
static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)
diff --git a/version.h b/version.h
index 71d9ca61..24555f51 100644
--- a/version.h
+++ b/version.h
@@ -41,6 +41,6 @@
/**
* Revision
*/
-#define ZEROTIER_ONE_VERSION_REVISION 2
+#define ZEROTIER_ONE_VERSION_REVISION 3
#endif