Browse Source

Fix Cluster to send from a designated endpoint address instead of wildcard.

Adam Ierymenko 9 years ago
parent
commit
d8b89b0c86
2 changed files with 60 additions and 6 deletions
  1. 12 3
      node/Cluster.cpp
  2. 48 3
      osdep/Binder.hpp

+ 12 - 3
node/Cluster.cpp

@@ -570,10 +570,19 @@ void Cluster::sendViaCluster(const Address &fromPeerAddress,const Address &toPee
 		Mutex::Lock _l2(_members[mostRecentMemberId].lock);
 		if (buf.size() > 0)
 			_send(mostRecentMemberId,CLUSTER_MESSAGE_PROXY_UNITE,buf.data(),buf.size());
-		if (_members[mostRecentMemberId].zeroTierPhysicalEndpoints.size() > 0) {
-			TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
-			RR->node->putPacket(InetAddress(),_members[mostRecentMemberId].zeroTierPhysicalEndpoints.front(),data,len);
+
+		for(std::vector<InetAddress>::const_iterator i1(_zeroTierPhysicalEndpoints.begin());i1!=_zeroTierPhysicalEndpoints.end();++i1) {
+			for(std::vector<InetAddress>::const_iterator i2(_members[mostRecentMemberId].zeroTierPhysicalEndpoints.begin());i2!=_members[mostRecentMemberId].zeroTierPhysicalEndpoints.end();++i2) {
+				if (i1->ss_family == i2->ss_family) {
+					TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u (%s->%s)",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId,i1->toString().c_str(),i2->toString().c_str());
+					RR->node->putPacket(*i1,*i2,data,len);
+					return;
+				}
+			}
 		}
+
+		TRACE("sendViaCluster relaying %u bytes from %s to %s by way of %u failed: no common endpoints with the same address family!",len,fromPeerAddress.toString().c_str(),toPeerAddress.toString().c_str(),(unsigned int)mostRecentMemberId);
+		return;
 	}
 }
 

+ 48 - 3
osdep/Binder.hpp

@@ -146,7 +146,7 @@ binder_hpp_interface_prefixes_dont_match:
 						default: break;
 						case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
 						case InetAddress::IP_SCOPE_GLOBAL:
-						case InetAddress::IP_SCOPE_LINK_LOCAL:
+						//case InetAddress::IP_SCOPE_LINK_LOCAL:
 						case InetAddress::IP_SCOPE_SHARED:
 						case InetAddress::IP_SCOPE_PRIVATE:
 							for(ii=ignoreInterfacesByAddress.begin();ii!=ignoreInterfacesByAddress.end();++ii) {
@@ -170,13 +170,13 @@ binder_hpp_ignore_interface:
 
 #endif
 
+		// Default to binding to wildcard if we can't enumerate addresses
 		if (localIfAddrs.size() == 0) {
 			localIfAddrs.push_back(InetAddress((uint32_t)0,_port));
 			localIfAddrs.push_back(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,_port));
-		} else {
-			std::sort(localIfAddrs.begin(),localIfAddrs.end());
 		}
 
+		// Close any bindings to anything that doesn't exist anymore
 		for(bi=_bindings.begin();bi!=_bindings.end();++bi) {
 			if (std::find(localIfAddrs.begin(),localIfAddrs.end(),bi->address) == localIfAddrs.end()) {
 				_phy.close(bi->udpSock,false);
@@ -185,6 +185,7 @@ binder_hpp_ignore_interface:
 		}
 
 		for(ii=localIfAddrs.begin();ii!=localIfAddrs.end();++ii) {
+			// Copy over bindings that still are valid
 			for(bi=_bindings.begin();bi!=_bindings.end();++bi) {
 				if (bi->address == *ii) {
 					newBindings.push_back(*bi);
@@ -192,6 +193,7 @@ binder_hpp_ignore_interface:
 				}
 			}
 
+			// Add new bindings
 			if (bi == _bindings.end()) {
 				udps = _phy.udpBind(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0,131072);
 				if (udps) {
@@ -208,9 +210,52 @@ binder_hpp_ignore_interface:
 			}
 		}
 
+		// Swapping pointers and then letting the old one fall out of scope is faster than copying again
 		_bindings.swap(newBindings);
 	}
 
+	/**
+	 * Send a UDP packet from the specified local interface, or all
+	 *
+	 * Unfortunately even by examining the routing table there is no ultimately
+	 * robust way to tell where we might reach another host that works in all
+	 * environments. As a result, we send packets with null (wildcard) local
+	 * addresses from *every* bound interface.
+	 *
+	 * These are typically initial HELLOs, path probes, etc., since normal
+	 * conversations will have a local endpoint address. So the cost is low and
+	 * if the peer is not reachable via that route then the packet will go
+	 * nowhere and nothing will happen.
+	 *
+	 * It will of course only send via interface bindings of the same socket
+	 * family. No point in sending V4 via V6 or vice versa.
+	 *
+	 * In any case on most hosts there's only one or two interfaces that we
+	 * will use, so none of this is particularly costly.
+	 *
+	 * @param local Local interface address or null address for 'all'
+	 * @param remote Remote address
+	 * @param data Data to send
+	 * @param len Length of data
+	 */
+	inline bool udpSend(const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len) const
+	{
+		if (local) {
+			for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
+				if (i->address == local)
+					return _phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+			}
+			return false;
+		} else {
+			bool result = false;
+			for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
+				if (i->address.ss_family == remote.ss_family)
+					result |= _phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+			}
+			return result;
+		}
+	}
+
 private:
 	std::vector<_Binding> _bindings;
 	typename Phy<PHY_HANDLER_TYPE> &_phy;