From 4e9280fc7a41aac6171165487412d2a9abd432a4 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 5 Sep 2014 16:23:24 -0700
Subject: [PATCH] Rip out dead "firewall opener" code, replace in pipeline with
 anti-symmetric-NAT tactics.

---
 node/Constants.hpp     | 28 ++--------------------------
 node/Node.cpp          |  3 ---
 node/Path.hpp          | 12 ++----------
 node/Peer.cpp          | 17 -----------------
 node/Peer.hpp          | 26 +-------------------------
 node/SocketManager.cpp | 14 --------------
 node/SocketManager.hpp | 10 ----------
 node/Switch.cpp        | 28 ++++++++++++++++++++++------
 node/Topology.hpp      | 23 -----------------------
 9 files changed, 27 insertions(+), 134 deletions(-)

diff --git a/node/Constants.hpp b/node/Constants.hpp
index ca4fc37ac..21360e51f 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -274,27 +274,6 @@
  */
 #define ZT_PEER_DIRECT_PING_DELAY 120000
 
-/**
- * Delay in ms between firewall opener packets to direct links
- *
- * This should be lower than the UDP conversation entry timeout in most
- * stateful firewalls.
- *
- * Uncomment to disable firewall openers.
- */
-//#define ZT_FIREWALL_OPENER_DELAY 30000
-
-/**
- * Number of hops to open via firewall opener packets
- *
- * The firewall opener code iterates from 1 to this value (inclusive), sending
- * a tiny packet with each TTL value.
- *
- * 2 should permit traversal of double-NAT configurations, such as from inside
- * a VM running behind local NAT on a host that is itself behind NAT.
- */
-//#define ZT_FIREWALL_OPENER_HOPS 2
-
 /**
  * Delay between requests for updated network autoconf information
  */
@@ -371,12 +350,9 @@
 #define ZT_MIN_UNITE_INTERVAL 30000
 
 /**
- * Delay in milliseconds between firewall opener and real packet for NAT-t
- *
- * If firewall openers are disbled, it just waits this long before sending
- * NAT-t packets.
+ * Delay between initial direct NAT-t packet and more aggressive techniques
  */
-#define ZT_RENDEZVOUS_NAT_T_DELAY 500
+#define ZT_NAT_T_TACTICAL_ESCALATION_DELAY 2000
 
 /**
  * Size of anti-recursion history (see AntiRecursion.hpp)
diff --git a/node/Node.cpp b/node/Node.cpp
index 99732c727..62a4d9aee 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -728,9 +728,6 @@ Node::ReasonForTermination Node::run()
 					lastPingCheck = now;
 					try {
 						_r->topology->eachPeer(Topology::PingPeersThatNeedPing(_r,now));
-#ifdef ZT_FIREWALL_OPENER_DELAY
-						_r->topology->eachPeer(Topology::OpenPeersThatNeedFirewallOpener(_r,now));
-#endif
 					} catch (std::exception &exc) {
 						LOG("unexpected exception running ping check cycle: %s",exc.what());
 					} catch ( ... ) {
diff --git a/node/Path.hpp b/node/Path.hpp
index e1900bbba..67dd27fc5 100644
--- a/node/Path.hpp
+++ b/node/Path.hpp
@@ -39,7 +39,7 @@
 #include "Utils.hpp"
 #include "Buffer.hpp"
 
-#define ZT_PATH_SERIALIZATION_VERSION 2
+#define ZT_PATH_SERIALIZATION_VERSION 3
 
 namespace ZeroTier {
 
@@ -60,7 +60,6 @@ public:
 	Path() :
 		_lastSend(0),
 		_lastReceived(0),
-		_lastFirewallOpener(0),
 		_lastPing(0),
 		_addr(),
 		_type(PATH_TYPE_NULL),
@@ -75,7 +74,6 @@ public:
 	Path(const InetAddress &addr,Type t,bool fixed = false) :
 		_lastSend(0),
 		_lastReceived(0),
-		_lastFirewallOpener(0),
 		_lastPing(0),
 		_addr(addr),
 		_type(t),
@@ -95,7 +93,6 @@ public:
 
 	inline uint64_t lastSend() const throw() { return _lastSend; }
 	inline uint64_t lastReceived() const throw() { return _lastReceived; }
-	inline uint64_t lastFirewallOpener() const throw() { return _lastFirewallOpener; }
 	inline uint64_t lastPing() const throw() { return _lastPing; }
 
 	inline bool fixed() const throw() { return _fixed; }
@@ -103,7 +100,6 @@ public:
 
 	inline void sent(uint64_t t) throw() { _lastSend = t; }
 	inline void received(uint64_t t) throw() { _lastReceived = t; }
-	inline void firewallOpenerSent(uint64_t t) throw() { _lastFirewallOpener = t; }
 	inline void pinged(uint64_t t) throw() { _lastPing = t; }
 
 	/**
@@ -130,12 +126,11 @@ public:
 			case PATH_TYPE_TCP_OUT: t = "tcp_out"; break;
 			case PATH_TYPE_TCP_IN: t = "tcp_in"; break;
 		}
-		Utils::snprintf(tmp,sizeof(tmp),"%s;%s;%lld;%lld;%lld;%lld;%s",
+		Utils::snprintf(tmp,sizeof(tmp),"%s;%s;%lld;%lld;%lld;%s",
 			t,
 			_addr.toString().c_str(),
 			(long long)((_lastSend != 0) ? ((now - _lastSend) / 1000LL) : -1),
 			(long long)((_lastReceived != 0) ? ((now - _lastReceived) / 1000LL) : -1),
-			(long long)((_lastFirewallOpener != 0) ? ((now - _lastFirewallOpener) / 1000LL) : -1),
 			(long long)((_lastPing != 0) ? ((now - _lastPing) / 1000LL) : -1),
 			((_fixed) ? "fixed" : (active(now) ? "active" : "inactive"))
 		);
@@ -161,7 +156,6 @@ public:
 		b.append((unsigned char)ZT_PATH_SERIALIZATION_VERSION);
 		b.append(_lastSend);
 		b.append(_lastReceived);
-		b.append(_lastFirewallOpener);
 		b.append(_lastPing);
 		b.append((unsigned char)_addr.type());
 		switch(_addr.type()) {
@@ -189,7 +183,6 @@ public:
 
 		_lastSend = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 		_lastReceived = b.template at<uint64_t>(p); p += sizeof(uint64_t);
-		_lastFirewallOpener = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 		_lastPing = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 		switch((InetAddress::AddressType)b[p++]) {
 			case InetAddress::TYPE_IPV4:
@@ -213,7 +206,6 @@ public:
 private:
 	volatile uint64_t _lastSend;
 	volatile uint64_t _lastReceived;
-	volatile uint64_t _lastFirewallOpener;
 	volatile uint64_t _lastPing;
 	InetAddress _addr;
 	Type _type;
diff --git a/node/Peer.cpp b/node/Peer.cpp
index b10cc1f2f..e05352c98 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -181,23 +181,6 @@ Path::Type Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int
 	return Path::PATH_TYPE_NULL;
 }
 
-#ifdef ZT_FIREWALL_OPENER_DELAY
-bool Peer::sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now)
-{
-	bool sent = false;
-	Mutex::Lock _l(_lock);
-
-	for(std::vector<Path>::iterator p(_paths.begin());p!=_paths.end();++p) {
-		if (p->type() == Path::PATH_TYPE_UDP) {
-			for(unsigned int h=1;h<=ZT_FIREWALL_OPENER_HOPS;++h)
-				sent |= _r->sm->sendFirewallOpener(p->address(),h);
-		}
-	}
-
-	return sent;
-}
-#endif
-
 bool Peer::sendPing(const RuntimeEnvironment *_r,uint64_t now)
 {
 	bool sent = false;
diff --git a/node/Peer.hpp b/node/Peer.hpp
index d3c3669ba..05005a301 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -50,7 +50,7 @@
 #include "NonCopyable.hpp"
 #include "Mutex.hpp"
 
-#define ZT_PEER_SERIALIZATION_VERSION 10
+#define ZT_PEER_SERIALIZATION_VERSION 11
 
 namespace ZeroTier {
 
@@ -142,17 +142,6 @@ public:
 	 */
 	Path::Type send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now);
 
-#ifdef ZT_FIREWALL_OPENER_DELAY
-	/**
-	 * Send firewall opener to all UDP paths
-	 * 
-	 * @param _r Runtime environment
-	 * @param now Current time
-	 * @return True if send appears successful for at least one address type
-	 */
-	bool sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now);
-#endif
-
 	/**
 	 * Send HELLO to a peer via all direct paths available
 	 *
@@ -193,19 +182,6 @@ public:
 		return false;
 	}
 
-	/**
-	 * @return Last successfully sent firewall opener for any path
-	 */
-	inline uint64_t lastFirewallOpener() const
-		throw()
-	{
-		uint64_t x = 0;
-		Mutex::Lock _l(_lock);
-		for(std::vector<Path>::const_iterator p(_paths.begin());p!=_paths.end();++p)
-			x = std::max(x,p->lastFirewallOpener());
-		return x;
-	}
-
 	/**
 	 * @return Time of last direct packet receive for any path
 	 */
diff --git a/node/SocketManager.cpp b/node/SocketManager.cpp
index f4ffdb231..8e3ee9e0e 100644
--- a/node/SocketManager.cpp
+++ b/node/SocketManager.cpp
@@ -452,20 +452,6 @@ bool SocketManager::send(const InetAddress &to,bool tcp,bool autoConnectTcp,cons
 	return false;
 }
 
-#ifdef ZT_FIREWALL_OPENER_DELAY
-bool SocketManager::sendFirewallOpener(const InetAddress &to,int hopLimit)
-{
-	if (to.isV4()) {
-		if (_udpV4Socket)
-			return ((UdpSocket *)_udpV4Socket.ptr())->sendWithHopLimit(to,"",1,hopLimit);
-	} else if (to.isV6()) {
-		if (_udpV6Socket)
-			return ((UdpSocket *)_udpV6Socket.ptr())->sendWithHopLimit(to,"",1,hopLimit);
-	}
-	return false;
-}
-#endif
-
 void SocketManager::poll(unsigned long timeout)
 {
 	fd_set rfds,wfds,efds;
diff --git a/node/SocketManager.hpp b/node/SocketManager.hpp
index 8dee7e915..81d2e7809 100644
--- a/node/SocketManager.hpp
+++ b/node/SocketManager.hpp
@@ -103,16 +103,6 @@ public:
 	 */
 	inline bool sendUdp(const InetAddress &to,const void *msg,unsigned int msglen) { return send(to,false,false,msg,msglen); }
 
-	/**
-	 * Send a UDP packet with a limited IP TTL
-	 *
-	 * @param to Destination address
-	 * @param hopLimit IP TTL
-	 */
-#ifdef ZT_FIREWALL_OPENER_DELAY
-	bool sendFirewallOpener(const InetAddress &to,int hopLimit);
-#endif
-
 	/**
 	 * Perform I/O polling operation (e.g. select())
 	 *
diff --git a/node/Switch.cpp b/node/Switch.cpp
index fa8a22c0e..dd4aec216 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -461,13 +461,14 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 
 void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr)
 {
-#ifdef ZT_FIREWALL_OPENER_HOPS
-	_r->sm->sendFirewallOpener(atAddr,ZT_FIREWALL_OPENER_HOPS);
-#endif
+	// Send simple packet directly to indicated address -- works for most NATs
+	sendHELLO(peer,atAddr);
+	TRACE("sending NAT-t HELLO to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
 
+	// If we have not punched through after this timeout, open refreshing can of whupass
 	{
 		Mutex::Lock _l(_contactQueue_m);
-		_contactQueue.push_back(ContactQueueEntry(peer,Utils::now() + ZT_RENDEZVOUS_NAT_T_DELAY,atAddr));
+		_contactQueue.push_back(ContactQueueEntry(peer,Utils::now() + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr));
 	}
 
 	// Kick main loop out of wait so that it can pick up this
@@ -484,8 +485,23 @@ unsigned long Switch::doTimerTasks()
 		Mutex::Lock _l(_contactQueue_m);
 		for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
 			if (now >= qi->fireAtTime) {
-				TRACE("sending NAT-T HELLO to %s(%s)",qi->peer->address().toString().c_str(),qi->inaddr.toString().c_str());
-				sendHELLO(qi->peer,qi->inaddr);
+				if (!qi->peer->hasActiveDirectPath(now)) {
+					TRACE("deploying aggressive NAT-t against %s(%s)",qi->peer->address().toString().c_str(),qi->inaddr.toString().c_str());
+
+					/* Shotgun approach -- literally -- against symmetric NATs. Most of these
+					 * either increment or decrement ports so this gets a good number. Also try
+					 * the original port one more time for good measure, since sometimes it
+					 * fails first time around. */
+					int p = (int)qi->inaddr.port() - 2;
+					for(int k=0;k<5;++k) {
+						if ((p > 0)&&(p <= 0xffff)) {
+							qi->inaddr.setPort((unsigned int)p);
+							sendHELLO(qi->peer,qi->inaddr);
+						}
+						++p;
+					}
+				}
+
 				_contactQueue.erase(qi++);
 			} else {
 				nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 478b4fc32..6349fe877 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -207,29 +207,6 @@ public:
 			f(*this,*p);
 	}
 
-#ifdef ZT_FIREWALL_OPENER_DELAY
-	/**
-	 * Function object to collect peers that need a firewall opener sent
-	 */
-	class OpenPeersThatNeedFirewallOpener
-	{
-	public:
-		OpenPeersThatNeedFirewallOpener(const RuntimeEnvironment *renv,uint64_t now) throw() :
-			_now(now),
-			_r(renv) {}
-
-		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
-		{
-			if ((p->hasDirectPath())&&((_now - std::max(p->lastFirewallOpener(),p->lastDirectSend())) >= ZT_FIREWALL_OPENER_DELAY))
-				p->sendFirewallOpener(_r,_now);
-		}
-
-	private:
-		uint64_t _now;
-		const RuntimeEnvironment *_r;
-	};
-#endif
-
 	/**
 	 * Pings all peers that need a ping sent, excluding supernodes
 	 *