From dcb1233b0d5478f2f544a99cf24314155e711050 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 3 Feb 2017 23:54:02 -0800
Subject: [PATCH] Slight refactor to RENEDEZVOUS sending code for federation.

---
 node/Cluster.cpp |   4 +-
 node/Peer.cpp    |   2 +-
 node/Peer.hpp    |   2 +-
 node/Switch.cpp  | 171 +++++++++++++++++++----------------------------
 node/Switch.hpp  |   2 -
 5 files changed, 72 insertions(+), 109 deletions(-)

diff --git a/node/Cluster.cpp b/node/Cluster.cpp
index 356a0887e..001224025 100644
--- a/node/Cluster.cpp
+++ b/node/Cluster.cpp
@@ -400,7 +400,7 @@ void Cluster::handleIncomingStateMessage(const void *msg,unsigned int len)
 						SharedPtr<Peer> localPeer(RR->topology->getPeerNoCache(localPeerAddress));
 						if ((localPeer)&&(numRemotePeerPaths > 0)) {
 							InetAddress bestLocalV4,bestLocalV6;
-							localPeer->getBestActiveAddresses(now,bestLocalV4,bestLocalV6);
+							localPeer->getRendezvousAddresses(now,bestLocalV4,bestLocalV6);
 
 							InetAddress bestRemoteV4,bestRemoteV6;
 							for(unsigned int i=0;i<numRemotePeerPaths;++i) {
@@ -652,7 +652,7 @@ void Cluster::relayViaCluster(const Address &fromPeerAddress,const Address &toPe
 			if (fromPeerAddress) {
 				SharedPtr<Peer> fromPeer(RR->topology->getPeerNoCache(fromPeerAddress));
 				if (fromPeer)
-					fromPeer->getBestActiveAddresses(now,v4,v6);
+					fromPeer->getRendezvousAddresses(now,v4,v6);
 			}
 			uint8_t addrCount = 0;
 			if (v4)
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 50135b9f0..129c2437b 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -446,7 +446,7 @@ void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uin
 	}
 }
 
-void Peer::getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
+void Peer::getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const
 {
 	Mutex::Lock _l(_paths_m);
 
diff --git a/node/Peer.hpp b/node/Peer.hpp
index 06abde3fa..bbe13a2eb 100644
--- a/node/Peer.hpp
+++ b/node/Peer.hpp
@@ -208,7 +208,7 @@ public:
 	 * @param v4 Result parameter to receive active IPv4 address, if any
 	 * @param v6 Result parameter to receive active IPv6 address, if any
 	 */
-	void getBestActiveAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
+	void getRendezvousAddresses(uint64_t now,InetAddress &v4,InetAddress &v6) const;
 
 	/**
 	 * @param now Current time
diff --git a/node/Switch.cpp b/node/Switch.cpp
index 2ab3ccfca..b53466cfa 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -64,10 +64,6 @@ Switch::Switch(const RuntimeEnvironment *renv) :
 {
 }
 
-Switch::~Switch()
-{
-}
-
 void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
 {
 	try {
@@ -221,7 +217,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 
 					if (packet.hops() < ZT_RELAY_MAX_HOPS) {
 #ifdef ZT_ENABLE_CLUSTER
-						if (source != RR->identity.address())
+						if (source != RR->identity.address()) // don't increment hops for cluster frontplane relays
 							packet.incrementHops();
 #else
 						packet.incrementHops();
@@ -229,13 +225,74 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 
 						SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
 						if ((relayTo)&&((relayTo->sendDirect(packet.data(),packet.size(),now,false)))) {
-							if (source != RR->identity.address()) {
-								Mutex::Lock _l(_lastUniteAttempt_m);
-								uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
-								if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
-									luts = now;
-									_unite(source,destination);
+							if (source != RR->identity.address()) { // don't send RENDEZVOUS for cluster frontplane relays
+
+								bool shouldUnite;
+								{
+									Mutex::Lock _l(_lastUniteAttempt_m);
+									uint64_t &lastUniteAt = _lastUniteAttempt[_LastUniteKey(source,destination)];
+									shouldUnite = ((now - lastUniteAt) >= ZT_MIN_UNITE_INTERVAL);
+									if (shouldUnite)
+										lastUniteAt = now;
 								}
+
+								if (shouldUnite) {
+									const InetAddress *hintToSource = (InetAddress *)0;
+									const InetAddress *hintToDest = (InetAddress *)0;
+
+									InetAddress destV4,destV6;
+									InetAddress sourceV4,sourceV6;
+									relayTo->getRendezvousAddresses(now,destV4,destV6);
+
+									const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(source));
+									if (sourcePeer) {
+										sourcePeer->getRendezvousAddresses(now,sourceV4,sourceV6);
+										if ((destV6)&&(sourceV6)) {
+											hintToSource = &destV6;
+											hintToDest = &sourceV6;
+										} else if ((destV4)&&(sourceV4)) {
+											hintToSource = &destV4;
+											hintToDest = &sourceV4;
+										}
+
+										if ((hintToSource)&&(hintToDest)) {
+											TRACE(">> RENDEZVOUS: %s(%s) <> %s(%s)",p1.toString().c_str(),p1a->toString().c_str(),p2.toString().c_str(),p2a->toString().c_str());
+											unsigned int alt = (unsigned int)RR->node->prng() & 1; // randomize which hint we send first for obscure NAT-t reasons
+											const unsigned int completed = alt + 2;
+											while (alt != completed) {
+												if ((alt & 1) == 0) {
+													Packet outp(source,RR->identity.address(),Packet::VERB_RENDEZVOUS);
+													outp.append((uint8_t)0);
+													destination.appendTo(outp);
+													outp.append((uint16_t)hintToSource->port());
+													if (hintToSource->ss_family == AF_INET6) {
+														outp.append((uint8_t)16);
+														outp.append(hintToSource->rawIpData(),16);
+													} else {
+														outp.append((uint8_t)4);
+														outp.append(hintToSource->rawIpData(),4);
+													}
+													send(outp,true);
+												} else {
+													Packet outp(destination,RR->identity.address(),Packet::VERB_RENDEZVOUS);
+													outp.append((uint8_t)0);
+													source.appendTo(outp);
+													outp.append((uint16_t)hintToDest->port());
+													if (hintToDest->ss_family == AF_INET6) {
+														outp.append((uint8_t)16);
+														outp.append(hintToDest->rawIpData(),16);
+													} else {
+														outp.append((uint8_t)4);
+														outp.append(hintToDest->rawIpData(),4);
+													}
+													send(outp,true);
+												}
+												++alt;
+											}
+										}
+									}
+								}
+
 							}
 						} else {
 #ifdef ZT_ENABLE_CLUSTER
@@ -824,96 +881,4 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 	return true;
 }
 
-bool Switch::_unite(const Address &p1,const Address &p2)
-{
-	if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
-		return false;
-
-	const uint64_t now = RR->node->now();
-	InetAddress *p1a = (InetAddress *)0;
-	InetAddress *p2a = (InetAddress *)0;
-	InetAddress p1v4,p1v6,p2v4,p2v6,uv4,uv6;
-	{
-		const SharedPtr<Peer> p1p(RR->topology->getPeer(p1));
-		const SharedPtr<Peer> p2p(RR->topology->getPeer(p2));
-		if ((!p1p)&&(!p2p)) return false;
-		if (p1p) p1p->getBestActiveAddresses(now,p1v4,p1v6);
-		if (p2p) p2p->getBestActiveAddresses(now,p2v4,p2v6);
-	}
-	if ((p1v6)&&(p2v6)) {
-		p1a = &p1v6;
-		p2a = &p2v6;
-	} else if ((p1v4)&&(p2v4)) {
-		p1a = &p1v4;
-		p2a = &p2v4;
-	} else {
-		SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer());
-		if (!upstream)
-			return false;
-		upstream->getBestActiveAddresses(now,uv4,uv6);
-		if ((p1v6)&&(uv6)) {
-			p1a = &p1v6;
-			p2a = &uv6;
-		} else if ((p1v4)&&(uv4)) {
-			p1a = &p1v4;
-			p2a = &uv4;
-		} else if ((p2v6)&&(uv6)) {
-			p1a = &p2v6;
-			p2a = &uv6;
-		} else if ((p2v4)&&(uv4)) {
-			p1a = &p2v4;
-			p2a = &uv4;
-		} else return false;
-	}
-
-	TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),p1a->toString().c_str(),p2.toString().c_str(),p2a->toString().c_str());
-
-	/* Tell P1 where to find P2 and vice versa, sending the packets to P1 and
-	 * P2 in randomized order in terms of which gets sent first. This is done
-	 * since in a few cases NAT-t can be sensitive to slight timing differences
-	 * in terms of when the two peers initiate. Normally this is accounted for
-	 * by the nearly-simultaneous RENDEZVOUS kickoff from the relay, but
-	 * given that relay are hosted on cloud providers this can in some
-	 * cases have a few ms of latency between packet departures. By randomizing
-	 * the order we make each attempted NAT-t favor one or the other going
-	 * first, meaning if it doesn't succeed the first time it might the second
-	 * and so forth. */
-	unsigned int alt = (unsigned int)RR->node->prng() & 1;
-	const unsigned int completed = alt + 2;
-	while (alt != completed) {
-		if ((alt & 1) == 0) {
-			// Tell p1 where to find p2.
-			Packet outp(p1,RR->identity.address(),Packet::VERB_RENDEZVOUS);
-			outp.append((unsigned char)0);
-			p2.appendTo(outp);
-			outp.append((uint16_t)p2a->port());
-			if (p2a->isV6()) {
-				outp.append((unsigned char)16);
-				outp.append(p2a->rawIpData(),16);
-			} else {
-				outp.append((unsigned char)4);
-				outp.append(p2a->rawIpData(),4);
-			}
-			send(outp,true);
-		} else {
-			// Tell p2 where to find p1.
-			Packet outp(p2,RR->identity.address(),Packet::VERB_RENDEZVOUS);
-			outp.append((unsigned char)0);
-			p1.appendTo(outp);
-			outp.append((uint16_t)p1a->port());
-			if (p1a->isV6()) {
-				outp.append((unsigned char)16);
-				outp.append(p1a->rawIpData(),16);
-			} else {
-				outp.append((unsigned char)4);
-				outp.append(p1a->rawIpData(),4);
-			}
-			send(outp,true);
-		}
-		++alt; // counts up and also flips LSB
-	}
-
-	return true;
-}
-
 } // namespace ZeroTier
diff --git a/node/Switch.hpp b/node/Switch.hpp
index 422f6c8ee..ce1c40b3d 100644
--- a/node/Switch.hpp
+++ b/node/Switch.hpp
@@ -55,7 +55,6 @@ class Switch : NonCopyable
 {
 public:
 	Switch(const RuntimeEnvironment *renv);
-	~Switch();
 
 	/**
 	 * Called when a packet is received from the real network
@@ -127,7 +126,6 @@ public:
 private:
 	Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
 	bool _trySend(Packet &packet,bool encrypt); // packet is modified if return is true
-	bool _unite(const Address &p1,const Address &p2);
 
 	const RuntimeEnvironment *const RR;
 	uint64_t _lastBeaconResponse;