From e30ba3e1382b50aa8f393132204f8f27ccfb03f9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 27 Jul 2015 16:43:05 -0700 Subject: Eliminate some aggressive port scanning NAT-t behavior that has proven ineffective. --- node/Switch.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index cf4fe249..3d9ef5b1 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -451,7 +451,7 @@ unsigned long Switch::doTimerTasks(uint64_t now) { unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum - { // Aggressive NAT traversal time! + { Mutex::Lock _l(_contactQueue_m); for(std::list::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) { if (now >= qi->fireAtTime) { @@ -460,26 +460,17 @@ unsigned long Switch::doTimerTasks(uint64_t now) _contactQueue.erase(qi++); continue; } else { - // Nope, nothing yet. Time to kill some kittens. if (qi->strategyIteration == 0) { // First strategy: send packet directly (we already tried this but try again) qi->peer->attemptToContactAt(RR,qi->inaddr,now); - } else if (qi->strategyIteration <= 9) { - // Strategies 1-9: try escalating ports + } else if (qi->strategyIteration <= 4) { + // Strategies 1-4: try escalating ports InetAddress tmpaddr(qi->inaddr); int p = (int)qi->inaddr.port() + qi->strategyIteration; if (p < 0xffff) { tmpaddr.setPort((unsigned int)p); qi->peer->attemptToContactAt(RR,tmpaddr,now); } else qi->strategyIteration = 9; - } else if (qi->strategyIteration <= 18) { - // Strategies 10-18: try ports below - InetAddress tmpaddr(qi->inaddr); - int p = (int)qi->inaddr.port() - (qi->strategyIteration - 9); - if (p >= 1024) { - tmpaddr.setPort((unsigned int)p); - qi->peer->attemptToContactAt(RR,tmpaddr,now); - } else qi->strategyIteration = 18; } else { // All strategies tried, expire entry _contactQueue.erase(qi++); -- cgit v1.2.3 From 821f1f366e468d1ff2f45a9e871ccec04b80fd3f Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 27 Jul 2015 17:34:58 -0700 Subject: Fix to NAT escalation sequence. --- node/Switch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index 3d9ef5b1..18935ce5 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -470,7 +470,7 @@ unsigned long Switch::doTimerTasks(uint64_t now) if (p < 0xffff) { tmpaddr.setPort((unsigned int)p); qi->peer->attemptToContactAt(RR,tmpaddr,now); - } else qi->strategyIteration = 9; + } else qi->strategyIteration = 5; } else { // All strategies tried, expire entry _contactQueue.erase(qi++); -- cgit v1.2.3 From b31071463cafda54afbf6f01d37aa7451b217b12 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 11:28:47 -0700 Subject: Try another NAT traversal improvement. --- node/Constants.hpp | 3 +++ node/IncomingPacket.cpp | 2 +- node/SelfAwareness.cpp | 15 +++++++++++++++ node/SelfAwareness.hpp | 5 +++++ node/Switch.cpp | 19 ++++++++++++++----- node/Switch.hpp | 4 ++-- 6 files changed, 40 insertions(+), 8 deletions(-) (limited to 'node/Switch.cpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index c192381c..31a4c313 100644 --- a/node/Constants.hpp +++ b/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 diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 76c47933..b1fda8ef 100644 --- a/node/IncomingPacket.cpp +++ b/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()); } diff --git a/node/SelfAwareness.cpp b/node/SelfAwareness.cpp index 00015788..716cf7f3 100644 --- a/node/SelfAwareness.cpp +++ b/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 diff --git a/node/SelfAwareness.hpp b/node/SelfAwareness.hpp index 4eede592..d3b79d18 100644 --- a/node/SelfAwareness.hpp +++ b/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 { diff --git a/node/Switch.cpp b/node/Switch.cpp index 18935ce5..a580078e 100644 --- a/node/Switch.cpp +++ b/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,const InetAddress &atAddr) +void Switch::rendezvous(const SharedPtr &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)); diff --git a/node/Switch.hpp b/node/Switch.hpp index e7f1523a..ac85606e 100644 --- a/node/Switch.hpp +++ b/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,const InetAddress &atAddr); + void rendezvous(const SharedPtr &peer,const InetAddress &atAddr); /** * Request WHOIS on a given address -- cgit v1.2.3 From 17bfd4d55e96390147e2804b81c08985816ac4cd Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 11:32:34 -0700 Subject: Add TRACE for NAT-t debugging. --- node/Switch.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index a580078e..6f4659d5 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -399,8 +399,11 @@ void Switch::rendezvous(const SharedPtr &peer,const InetAddress &atAddr) * 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())) + if ((atAddr.ss_family != AF_INET)||(!RR->sa->areGlobalIPv4PortsRandomized())) { peer->attemptToContactAt(RR,atAddr,now); + } else { + TRACE("behind randomizing symmetric NAT -- delaying initial message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str()); + } // After 1s, try again and perhaps try more NAT-t strategies { -- cgit v1.2.3 From d2bfdfa6e79e54ba1d5127a75a56f7ec57415cf9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 11:57:18 -0700 Subject: Play with NAT-t tweaks some more. --- node/Switch.cpp | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index 6f4659d5..247b2d18 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -391,24 +391,15 @@ void Switch::rendezvous(const SharedPtr &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 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())) { + if ((atAddr.ss_family == AF_INET)&&(RR->sa->areGlobalIPv4PortsRandomized())) { peer->attemptToContactAt(RR,atAddr,now); } else { TRACE("behind randomizing symmetric NAT -- delaying initial message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str()); } - // 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)); + _contactQueue.push_back(ContactQueueEntry(peer,now + (ZT_NAT_T_TACTICAL_ESCALATION_DELAY / 2),atAddr)); } } @@ -473,10 +464,10 @@ unsigned long Switch::doTimerTasks(uint64_t now) continue; } else { if (qi->strategyIteration == 0) { - // First strategy: send packet directly (we already tried this but try again) + // First strategy: send packet directly to destination qi->peer->attemptToContactAt(RR,qi->inaddr,now); } else if (qi->strategyIteration <= 4) { - // Strategies 1-4: try escalating ports + // Strategies 1-4: try escalating ports for symmetric NATs that remap sequentially InetAddress tmpaddr(qi->inaddr); int p = (int)qi->inaddr.port() + qi->strategyIteration; if (p < 0xffff) { -- cgit v1.2.3 From 4564dd95ffa287b3752a59378137c59773d51ef9 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 12:00:50 -0700 Subject: Revert... no luck with any of that. --- node/Switch.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index 247b2d18..bf9308b0 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -390,16 +390,10 @@ void Switch::rendezvous(const SharedPtr &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(); - - if ((atAddr.ss_family == AF_INET)&&(RR->sa->areGlobalIPv4PortsRandomized())) { - peer->attemptToContactAt(RR,atAddr,now); - } else { - TRACE("behind randomizing symmetric NAT -- delaying initial message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str()); - } - + peer->attemptToContactAt(RR,atAddr,now); { Mutex::Lock _l(_contactQueue_m); - _contactQueue.push_back(ContactQueueEntry(peer,now + (ZT_NAT_T_TACTICAL_ESCALATION_DELAY / 2),atAddr)); + _contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr)); } } -- cgit v1.2.3 From 21e6850722539d4ff72b6c5841da47356233bb67 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 12:18:59 -0700 Subject: Cancel NAT-t attempts if peer is no longer "alive" --- node/Switch.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index bf9308b0..02881331 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -452,8 +452,8 @@ unsigned long Switch::doTimerTasks(uint64_t now) Mutex::Lock _l(_contactQueue_m); for(std::list::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) { if (now >= qi->fireAtTime) { - if (qi->peer->hasActiveDirectPath(now)) { - // We've successfully NAT-t'd, so cancel attempt + if ((!qi->peer->alive(now))||(qi->peer->hasActiveDirectPath(now))) { + // Cancel attempt if we've already connected or peer is no longer "alive" _contactQueue.erase(qi++); continue; } else { -- cgit v1.2.3 From eea8d58afa6ceb0fd04d6d82c551ff9f81a0ffe7 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 28 Jul 2015 12:39:03 -0700 Subject: docs,cleanup --- node/Switch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'node/Switch.cpp') diff --git a/node/Switch.cpp b/node/Switch.cpp index 02881331..989f497a 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -448,7 +448,7 @@ unsigned long Switch::doTimerTasks(uint64_t now) { unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum - { + { // Iterate through NAT traversal strategies for entries in contact queue Mutex::Lock _l(_contactQueue_m); for(std::list::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) { if (now >= qi->fireAtTime) { -- cgit v1.2.3