Browse Source

Merge branch 'dev' into edge

Adam Ierymenko 9 years ago
parent
commit
ba9fcb31d0

+ 49 - 15
include/ZeroTierOne.h

@@ -1097,13 +1097,42 @@ typedef int (*ZT_DataStorePutFunction)(
  * delivery. It only means that the packet appears to have been sent.
  * delivery. It only means that the packet appears to have been sent.
  */
  */
 typedef int (*ZT_WirePacketSendFunction)(
 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                                                               */
 /* C Node API                                                               */
@@ -1121,6 +1150,7 @@ typedef int (*ZT_WirePacketSendFunction)(
  * @param dataStoreGetFunction Function called to get objects from persistent storage
  * @param dataStoreGetFunction Function called to get objects from persistent storage
  * @param dataStorePutFunction Function called to put objects in 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 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
  * @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
  * @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_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+	ZT_PathCheckFunction pathCheckFunction,
 	ZT_EventCallback eventCallback);
 	ZT_EventCallback eventCallback);
 
 
 /**
 /**
@@ -1334,18 +1365,21 @@ void ZT_Node_freeQueryResult(ZT_Node *node,void *qr);
 /**
 /**
  * Add a local interface address
  * 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
  * This returns a boolean indicating whether or not the address was
  * accepted. ZeroTier will only communicate over certain address types
  * 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
  * @param addr Local interface address
  * @return Boolean: non-zero if address was accepted and added
  * @return Boolean: non-zero if address was accepted and added

+ 0 - 135
node/AntiRecursion.hpp

@@ -1,135 +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

+ 6 - 18
node/IncomingPacket.cpp

@@ -45,7 +45,6 @@
 #include "World.hpp"
 #include "World.hpp"
 #include "Cluster.hpp"
 #include "Cluster.hpp"
 #include "Node.hpp"
 #include "Node.hpp"
-#include "AntiRecursion.hpp"
 #include "DeferredPackets.hpp"
 #include "DeferredPackets.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
@@ -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);
 						Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
 						nconf->com().serialize(outp);
 						nconf->com().serialize(outp);
 						outp.armor(peer->key(),true);
 						outp.armor(peer->key(),true);
-						RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					}
 					}
 				}
 				}
@@ -345,7 +343,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,SharedPtr<Peer> &peer
 		}
 		}
 
 
 		outp.armor(peer->key(),true);
 		outp.armor(peer->key(),true);
-		RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 		RR->node->putPacket(_localAddress,_remoteAddress,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->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
@@ -483,7 +480,6 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				outp.append(packetId());
 				outp.append(packetId());
 				queried.serialize(outp,false);
 				queried.serialize(outp,false);
 				outp.armor(peer->key(),true);
 				outp.armor(peer->key(),true);
-				RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 				RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 				RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 			} else {
 			} else {
 #ifdef ZT_ENABLE_CLUSTER
 #ifdef ZT_ENABLE_CLUSTER
@@ -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 port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
 				const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 				const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 				if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
 				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);
 					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());
 					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(_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 {
 				} else {
 					TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
 					TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
 				}
 				}
@@ -644,7 +642,6 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 		if (size() > ZT_PACKET_IDX_PAYLOAD)
 		if (size() > ZT_PACKET_IDX_PAYLOAD)
 			outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,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);
 		outp.armor(peer->key(),true);
-		RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 		RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 		RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 		peer->received(_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 ( ... ) {
 	} catch ( ... ) {
@@ -722,7 +719,6 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
 						if (outp.size() > ZT_PROTO_MAX_PACKET_LENGTH) { // sanity check
 						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());
 							TRACE("NETWORK_CONFIG_REQUEST failed: internal error: netconf size %u is too large",(unsigned int)netconfStr.length());
 						} else {
 						} else {
-							RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 							RR->node->putPacket(_localAddress,_remoteAddress,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((unsigned char)Packet::ERROR_OBJ_NOT_FOUND);
 					outp.append(nwid);
 					outp.append(nwid);
 					outp.armor(peer->key(),true);
 					outp.armor(peer->key(),true);
-					RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 				}	break;
 				}	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((unsigned char)Packet::ERROR_NETWORK_ACCESS_DENIED_);
 					outp.append(nwid);
 					outp.append(nwid);
 					outp.armor(peer->key(),true);
 					outp.armor(peer->key(),true);
-					RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 				} break;
 				} break;
 
 
@@ -769,7 +763,6 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
 			outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
 			outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
 			outp.append(nwid);
 			outp.append(nwid);
 			outp.armor(peer->key(),true);
 			outp.armor(peer->key(),true);
-			RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 			RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 			RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 		}
 		}
 	} catch ( ... ) {
 	} catch ( ... ) {
@@ -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);
 			const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
 			if (gatheredLocally) {
 			if (gatheredLocally) {
 				outp.armor(peer->key(),true);
 				outp.armor(peer->key(),true);
-				RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 				RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 				RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 			}
 			}
 
 
@@ -909,7 +901,6 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 				outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
 				outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
 				if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
 				if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
 					outp.armor(peer->key(),true);
 					outp.armor(peer->key(),true);
-					RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 				}
 				}
 			}
 			}
@@ -952,7 +943,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 			switch(addrType) {
 			switch(addrType) {
 				case 4: {
 				case 4: {
 					InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 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) {
 						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());
 							TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
 							peer->sendHELLO(_localAddress,a,now);
 							peer->sendHELLO(_localAddress,a,now);
@@ -963,7 +954,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 				}	break;
 				}	break;
 				case 6: {
 				case 6: {
 					InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
 					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) {
 						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());
 							TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
 							peer->sendHELLO(_localAddress,a,now);
 							peer->sendHELLO(_localAddress,a,now);
@@ -1220,7 +1211,6 @@ bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const
 						outp.append((uint16_t)sizeof(result));
 						outp.append((uint16_t)sizeof(result));
 						outp.append(result,sizeof(result));
 						outp.append(result,sizeof(result));
 						outp.armor(peer->key(),true);
 						outp.armor(peer->key(),true);
-						RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					} else {
 					} else {
 						Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
 						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(pid);
 						outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
 						outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
 						outp.armor(peer->key(),true);
 						outp.armor(peer->key(),true);
-						RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 						RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 					}
 					}
 				}	break;
 				}	break;
@@ -1334,7 +1323,6 @@ void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,cons
 	outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
 	outp.append((unsigned char)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
 	outp.append(nwid);
 	outp.append(nwid);
 	outp.armor(peer->key(),true);
 	outp.armor(peer->key(),true);
-	RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 	RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 	RR->node->putPacket(_localAddress,_remoteAddress,outp.data(),outp.size());
 }
 }
 
 

+ 24 - 0
node/InetAddress.cpp

@@ -280,6 +280,30 @@ InetAddress InetAddress::network() const
 	return r;
 	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
 bool InetAddress::isNetwork() const
 	throw()
 	throw()
 {
 {

+ 8 - 0
node/InetAddress.hpp

@@ -325,6 +325,14 @@ struct InetAddress : public sockaddr_storage
 	 */
 	 */
 	InetAddress network() const;
 	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
 	 * @return True if this is an IPv4 address
 	 */
 	 */

+ 25 - 5
node/Node.cpp

@@ -39,7 +39,6 @@
 #include "NetworkController.hpp"
 #include "NetworkController.hpp"
 #include "Switch.hpp"
 #include "Switch.hpp"
 #include "Multicaster.hpp"
 #include "Multicaster.hpp"
-#include "AntiRecursion.hpp"
 #include "Topology.hpp"
 #include "Topology.hpp"
 #include "Buffer.hpp"
 #include "Buffer.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
@@ -65,6 +64,7 @@ Node::Node(
 	ZT_WirePacketSendFunction wirePacketSendFunction,
 	ZT_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+	ZT_PathCheckFunction pathCheckFunction,
 	ZT_EventCallback eventCallback) :
 	ZT_EventCallback eventCallback) :
 	_RR(this),
 	_RR(this),
 	RR(&_RR),
 	RR(&_RR),
@@ -74,6 +74,7 @@ Node::Node(
 	_wirePacketSendFunction(wirePacketSendFunction),
 	_wirePacketSendFunction(wirePacketSendFunction),
 	_virtualNetworkFrameFunction(virtualNetworkFrameFunction),
 	_virtualNetworkFrameFunction(virtualNetworkFrameFunction),
 	_virtualNetworkConfigFunction(virtualNetworkConfigFunction),
 	_virtualNetworkConfigFunction(virtualNetworkConfigFunction),
+	_pathCheckFunction(pathCheckFunction),
 	_eventCallback(eventCallback),
 	_eventCallback(eventCallback),
 	_networks(),
 	_networks(),
 	_networks_m(),
 	_networks_m(),
@@ -114,7 +115,6 @@ Node::Node(
 	try {
 	try {
 		RR->sw = new Switch(RR);
 		RR->sw = new Switch(RR);
 		RR->mc = new Multicaster(RR);
 		RR->mc = new Multicaster(RR);
-		RR->antiRec = new AntiRecursion();
 		RR->topology = new Topology(RR);
 		RR->topology = new Topology(RR);
 		RR->sa = new SelfAwareness(RR);
 		RR->sa = new SelfAwareness(RR);
 		RR->dp = new DeferredPackets(RR);
 		RR->dp = new DeferredPackets(RR);
@@ -122,7 +122,6 @@ Node::Node(
 		delete RR->dp;
 		delete RR->dp;
 		delete RR->sa;
 		delete RR->sa;
 		delete RR->topology;
 		delete RR->topology;
-		delete RR->antiRec;
 		delete RR->mc;
 		delete RR->mc;
 		delete RR->sw;
 		delete RR->sw;
 		throw;
 		throw;
@@ -141,7 +140,6 @@ Node::~Node()
 	delete RR->dp;
 	delete RR->dp;
 	delete RR->sa;
 	delete RR->sa;
 	delete RR->topology;
 	delete RR->topology;
-	delete RR->antiRec;
 	delete RR->mc;
 	delete RR->mc;
 	delete RR->sw;
 	delete RR->sw;
 #ifdef ZT_ENABLE_CLUSTER
 #ifdef ZT_ENABLE_CLUSTER
@@ -675,6 +673,27 @@ std::string Node::dataStoreGet(const char *name)
 	return r;
 	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
 #ifdef ZT_TRACE
 void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
 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_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+	ZT_PathCheckFunction pathCheckFunction,
 	ZT_EventCallback eventCallback)
 	ZT_EventCallback eventCallback)
 {
 {
 	*node = (ZT_Node *)0;
 	*node = (ZT_Node *)0;
 	try {
 	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;
 		return ZT_RESULT_OK;
 	} catch (std::bad_alloc &exc) {
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;

+ 9 - 0
node/Node.hpp

@@ -71,6 +71,7 @@ public:
 		ZT_WirePacketSendFunction wirePacketSendFunction,
 		ZT_WirePacketSendFunction wirePacketSendFunction,
 		ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 		ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 		ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
 		ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
+		ZT_PathCheckFunction pathCheckFunction,
 		ZT_EventCallback eventCallback);
 		ZT_EventCallback eventCallback);
 
 
 	~Node();
 	~Node();
@@ -189,6 +190,13 @@ public:
 			len);
 			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
 	inline SharedPtr<Network> network(uint64_t nwid) const
 	{
 	{
 		Mutex::Lock _l(_networks_m);
 		Mutex::Lock _l(_networks_m);
@@ -288,6 +296,7 @@ private:
 	ZT_WirePacketSendFunction _wirePacketSendFunction;
 	ZT_WirePacketSendFunction _wirePacketSendFunction;
 	ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
 	ZT_VirtualNetworkFrameFunction _virtualNetworkFrameFunction;
 	ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
 	ZT_VirtualNetworkConfigFunction _virtualNetworkConfigFunction;
+	ZT_PathCheckFunction _pathCheckFunction;
 	ZT_EventCallback _eventCallback;
 	ZT_EventCallback _eventCallback;
 
 
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;

+ 0 - 2
node/Path.cpp

@@ -26,7 +26,6 @@
  */
  */
 
 
 #include "Path.hpp"
 #include "Path.hpp"
-#include "AntiRecursion.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "Node.hpp"
 #include "Node.hpp"
 
 
@@ -34,7 +33,6 @@ namespace ZeroTier {
 
 
 bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
 bool Path::send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
 {
 {
-	RR->antiRec->logOutgoingZT(data,len);
 	if (RR->node->putPacket(_localAddress,address(),data,len)) {
 	if (RR->node->putPacket(_localAddress,address(),data,len)) {
 		sent(now);
 		sent(now);
 		return true;
 		return true;

+ 7 - 6
node/Peer.cpp

@@ -32,7 +32,6 @@
 #include "Node.hpp"
 #include "Node.hpp"
 #include "Switch.hpp"
 #include "Switch.hpp"
 #include "Network.hpp"
 #include "Network.hpp"
-#include "AntiRecursion.hpp"
 #include "SelfAwareness.hpp"
 #include "SelfAwareness.hpp"
 #include "Cluster.hpp"
 #include "Cluster.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
@@ -104,7 +103,6 @@ void Peer::received(
 				}
 				}
 				outp.append((uint16_t)redirectTo.port());
 				outp.append((uint16_t)redirectTo.port());
 				outp.armor(_key,true);
 				outp.armor(_key,true);
-				RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 				RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
 				RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
 			} else {
 			} else {
 				// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
 				// For older peers we use RENDEZVOUS to coax them into contacting us elsewhere.
@@ -120,7 +118,6 @@ void Peer::received(
 					outp.append(redirectTo.rawIpData(),16);
 					outp.append(redirectTo.rawIpData(),16);
 				}
 				}
 				outp.armor(_key,true);
 				outp.armor(_key,true);
-				RR->antiRec->logOutgoingZT(outp.data(),outp.size());
 				RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
 				RR->node->putPacket(localAddr,remoteAddr,outp.data(),outp.size());
 			}
 			}
 			suboptimalPath = true;
 			suboptimalPath = true;
@@ -159,7 +156,7 @@ void Peer::received(
 				}
 				}
 			}
 			}
 
 
-			if (!pathIsConfirmed) {
+			if ((!pathIsConfirmed)&&(RR->node->shouldUsePathForZeroTierTraffic(localAddr,remoteAddr))) {
 				if (verb == Packet::VERB_OK) {
 				if (verb == Packet::VERB_OK) {
 
 
 					Path *slot = (Path *)0;
 					Path *slot = (Path *)0;
@@ -232,7 +229,6 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
 	outp.append((uint64_t)RR->topology->worldTimestamp());
 	outp.append((uint64_t)RR->topology->worldTimestamp());
 
 
 	outp.armor(_key,false); // HELLO is sent in the clear
 	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);
 	RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size(),ttl);
 }
 }
 
 
@@ -512,7 +508,12 @@ bool Peer::_checkPath(Path &p,const uint64_t now)
 	 * counter is reset on any packet receive over this path. If it reaches the
 	 * counter is reset on any packet receive over this path. If it reaches the
 	 * MAX_PROBATION threshold the path is considred dead. */
 	 * 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) ) {
+	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());
 		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)) ) ) {
 		if ( (_vProto >= 5) && ( !((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0)) ) ) {

+ 0 - 3
node/RuntimeEnvironment.hpp

@@ -41,7 +41,6 @@ class Switch;
 class Topology;
 class Topology;
 class Node;
 class Node;
 class Multicaster;
 class Multicaster;
-class AntiRecursion;
 class NetworkController;
 class NetworkController;
 class SelfAwareness;
 class SelfAwareness;
 class Cluster;
 class Cluster;
@@ -59,7 +58,6 @@ public:
 		,localNetworkController((NetworkController *)0)
 		,localNetworkController((NetworkController *)0)
 		,sw((Switch *)0)
 		,sw((Switch *)0)
 		,mc((Multicaster *)0)
 		,mc((Multicaster *)0)
-		,antiRec((AntiRecursion *)0)
 		,topology((Topology *)0)
 		,topology((Topology *)0)
 		,sa((SelfAwareness *)0)
 		,sa((SelfAwareness *)0)
 		,dp((DeferredPackets *)0)
 		,dp((DeferredPackets *)0)
@@ -91,7 +89,6 @@ public:
 
 
 	Switch *sw;
 	Switch *sw;
 	Multicaster *mc;
 	Multicaster *mc;
-	AntiRecursion *antiRec;
 	Topology *topology;
 	Topology *topology;
 	SelfAwareness *sa;
 	SelfAwareness *sa;
 	DeferredPackets *dp;
 	DeferredPackets *dp;

+ 0 - 10
node/Switch.cpp

@@ -42,7 +42,6 @@
 #include "InetAddress.hpp"
 #include "InetAddress.hpp"
 #include "Topology.hpp"
 #include "Topology.hpp"
 #include "Peer.hpp"
 #include "Peer.hpp"
-#include "AntiRecursion.hpp"
 #include "SelfAwareness.hpp"
 #include "SelfAwareness.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
 #include "Cluster.hpp"
 #include "Cluster.hpp"
@@ -124,15 +123,6 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	if (to == network->mac())
 	if (to == network->mac())
 		return;
 		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
 	// Check to make sure this protocol is allowed on this network
 	if (!nconf->permitsEtherType(etherType)) {
 	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());
 		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());

+ 27 - 10
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 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 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 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
 #ifdef ZT_ENABLE_CLUSTER
 static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
 static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
@@ -592,6 +593,7 @@ public:
 				SnodeWirePacketSendFunction,
 				SnodeWirePacketSendFunction,
 				SnodeVirtualNetworkFrameFunction,
 				SnodeVirtualNetworkFrameFunction,
 				SnodeVirtualNetworkConfigFunction,
 				SnodeVirtualNetworkConfigFunction,
+				SnodePathCheckFunction,
 				SnodeEventCallback);
 				SnodeEventCallback);
 
 
 #ifdef ZT_USE_MINIUPNPC
 #ifdef ZT_USE_MINIUPNPC
@@ -763,6 +765,13 @@ public:
 				if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
 				if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
 					lastLocalInterfaceAddressCheck = now;
 					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__
 #ifdef __UNIX_LIKE__
 					std::vector<std::string> ztDevices;
 					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)
 						for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
 							ztDevices.push_back(t->second->deviceName());
 							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;
 					struct ifaddrs *ifatbl = (struct ifaddrs *)0;
 					if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
 					if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
 						struct ifaddrs *ifa = 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)
 						for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
 							ztDevices.push_back(t->second->luid());
 							ztDevices.push_back(t->second->luid());
 					}
 					}
-
 					char aabuf[16384];
 					char aabuf[16384];
 					ULONG aalen = sizeof(aabuf);
 					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) {
 					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);
 			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)
 	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);
 		_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); }
 { 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)
 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); }
 { 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
 #ifdef ZT_ENABLE_CLUSTER
 static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)
 static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)