Browse Source

Modify unite() to deal with a second layer of upstreams.

Adam Ierymenko 8 years ago
parent
commit
39333c9e8e
3 changed files with 94 additions and 101 deletions
  1. 0 20
      node/Peer.hpp
  2. 93 70
      node/Switch.cpp
  3. 1 11
      node/Switch.hpp

+ 0 - 20
node/Peer.hpp

@@ -403,26 +403,6 @@ public:
 		return false;
 	}
 
-	/**
-	 * Find a common set of addresses by which two peers can link, if any
-	 *
-	 * @param a Peer A
-	 * @param b Peer B
-	 * @param now Current time
-	 * @return Pair: B's address (to send to A), A's address (to send to B)
-	 */
-	static inline std::pair<InetAddress,InetAddress> findCommonGround(const Peer &a,const Peer &b,uint64_t now)
-	{
-		std::pair<InetAddress,InetAddress> v4,v6;
-		b.getBestActiveAddresses(now,v4.first,v6.first);
-		a.getBestActiveAddresses(now,v4.second,v6.second);
-		if ((v6.first)&&(v6.second)) // prefer IPv6 if both have it since NAT-t is (almost) unnecessary
-			return v6;
-		else if ((v4.first)&&(v4.second))
-			return v4;
-		else return std::pair<InetAddress,InetAddress>();
-	}
-
 private:
 	inline uint64_t _pathScore(const unsigned int p,const uint64_t now) const
 	{

+ 93 - 70
node/Switch.cpp

@@ -237,7 +237,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 							uint64_t &luts = _lastUniteAttempt[_LastUniteKey(source,destination)];
 							if ((now - luts) >= ZT_MIN_UNITE_INTERVAL) {
 								luts = now;
-								unite(source,destination);
+								_unite(source,destination);
 							}
 						} else {
 #ifdef ZT_ENABLE_CLUSTER
@@ -590,75 +590,6 @@ void Switch::send(const Packet &packet,bool encrypt)
 	}
 }
 
-bool Switch::unite(const Address &p1,const Address &p2)
-{
-	if ((p1 == RR->identity.address())||(p2 == RR->identity.address()))
-		return false;
-	SharedPtr<Peer> p1p = RR->topology->getPeer(p1);
-	if (!p1p)
-		return false;
-	SharedPtr<Peer> p2p = RR->topology->getPeer(p2);
-	if (!p2p)
-		return false;
-
-	const uint64_t now = RR->node->now();
-
-	std::pair<InetAddress,InetAddress> cg(Peer::findCommonGround(*p1p,*p2p,now));
-	if ((!(cg.first))||(cg.first.ipScope() != cg.second.ipScope()))
-		return false;
-
-	TRACE("unite: %s(%s) <> %s(%s)",p1.toString().c_str(),cg.second.toString().c_str(),p2.toString().c_str(),cg.first.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;
-	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)cg.first.port());
-			if (cg.first.isV6()) {
-				outp.append((unsigned char)16);
-				outp.append(cg.first.rawIpData(),16);
-			} else {
-				outp.append((unsigned char)4);
-				outp.append(cg.first.rawIpData(),4);
-			}
-			outp.armor(p1p->key(),true);
-			p1p->sendDirect(outp.data(),outp.size(),now,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)cg.second.port());
-			if (cg.second.isV6()) {
-				outp.append((unsigned char)16);
-				outp.append(cg.second.rawIpData(),16);
-			} else {
-				outp.append((unsigned char)4);
-				outp.append(cg.second.rawIpData(),4);
-			}
-			outp.armor(p2p->key(),true);
-			p2p->sendDirect(outp.data(),outp.size(),now,true);
-		}
-		++alt; // counts up and also flips LSB
-	}
-
-	return true;
-}
-
 void Switch::requestWhois(const Address &addr)
 {
 	bool inserted = false;
@@ -839,4 +770,96 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 	return false;
 }
 
+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

+ 1 - 11
node/Switch.hpp

@@ -97,17 +97,6 @@ public:
 	 */
 	void send(const Packet &packet,bool encrypt);
 
-	/**
-	 * Send RENDEZVOUS to two peers to permit them to directly connect
-	 *
-	 * This only works if both peers are known, with known working direct
-	 * links to this peer. The best link for each peer is sent to the other.
-	 *
-	 * @param p1 One of two peers (order doesn't matter)
-	 * @param p2 Second of pair
-	 */
-	bool unite(const Address &p1,const Address &p2);
-
 	/**
 	 * Request WHOIS on a given address
 	 *
@@ -138,6 +127,7 @@ public:
 private:
 	Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
 	bool _trySend(const Packet &packet,bool encrypt);
+	bool _unite(const Address &p1,const Address &p2);
 
 	const RuntimeEnvironment *const RR;
 	uint64_t _lastBeaconResponse;