Browse Source

Implemented empirical determination of external addressing, paritioned per scope.

Adam Ierymenko 10 năm trước cách đây
mục cha
commit
52c3b7c34e
10 tập tin đã thay đổi với 102 bổ sung95 xóa
  1. 0 5
      node/Constants.hpp
  2. 2 2
      node/IncomingPacket.cpp
  3. 10 12
      node/InetAddress.hpp
  4. 1 1
      node/Path.hpp
  5. 28 0
      node/Peer.cpp
  6. 16 3
      node/Peer.hpp
  7. 40 1
      node/SelfAwareness.cpp
  8. 5 3
      node/SelfAwareness.hpp
  9. 0 30
      node/Topology.cpp
  10. 0 38
      node/Topology.hpp

+ 0 - 5
node/Constants.hpp

@@ -301,11 +301,6 @@
  */
 #define ZT_PEER_ACTIVITY_TIMEOUT ((ZT_PEER_DIRECT_PING_DELAY * 2) + ZT_PING_CHECK_DELAY)
 
-/**
- * Path activity timeout (for non-fixed paths)
- */
-#define ZT_PEER_PATH_ACTIVITY_TIMEOUT ZT_PEER_ACTIVITY_TIMEOUT
-
 /**
  * Stop relaying via peers that have not responded to direct sends
  *

+ 2 - 2
node/IncomingPacket.cpp

@@ -264,9 +264,9 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
 
 		if (RR->topology->isSupernode(id.address())) {
 			RR->node->postNewerVersionIfNewer(vMajor,vMinor,vRevision);
-			RR->sa->iam(destAddr,true);
+			RR->sa->iam(_remoteAddress,destAddr,true);
 		} else {
-			RR->sa->iam(destAddr,false);
+			RR->sa->iam(_remoteAddress,destAddr,false);
 		}
 
 		Packet outp(id.address(),RR->identity.address(),Packet::VERB_OK);

+ 10 - 12
node/InetAddress.hpp

@@ -74,21 +74,19 @@ struct InetAddress : public sockaddr_storage
 	/**
 	 * IP address scope
 	 *
-	 * Be sure the integer values of these start at 0 and increment
-	 * monotonically without gaps, as they're used as an array index.
-	 * The NONE entry must be the last, since it's the count. It's
-	 * okay to change these since they are not exported via the API.
+	 * Do not change these numeric index values without taking a look
+	 * at SelfAwareness. Values 1-5 are mapped onto an array index.
 	 */
 	enum IpScope
 	{
-		IP_SCOPE_LOOPBACK = 0,      // 127.0.0.1
-		IP_SCOPE_MULTICAST = 1,     // 224.0.0.0 and other multicast IPs
-		IP_SCOPE_LINK_LOCAL = 2,    // 169.254.x.x, IPv6 LL
-		IP_SCOPE_PRIVATE = 3,       // 10.x.x.x, etc.
-		IP_SCOPE_PSEUDOPRIVATE = 4, // 28.x.x.x, etc. -- unofficially unrouted IP blocks often "bogarted"
-		IP_SCOPE_SHARED = 5,        // 100.64.0.0/10, shared space for e.g. carrier-grade NAT
-		IP_SCOPE_GLOBAL = 6,        // globally routable IP address (all others)
-		IP_SCOPE_NONE = 7           // not an IP address -- also the number of classes, must be last entry
+		IP_SCOPE_NONE = 0,          // not an IP address -- also the number of classes, must be last entry
+		IP_SCOPE_LINK_LOCAL = 1,    // 169.254.x.x, IPv6 LL
+		IP_SCOPE_PRIVATE = 2,       // 10.x.x.x, etc.
+		IP_SCOPE_PSEUDOPRIVATE = 3, // 28.x.x.x, etc. -- unofficially unrouted IP blocks often "bogarted"
+		IP_SCOPE_SHARED = 4,        // 100.64.0.0/10, shared space for e.g. carrier-grade NAT
+		IP_SCOPE_GLOBAL = 5,        // globally routable IP address (all others)
+		IP_SCOPE_LOOPBACK = 6,      // 127.0.0.1
+		IP_SCOPE_MULTICAST = 7      // 224.0.0.0 and other multicast IPs
 	};
 
 	InetAddress() throw() { memset(this,0,sizeof(InetAddress)); }

+ 1 - 1
node/Path.hpp

@@ -145,7 +145,7 @@ public:
 	inline bool active(uint64_t now) const
 		throw()
 	{
-		return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_PATH_ACTIVITY_TIMEOUT) );
+		return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) );
 	}
 
 	/**

+ 28 - 0
node/Peer.cpp

@@ -192,6 +192,34 @@ void Peer::clearPaths(bool fixedToo)
 	}
 }
 
+void Peer::resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now)
+{
+	unsigned int np = _numPaths;
+	unsigned int x = 0;
+	unsigned int y = 0;
+	while (x < np) {
+		if (_paths[x].address().ipScope() == scope) {
+			if (_paths[x].fixed()) {
+				Packet outp(_id.address(),RR->identity.address(),Packet::VERB_NOP);
+				outp.armor(_key,false);
+				RR->node->putPacket(_paths[x].address(),outp.data(),outp.size(),_paths[x].desperation(now));
+				_paths[y++] = _paths[x]; // keep fixed paths
+			}
+		} else {
+			_paths[y++] = _paths[x]; // keep paths not in this scope
+		}
+		++x;
+	}
+	_numPaths = y;
+
+	if ((y < np)&&(alive(now))) {
+		// Try to re-establish direct connectivity to this peer if it's alive
+		// and we have forgotten paths to it.
+		Packet outp(_id.address(),RR->identity.address(),Packet::VERB_NOP);
+		RR->sw->send(outp,true);
+	}
+}
+
 void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
 {
 	uint64_t bestV4 = 0,bestV6 = 0;

+ 16 - 3
node/Peer.hpp

@@ -230,10 +230,9 @@ public:
 	inline uint64_t lastAnnouncedTo() const throw() { return _lastAnnouncedTo; }
 
 	/**
-	 * @param now Current time
-	 * @return True if peer has received something within ZT_PEER_ACTIVITY_TIMEOUT ms
+	 * @return True if peer has received an actual data frame within ZT_PEER_ACTIVITY_TIMEOUT milliseconds
 	 */
-	inline bool alive(uint64_t now) const throw() { return ((now - _lastReceive) < ZT_PEER_ACTIVITY_TIMEOUT); }
+	inline uint64_t alive(uint64_t now) const throw() { return ((now - lastFrame()) < ZT_PEER_ACTIVITY_TIMEOUT); }
 
 	/**
 	 * @return Current latency or 0 if unknown (max: 65535)
@@ -292,6 +291,20 @@ public:
 	 */
 	void clearPaths(bool fixedToo);
 
+	/**
+	 * Reset paths within a given scope
+	 *
+	 * For fixed paths in this scope, a packet is sent. Non-fixed paths in this
+	 * scope are forgotten. If there are no paths remaining, a message is sent
+	 * indirectly to reestablish connectivity if we're actively exchanging
+	 * data with this peer (alive).
+	 *
+	 * @param RR Runtime environment
+	 * @param scope IP scope of paths to reset
+	 * @param now Current time
+	 */
+	void resetWithinScope(const RuntimeEnvironment *RR,InetAddress::IpScope scope,uint64_t now);
+
 	/**
 	 * @return 256-bit secret symmetric encryption key
 	 */

+ 40 - 1
node/SelfAwareness.cpp

@@ -25,6 +25,10 @@
  * LLC. Start here: http://www.zerotier.com/
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
 #include "Constants.hpp"
 #include "SelfAwareness.hpp"
 #include "RuntimeEnvironment.hpp"
@@ -35,17 +39,52 @@
 
 namespace ZeroTier {
 
+class _ResetWithinScope
+{
+public:
+	_ResetWithinScope(const RuntimeEnvironment *renv,uint64_t now,InetAddress::IpScope scope) :
+		RR(renv),
+		_now(now),
+		_scope(scope) {}
+	inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(RR,_scope,_now); }
+private:
+	const RuntimeEnvironment *RR;
+	uint64_t _now;
+	InetAddress::IpScope _scope;
+};
+
 SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
 	RR(renv)
 {
+	memset(_lastPhysicalAddress,0,sizeof(_lastPhysicalAddress));
 }
 
 SelfAwareness::~SelfAwareness()
 {
 }
 
-void SelfAwareness::iam(const InetAddress &physicalAddress,bool trusted)
+void SelfAwareness::iam(const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted)
 {
+	const unsigned int scope = (unsigned int)myPhysicalAddress.ipScope();
+
+	// This code depends on the numeric values assigned to scopes in InetAddress.hpp
+	if ((scope > 0)&&(scope < (unsigned int)InetAddress::IP_SCOPE_LOOPBACK)) {
+		/* For now only trusted peers are permitted to inform us of changes to
+		 * our global Internet IP or to changes of NATed IPs. We'll let peers on
+		 * private, shared, or link-local networks inform us of changes as long
+		 * as they too are at the same scope. This discrimination avoids a DoS
+		 * attack in which an attacker could force us to reset our connections. */
+		if ( (!trusted) && ((scope == (unsigned int)InetAddress::IP_SCOPE_GLOBAL)||(scope != (unsigned int)reporterPhysicalAddress.ipScope())) )
+			return;
+
+		InetAddress &lastPhy = _lastPhysicalAddress[scope - 1];
+		if ((lastPhy)&&(lastPhy != myPhysicalAddress)) {
+			lastPhy = myPhysicalAddress;
+			_ResetWithinScope rset(RR,RR->node->now(),(InetAddress::IpScope)scope);
+			RR->topology->eachPeer<_ResetWithinScope &>(rset);
+		}
+	}
+
 	Mutex::Lock _l(_lock);
 }
 

+ 5 - 3
node/SelfAwareness.hpp

@@ -47,14 +47,16 @@ public:
 	/**
 	 * Called when a trusted remote peer informs us of our external network address
 	 *
-	 * @param physicalAddress Physical address as reflected by any trusted peer
-	 * @param trusted True if this peer is trusted
+	 * @param reporterPhysicalAddress Physical address that reporting peer seems to have
+	 * @param myPhysicalAddress Physical address that peer says we have
+	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 */
-	void iam(const InetAddress &physicalAddress,bool trusted);
+	void iam(const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted);
 
 private:
 	const RuntimeEnvironment *RR;
 	Mutex _lock;
+	InetAddress _lastPhysicalAddress[5]; // 5 == the number of address classes we care about, see InetAddress.hpp and SelfAwareness.cpp
 };
 
 } // namespace ZeroTier

+ 0 - 30
node/Topology.cpp

@@ -253,36 +253,6 @@ void Topology::clean(uint64_t now)
 	}
 }
 
-/*
-bool Topology::updateSurface(const SharedPtr<Peer> &remotePeer,const InetAddress &mirroredAddress,uint64_t now)
-{
-	Mutex::Lock _l(_lock);
-
-	if (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),remotePeer->address()) == _supernodeAddresses.end())
-		return false;
-
-	if (_surface.update(mirroredAddress)) {
-		// Clear non-fixed paths for all peers -- will force reconnect on next activity
-		for(std::map< Address,SharedPtr<Peer> >::const_iterator ap(_activePeers.begin());ap!=_activePeers.end();++ap)
-			ap->second->clearPaths(false);
-
-		// Reset TCP tunneling if our global addressing has changed
-		if (!mirroredAddress.isLinkLocal())
-			(const_cast <RuntimeEnvironment *>(RR))->tcpTunnelingEnabled = false;
-
-		// Ping supernodes now (other than the one we might have just heard from)
-		for(std::vector< SharedPtr<Peer> >::const_iterator sn(_supernodePeers.begin());sn!=_supernodePeers.end();++sn) {
-			if (remotePeer != *sn)
-				(*sn)->sendPing(RR,now);
-		}
-
-		return true;
-	}
-
-	return false;
-}
-*/
-
 bool Topology::authenticateRootTopology(const Dictionary &rt)
 {
 	try {

+ 0 - 38
node/Topology.hpp

@@ -280,44 +280,6 @@ public:
 		uint64_t _now;
 		const RuntimeEnvironment *RR;
 	};
-
-	/**
-	 * Function object to forget direct links to active peers and then ping them indirectly
-	 */
-	class ResetActivePeers
-	{
-	public:
-		ResetActivePeers(const RuntimeEnvironment *renv,uint64_t now) throw() :
-			_now(now),
-			_supernode(renv->topology->getBestSupernode()),
-			_supernodeAddresses(renv->topology->supernodeAddresses()),
-			RR(renv) {}
-
-		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
-		{
-			p->clearPaths(false); // false means don't forget 'fixed' paths e.g. supernodes
-
-			Packet outp(p->address(),RR->identity.address(),Packet::VERB_NOP);
-			outp.armor(p->key(),false); // no need to encrypt a NOP
-
-			if (std::find(_supernodeAddresses.begin(),_supernodeAddresses.end(),p->address()) != _supernodeAddresses.end()) {
-				// Send NOP directly to supernodes
-				p->send(RR,outp.data(),outp.size(),_now);
-			} else {
-				// Send NOP indirectly to regular peers if still active, triggering a new RENDEZVOUS
-				if (((_now - p->lastFrame()) < ZT_PEER_PATH_ACTIVITY_TIMEOUT)&&(_supernode)) {
-					TRACE("sending reset NOP to %s",p->address().toString().c_str());
-					_supernode->send(RR,outp.data(),outp.size(),_now);
-				}
-			}
-		}
-
-	private:
-		uint64_t _now;
-		SharedPtr<Peer> _supernode;
-		std::vector<Address> _supernodeAddresses;
-		const RuntimeEnvironment *RR;
-	};
 #endif
 
 	/**