Implemented empirical determination of external addressing, paritioned per scope.

This commit is contained in:
Adam Ierymenko 2015-04-07 11:56:10 -07:00
parent 817824b88b
commit 52c3b7c34e
10 changed files with 102 additions and 95 deletions

View File

@ -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
*

View File

@ -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);

View File

@ -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)); }

View File

@ -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) );
}
/**

View File

@ -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;

View File

@ -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
*/

View File

@ -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);
}

View File

@ -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

View File

@ -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 {

View File

@ -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
/**