From ef8706995786f26df7bcb9f69b2a332419841964 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 9 Sep 2016 09:32:00 -0700
Subject: [PATCH] Fix gating of multicast GATHER replies since these can come
 from upstream, etc., and fix an issue with sending ECHO to recheck marginal
 paths.

---
 node/IncomingPacket.cpp |  4 ++--
 node/Network.cpp        |  5 +++++
 node/Network.hpp        |  5 +++++
 node/NetworkConfig.hpp  | 13 +++++++++++++
 node/Switch.cpp         |  7 ++-----
 5 files changed, 27 insertions(+), 7 deletions(-)

diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index c83644152..1ce942c9a 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -443,7 +443,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 			case Packet::VERB_MULTICAST_GATHER: {
 				const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
 				SharedPtr<Network> network(RR->node->network(nwid));
-				if ((network)&&(network->gate(peer,verb(),packetId()))) {
+				if ((network)&&(network->gateMulticastGather(peer,verb(),packetId()))) {
 					const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
 					//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
 					const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
@@ -469,7 +469,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 							network->addCredential(com);
 					}
 
-					if (network->gate(peer,verb(),packetId())) {
+					if (network->gateMulticastGather(peer,verb(),packetId())) {
 						if ((flags & 0x02) != 0) {
 							// OK(MULTICAST_FRAME) includes implicit gather results
 							offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
diff --git a/node/Network.cpp b/node/Network.cpp
index 710e70ddf..a9b149420 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -1093,6 +1093,11 @@ bool Network::gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uin
 	return false;
 }
 
+bool Network::gateMulticastGather(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId)
+{
+	return ( (peer->address() == controller()) || RR->topology->isUpstream(peer->identity()) || gate(peer,verb,packetId) || _config.isAnchor(peer->address()) );
+}
+
 bool Network::recentlyAllowedOnNetwork(const SharedPtr<Peer> &peer) const
 {
 	Mutex::Lock _l(_lock);
diff --git a/node/Network.hpp b/node/Network.hpp
index e8d6e2a54..d80b13b9e 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -257,6 +257,11 @@ public:
 	 */
 	bool gate(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
 
+	/**
+	 * Check whether this peer is allowed to provide multicast info for this network
+	 */
+	bool gateMulticastGather(const SharedPtr<Peer> &peer,const Packet::Verb verb,const uint64_t packetId);
+
 	/**
 	 * @param peer Peer to check
 	 * @return True if peer has recently been a valid member of this network
diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp
index b5ab9ccb2..ad1cafa50 100644
--- a/node/NetworkConfig.hpp
+++ b/node/NetworkConfig.hpp
@@ -285,6 +285,19 @@ public:
 		return r;
 	}
 
+	/**
+	 * @param a Address to check
+	 * @return True if address is an anchor
+	 */
+	inline bool isAnchor(const Address &a) const
+	{
+		for(unsigned int i=0;i<specialistCount;++i) {
+			if ((a == specialists[i])&&((specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ANCHOR) != 0))
+				return true;
+		}
+		return false;
+	}
+
 	/**
 	 * @param fromPeer Peer attempting to bridge other Ethernet peers onto network
 	 * @return True if this network allows bridging
diff --git a/node/Switch.cpp b/node/Switch.cpp
index f2a0d35b7..ea92c99a9 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -759,11 +759,8 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 
 		SharedPtr<Path> viaPath(peer->getBestPath(now,false));
 		if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isRoot(peer->identity())) ) {
-			if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) >> 2,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
-				Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ECHO);
-				outp.armor(peer->key(),true);
-				viaPath->send(RR,outp.data(),outp.size(),now);
-			}
+			if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL))
+				peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now);
 			viaPath.zero();
 		}
 		if (!viaPath) {