Bladeren bron

Implemented empirical determination of external addressing, paritioned per scope.

Adam Ierymenko 10 jaren geleden
bovenliggende
commit
52c3b7c34e
10 gewijzigde bestanden met toevoegingen van 102 en 95 verwijderingen
  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
 
 	/**