Ver Fonte

Try another NAT traversal improvement.

Adam Ierymenko há 10 anos atrás
pai
commit
b31071463c
6 ficheiros alterados com 40 adições e 8 exclusões
  1. 3 0
      node/Constants.hpp
  2. 1 1
      node/IncomingPacket.cpp
  3. 15 0
      node/SelfAwareness.cpp
  4. 5 0
      node/SelfAwareness.hpp
  5. 14 5
      node/Switch.cpp
  6. 2 2
      node/Switch.hpp

+ 3 - 0
node/Constants.hpp

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

+ 1 - 1
node/IncomingPacket.cpp

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

+ 15 - 0
node/SelfAwareness.cpp

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

+ 5 - 0
node/SelfAwareness.hpp

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

+ 14 - 5
node/Switch.cpp

@@ -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);
-
-	// If we have not punched through after this timeout, open refreshing can of whupass
+	/* 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);
+
+	// 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));

+ 2 - 2
node/Switch.hpp

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