Try another NAT traversal improvement.

This commit is contained in:
Adam Ierymenko 2015-07-28 11:28:47 -07:00
parent dda376c9eb
commit b31071463c
6 changed files with 39 additions and 7 deletions

View File

@ -296,6 +296,9 @@
/**
* Delay between initial direct NAT-t packet and more aggressive techniques
*
* This may also be a delay before sending the first packet if we determine
* that we should wait for the remote to initiate rendezvous first.
*/
#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 1000

View File

@ -488,7 +488,7 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
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,_remoteAddress,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP);
RR->sw->contact(withPeer,atAddr);
RR->sw->rendezvous(withPeer,atAddr);
} else {
TRACE("dropped corrupt RENDEZVOUS from %s(%s) (bad address or port)",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
}

View File

@ -147,4 +147,19 @@ void SelfAwareness::clean(uint64_t now)
}
}
bool SelfAwareness::areGlobalIPv4PortsRandomized() const
{
int port = 0;
Mutex::Lock _l(_phy_m);
for(std::map< PhySurfaceKey,PhySurfaceEntry >::const_iterator p(_phy.begin());p!=_phy.end();++p) {
if ((p->first.scope == InetAddress::IP_SCOPE_GLOBAL)&&(p->second.mySurface.ss_family == AF_INET)) {
const int tmp = (int)p->second.mySurface.port();
if ((port)&&(tmp != port))
return true;
else port = tmp;
}
}
return false;
}
} // namespace ZeroTier

View File

@ -66,6 +66,11 @@ public:
*/
void clean(uint64_t now);
/**
* @return True if our external (global scope) IPv4 ports appear to be randomized by a NAT device
*/
bool areGlobalIPv4PortsRandomized() const;
private:
struct PhySurfaceKey
{

View File

@ -43,6 +43,7 @@
#include "Topology.hpp"
#include "Peer.hpp"
#include "AntiRecursion.hpp"
#include "SelfAwareness.hpp"
#include "Packet.hpp"
namespace ZeroTier {
@ -385,15 +386,23 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
return true;
}
void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr)
void Switch::rendezvous(const SharedPtr<Peer> &peer,const InetAddress &atAddr)
{
TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
const uint64_t now = RR->node->now();
// Attempt to contact directly
peer->attemptToContactAt(RR,atAddr,now);
/* Attempt direct contact now unless we are IPv4 and our external ports
* appear to be randomized by a NAT device. In that case, we should let
* the other side send a message first. Why? If the other side is also
* randomized and symmetric, we are probably going to fail. But if the
* other side is "port restricted" but otherwise sane, us sending a
* packet first may actually close the remote's outgoing port to us!
* This assists with NAT-t in cases where one side is symmetric and the
* other is full cone but port restricted. */
if ((atAddr.ss_family != AF_INET)||(!RR->sa->areGlobalIPv4PortsRandomized()))
peer->attemptToContactAt(RR,atAddr,now);
// If we have not punched through after this timeout, open refreshing can of whupass
// After 1s, try again and perhaps try more NAT-t strategies
{
Mutex::Lock _l(_contactQueue_m);
_contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr));

View File

@ -136,12 +136,12 @@ public:
bool unite(const Address &p1,const Address &p2,bool force);
/**
* Send NAT traversal messages to peer at the given candidate address
* Attempt NAT traversal to peer at a given physical address
*
* @param peer Peer to contact
* @param atAddr Address of peer
*/
void contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr);
void rendezvous(const SharedPtr<Peer> &peer,const InetAddress &atAddr);
/**
* Request WHOIS on a given address