Browse Source

Bunch more cleanup, improvements to NAT traversal logic, finished updating Switch.

Adam Ierymenko 10 years ago
parent
commit
6eb9289367
11 changed files with 162 additions and 142 deletions
  1. 4 14
      include/ZeroTierOne.h
  2. 1 1
      node/Constants.hpp
  3. 4 6
      node/IncomingPacket.hpp
  4. 2 2
      node/Node.cpp
  5. 2 2
      node/Node.hpp
  6. 0 1
      node/Packet.cpp
  7. 1 39
      node/Packet.hpp
  8. 4 4
      node/Path.hpp
  9. 20 1
      node/Peer.hpp
  10. 114 69
      node/Switch.cpp
  11. 10 3
      node/Switch.hpp

+ 4 - 14
include/ZeroTierOne.h

@@ -151,12 +151,7 @@ enum ZT1_NodeStatusCode
 	/**
 	 * Node is online -- at least one upstream is reachable
 	 */
-	ZT1_NODE_STATUS_ONLINE = 1,
-
-	/**
-	 * Link desperation level has changed
-	 */
-	ZT1_NODE_STATUS_DESPERATION_CHANGE = 3
+	ZT1_NODE_STATUS_ONLINE = 1
 };
 
 /**
@@ -191,7 +186,7 @@ typedef struct
 	/**
 	 * Current maximum link desperation metric
 	 */
-	int desperation;
+	unsigned int desperation;
 } ZT1_NodeStatus;
 
 /**
@@ -393,11 +388,6 @@ typedef struct
 	 */
 	uint64_t bytesReceived;
 
-	/**
-	 * This path's desperation metric (higher == worse)
-	 */
-	int desperation;
-
 	/**
 	 * Is path fixed? (i.e. not learned, static)
 	 */
@@ -548,7 +538,7 @@ typedef int (*ZT1_DataStorePutFunction)(ZT1_Node *,const char *,const void *,uns
  * on failure. Note that success does not (of course) guarantee packet
  * delivery. It only means that the packet appears to have been sent.
  */
-typedef int (*ZT1_WirePacketSendFunction)(ZT1_Node *,const struct sockaddr_storage *,int,const void *,unsigned int);
+typedef int (*ZT1_WirePacketSendFunction)(ZT1_Node *,const struct sockaddr_storage *,unsigned int,const void *,unsigned int);
 
 /**
  * Function to send a frame out to a virtual network port
@@ -602,7 +592,7 @@ enum ZT1_ResultCode ZT1_Node_processWirePacket(
 	ZT1_Node *node,
 	uint64_t now,
 	const struct sockaddr_storage *remoteAddress,
-	int linkDesperation,
+	unsigned int linkDesperation,
 	const void *packetData,
 	unsigned int packetLength,
 	uint64_t *nextCallDeadline);

+ 1 - 1
node/Constants.hpp

@@ -334,7 +334,7 @@
  * a RENDEZVOUS message no more than this often. This instructs the peers
  * to attempt NAT-t and gives each the other's corresponding IP:port pair.
  */
-#define ZT_MIN_UNITE_INTERVAL 30000
+#define ZT_MIN_UNITE_INTERVAL 60000
 
 /**
  * Delay between initial direct NAT-t packet and more aggressive techniques

+ 4 - 6
node/IncomingPacket.hpp

@@ -31,14 +31,12 @@
 #include <stdexcept>
 
 #include "Packet.hpp"
-#include "SocketManager.hpp"
 #include "InetAddress.hpp"
 #include "Utils.hpp"
 #include "SharedPtr.hpp"
 #include "AtomicCounter.hpp"
 #include "MulticastGroup.hpp"
 #include "Peer.hpp"
-#include "Socket.hpp"
 
 /*
  * The big picture:
@@ -73,17 +71,17 @@ public:
 	 * Create a new packet-in-decode
 	 *
 	 * @param b Source buffer with raw packet data
-	 * @param fromSock Socket on which packet was received
 	 * @param remoteAddress Address from which packet came
+	 * @param linkDesperation Link desperation for link over which packet was received
 	 * @throws std::out_of_range Range error processing packet
 	 */
 	template<unsigned int C2>
-	IncomingPacket(const Buffer<C2> &b,const SharedPtr<Socket> &fromSock,const InetAddress &remoteAddress)
+	IncomingPacket(const Buffer<C2> &b,const InetAddress &remoteAddress,unsigned int linkDesperation)
  		throw(std::out_of_range) :
  		Packet(b),
  		_receiveTime(Utils::now()),
- 		_fromSock(fromSock),
  		_remoteAddress(remoteAddress),
+ 		_linkDesperation(linkDesperation),
  		__refCount()
 	{
 	}
@@ -130,8 +128,8 @@ private:
 	void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);
 
 	uint64_t _receiveTime;
-	SharedPtr<Socket> _fromSock;
 	InetAddress _remoteAddress;
+	unsigned int _linkDesperation;
 	AtomicCounter __refCount;
 };
 

+ 2 - 2
node/Node.cpp

@@ -95,7 +95,7 @@ Node::~Node()
 ZT1_ResultCode Node::processWirePacket(
 	uint64_t now,
 	const struct sockaddr_storage *remoteAddress,
-	int linkDesperation,
+	unsigned int linkDesperation,
 	const void *packetData,
 	unsigned int packetLength,
 	uint64_t *nextCallDeadline)
@@ -207,7 +207,7 @@ enum ZT1_ResultCode ZT1_Node_processWirePacket(
 	ZT1_Node *node,
 	uint64_t now,
 	const struct sockaddr_storage *remoteAddress,
-	int linkDesperation,
+	unsigned int linkDesperation,
 	const void *packetData,
 	unsigned int packetLength,
 	uint64_t *nextCallDeadline)

+ 2 - 2
node/Node.hpp

@@ -71,7 +71,7 @@ public:
 	ZT1_ResultCode processWirePacket(
 		uint64_t now,
 		const struct sockaddr_storage *remoteAddress,
-		int linkDesperation,
+		unsigned int linkDesperation,
 		const void *packetData,
 		unsigned int packetLength,
 		uint64_t *nextCallDeadline);
@@ -113,7 +113,7 @@ public:
 	 * @param desperation Link desperation for reaching this address
 	 * @return True if packet appears to have been sent
 	 */
-	inline bool putPacket(const InetAddress &addr,const void *data,unsigned int len,int desperation)
+	inline bool putPacket(const InetAddress &addr,const void *data,unsigned int len,unsigned int desperation)
 	{
 		return (_wirePacketSendFunction(
 			reinterpret_cast<ZT1_Node *>(this),

+ 0 - 1
node/Packet.cpp

@@ -50,7 +50,6 @@ const char *Packet::verbString(Verb v)
 		case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
 		case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
 		case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
-		case VERB_PHYSICAL_ADDRESS_CHANGED: return "PHYSICAL_ADDRESS_CHANGED";
 	}
 	return "(unknown)";
 }

+ 1 - 39
node/Packet.hpp

@@ -717,45 +717,7 @@ public:
 		 *   <[6] multicast group MAC>
 		 *   <[4] 32-bit multicast group ADI>
 		 */
-		VERB_MULTICAST_FRAME = 14,
-
-		/* Message sent to notify of a change in underlying address:
-		 *   <[1] flags>
-		 *   <[1] address type>
-		 *   <[2] 16-bit length of address>
-		 *   <[...] new address>
-		 *
-		 * Flags:
-		 *   0x01 - Address was confirmed (if unset no confirmation was done)
-		 *
-		 * Address types:
-		 *   0x01 - IPv4/UDP
-		 *   0x02 - IPv6/UDP
-		 *   0x03 - IPv4/TCP
-		 *   0x04 - IPv6/TCP
-		 *   0x05 - Ethernet MAC (raw framing address)
-		 *   0x06 - Bluetooth MAC
-		 *   0x07 - HTTP URL
-		 *   (other values are reserved)
-		 *
-		 * Address formats:
-		 *   IPv4: 32-bit address, 16-bit port in network byte order
-		 *   IPv6: 128-bit address, 16-bit port in network byte order
-		 *   Ethernet: 48-bit / 6-byte MAC
-		 *   Bluetooth: 48-bit / 6-byte Bluetooth MAC
-		 *   HTTP URL: ASCII string containing endpoint URL
-		 *
-		 * This should be sent by peers when a new remote address is detected by
-		 * way of a new packet from a previously unknown underlying physical
-		 * address. It may also be sent with flag 0x01 set once a new address is
-		 * confirmed via a bi-directional transaction, indicating a higher
-		 * confidence level.
-		 *
-		 * Peers can use this message to trigger reconnection semantics when
-		 * mobility, DHCP reassignment, VM migration, or other factors lead to
-		 * a change in physical address.
-		 */
-		VERB_PHYSICAL_ADDRESS_CHANGED = 15
+		VERB_MULTICAST_FRAME = 14
 	};
 
 	/**

+ 4 - 4
node/Path.hpp

@@ -106,7 +106,7 @@ public:
 	 * @param t Time of receive
 	 * @param d Link desperation of receive
 	 */
-	inline void received(uint64_t t,int d) throw() { _lastReceived = t; _lastReceiveDesperation = d; }
+	inline void received(uint64_t t,unsigned int d) throw() { _lastReceived = t; _lastReceiveDesperation = d; }
 
 	/**
 	 * @return Is this a fixed path?
@@ -131,10 +131,10 @@ public:
 	 * @param now Current time
 	 * @return Path desperation, starting at 0
 	 */
-	inline int desperation(uint64_t now) const
+	inline unsigned int desperation(uint64_t now) const
 	{
 		if ((_fixed)&&(_lastSend > _lastReceived))
-			return std::max(_lastReceiveDesperation,(int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT));
+			return std::max(_lastReceiveDesperation,(unsigned int)((_lastSend - _lastReceived) / ZT_DESPERATION_INCREMENT));
 		return _lastReceiveDesperation;
 	}
 
@@ -194,7 +194,7 @@ private:
 	uint64_t _lastSend;
 	uint64_t _lastReceived;
 	InetAddress _addr;
-	int _lastReceiveDesperation;
+	unsigned int _lastReceiveDesperation;
 	bool _fixed;
 };
 

+ 20 - 1
node/Peer.hpp

@@ -137,7 +137,7 @@ public:
 	 * @param now Current time
 	 * @return Best path or NULL if there are no active (or fixed) direct paths
 	 */
-	Path *getBestPath(uint64_t now)
+	inline Path *getBestPath(uint64_t now)
 	{
 		Path *bestPath = (Path *)0;
 		uint64_t lrMax = 0;
@@ -150,6 +150,25 @@ public:
 		return bestPath;
 	}
 
+	/**
+	 * Send via best path
+	 *
+	 * @param RR Runtime environment
+	 * @param data Packet data
+	 * @param len Packet length
+	 * @param now Current time
+	 * @return Path used on success or NULL on failure
+	 */
+	inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+	{
+		Path *bestPath = getBestPath(now);
+		if (bestPath) {
+			if (bestPath->send(RR,data,len,now))
+				return bestPath;
+		}
+		return (Path *)0;
+	}
+
 	/**
 	 * @return All known direct paths to this peer
 	 */

+ 114 - 69
node/Switch.cpp

@@ -36,16 +36,15 @@
 #include "../include/ZeroTierOne.h"
 
 #include "Constants.hpp"
+#include "RuntimeEnvironment.hpp"
 #include "Switch.hpp"
 #include "Node.hpp"
-#include "EthernetTap.hpp"
 #include "InetAddress.hpp"
 #include "Topology.hpp"
-#include "RuntimeEnvironment.hpp"
 #include "Peer.hpp"
-#include "NodeConfig.hpp"
 #include "CMWC4096.hpp"
 #include "AntiRecursion.hpp"
+#include "Packet.hpp"
 
 namespace ZeroTier {
 
@@ -93,13 +92,13 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	 * still happen because Windows likes to send broadcasts over interfaces that have little
 	 * to do with their intended target audience. :P */
 	if (!RR->antiRec->checkEthernetFrame(data.data(),data.size())) {
-		TRACE("%s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->tapDeviceName().c_str(),etherTypeName(etherType),data.size());
+		TRACE("%.16llx: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u)",network->id(),etherTypeName(etherType),data.size());
 		return;
 	}
 
 	// Check to make sure this protocol is allowed on this network
 	if (!nconf->permitsEtherType(etherType)) {
-		TRACE("%s: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
+		TRACE("%.16llx: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),(unsigned long long)network->id());
 		return;
 	}
 
@@ -107,7 +106,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	bool fromBridged = false;
 	if (from != network->mac()) {
 		if (!network->permitsBridging(RR->identity.address())) {
-			LOG("%s: %s -> %s %s not forwarded, bridging disabled on %.16llx or this peer not a bridge",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
+			LOG("%.16llx: %s -> %s %s not forwarded, bridging disabled or this peer not a bridge",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 			return;
 		}
 		fromBridged = true;
@@ -126,7 +125,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 				mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(data.field(24,4),4,0));
 			} else if (!nconf->enableBroadcast()) {
 				// Don't transmit broadcasts if this network doesn't want them
-				TRACE("%s: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled on network %.16llx",network->tapDeviceName().c_str(),network->id());
+				TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
 				return;
 			}
 		}
@@ -140,11 +139,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 
 		// Check multicast/broadcast bandwidth quotas and reject if quota exceeded
 		if (!network->updateAndCheckMulticastBalance(mg,data.size())) {
-			TRACE("%s: didn't multicast %d bytes, quota exceeded for multicast group %s",network->tapDeviceName().c_str(),(int)data.size(),mg.toString().c_str());
+			TRACE("%.16llx: didn't multicast %d bytes, quota exceeded for multicast group %s",network->id(),(int)data.size(),mg.toString().c_str());
 			return;
 		}
 
-		TRACE("%s: MULTICAST %s -> %s %s %d",network->tapDeviceName().c_str(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size());
+		TRACE("%.16llx: MULTICAST %s -> %s %s %d",network->id(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size());
 
 		RR->mc->send(
 			((!nconf->isPublic())&&(nconf->com())) ? &(nconf->com()) : (const CertificateOfMembership *)0,
@@ -195,7 +194,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 				send(outp,true);
 			}
 		} else {
-			TRACE("%s: UNICAST: %s -> %s %s dropped, destination not a member of closed network %.16llx",network->tapDeviceName().c_str(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),network->id());
+			TRACE("%.16llx: UNICAST: %s -> %s %s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 		}
 
 		return;
@@ -368,26 +367,32 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 	return true;
 }
 
-void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr)
+void Switch::contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr,unsigned int maxDesperation)
 {
-	// Send simple packet directly to indicated address -- works for most NATs
-	sendHELLO(peer,atAddr);
 	TRACE("sending NAT-t message to %s(%s)",peer->address().toString().c_str(),atAddr.toString().c_str());
 
+	uint64_t now = RR->node->now();
+
+	Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
+	outp.armor(peer->key(),false);
+
+	/* Note that we don't log this as a "sent" packet or send it via the peer's
+	 * normal send() path. That's because this is a trial packet to an
+	 * unconfirmed address.
+	 *
+	 * First attempt is always at desperation zero. Then we escalate to max
+	 * before escalating through other NAT-t strategies. */
+	RR->node->putPacket(atAddr,outp.data(),outp.size(),0);
+
 	// 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_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr));
+		_contactQueue.push_back(ContactQueueEntry(peer,now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY,atAddr,maxDesperation));
 	}
-
-	// Kick main loop out of wait so that it can pick up this
-	// change to our scheduled timer tasks.
-	RR->sm->whack();
 }
 
 void Switch::requestWhois(const Address &addr)
 {
-	//TRACE("requesting WHOIS for %s",addr.toString().c_str());
 	bool inserted = false;
 	{
 		Mutex::Lock _l(_outstandingWhoisRequests_m);
@@ -436,38 +441,84 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
 unsigned long Switch::doTimerTasks()
 {
 	unsigned long nextDelay = ~((unsigned long)0); // big number, caller will cap return value
-	uint64_t now = Utils::now();
+	const uint64_t now = RR->node->now();
 
-	{
+	{ // Aggressive NAT traversal time!
 		Mutex::Lock _l(_contactQueue_m);
 		for(std::list<ContactQueueEntry>::iterator qi(_contactQueue.begin());qi!=_contactQueue.end();) {
 			if (now >= qi->fireAtTime) {
-				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<6;++k) {
-						if ((p > 0)&&(p <= 0xffff)) {
-							qi->inaddr.setPort((unsigned int)p);
-							sendHELLO(qi->peer,qi->inaddr);
-						}
-						++p;
+				if (qi->peer->hasActiveDirectPath(now)) {
+					// We've successfully NAT-t'd, so cancel attempt
+					_contactQueue.erase(qi++);
+					continue;
+				} else {
+					// Nope, nothing yet. Time to kill some kittens.
+
+					Packet outp(qi->peer->address(),RR->identity.address(),Packet::VERB_NOP);
+					outp.armor(qi->peer->key(),false);
+
+					switch(qi->strategyIteration) {
+						case 0:
+							// First strategy: rifle method: direct packet to known port
+							++qi->strategyIteration;
+							RR->node->putPacket(qi->inaddr,outp.data(),outp.size(),qi->currentDesperation);
+							break;
+						case 1: {
+							// Second strategy: shotgun method up: try a few ports above
+							++qi->strategyIteration;
+							int p = (int)qi->inaddr.port();
+							for(int i=0;i<6;++i) {
+								if (++p > 0xffff)
+									break;
+								InetAddress tmpaddr(qi->inaddr);
+								tmpaddr.setPort((unsigned int)p);
+								RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
+							}
+						}	break;
+						case 2: {
+							// Third strategy: shotgun method down: try a few ports below
+							++qi->strategyIteration;
+							int p = (int)qi->inaddr.port();
+							for(int i=0;i<3;++i) {
+								if (--p < 1024)
+									break;
+								InetAddress tmpaddr(qi->inaddr);
+								tmpaddr.setPort((unsigned int)p);
+								RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
+							}
+						}	break;
+						case 3:
+							// Fourth strategy: sawed-off shotgun: try random non-privileged ports
+							for(int i=0;i<16;++i) {
+								InetAddress tmpaddr(qi->inaddr);
+								tmpaddr.setPort((unsigned int)(1024 + (RR->prng->next32() % (65536 - 1024))));
+								RR->node->putPacket(tmpaddr,outp.data(),outp.size(),qi->currentDesperation);
+							}
+
+							// Escalate link desperation after all strategies attempted
+							++qi->currentDesperation;
+							if (qi->currentDesperation > qi->maxDesperation) {
+								// We've tried all strategies at all levels of desperation, give up.
+								_contactQueue.erase(qi++);
+								continue;
+							} else {
+								// Otherwise restart at new link desperation level (e.g. try a tougher transport)
+								qi->strategyIteration = 0;
+							}
+							break;
 					}
-				}
 
-				_contactQueue.erase(qi++);
+					qi->fireAtTime = now + ZT_NAT_T_TACTICAL_ESCALATION_DELAY;
+					nextDelay = std::min(nextDelay,(unsigned long)ZT_NAT_T_TACTICAL_ESCALATION_DELAY);
+				}
 			} else {
 				nextDelay = std::min(nextDelay,(unsigned long)(qi->fireAtTime - now));
-				++qi;
 			}
+			++qi; // if qi was erased, loop will have continued before here
 		}
 	}
 
-	{
+	{	// Retry outstanding WHOIS requests
 		Mutex::Lock _l(_outstandingWhoisRequests_m);
 		for(std::map< Address,WhoisRequest >::iterator i(_outstandingWhoisRequests.begin());i!=_outstandingWhoisRequests.end();) {
 			unsigned long since = (unsigned long)(now - i->second.lastSent);
@@ -483,12 +534,14 @@ unsigned long Switch::doTimerTasks()
 					TRACE("WHOIS %s (retry %u)",i->first.toString().c_str(),i->second.retries);
 					nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
 				}
-			} else nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
+			} else {
+				nextDelay = std::min(nextDelay,ZT_WHOIS_RETRY_DELAY - since);
+			}
 			++i;
 		}
 	}
 
-	{
+	{	// Time out TX queue packets that never got WHOIS lookups or other info.
 		Mutex::Lock _l(_txQueue_m);
 		for(std::multimap< Address,TXQueueEntry >::iterator i(_txQueue.begin());i!=_txQueue.end();) {
 			if (_trySend(i->second.packet,i->second.encrypt))
@@ -500,7 +553,7 @@ unsigned long Switch::doTimerTasks()
 		}
 	}
 
-	{
+	{	// Time out RX queue packets that never got WHOIS lookups or other info.
 		Mutex::Lock _l(_rxQueue_m);
 		for(std::list< SharedPtr<IncomingPacket> >::iterator i(_rxQueue.begin());i!=_rxQueue.end();) {
 			if ((now - (*i)->receiveTime()) > ZT_RECEIVE_QUEUE_TIMEOUT) {
@@ -510,7 +563,7 @@ unsigned long Switch::doTimerTasks()
 		}
 	}
 
-	{
+	{	// Time out packets that didn't get all their fragments.
 		Mutex::Lock _l(_defragQueue_m);
 		for(std::map< uint64_t,DefragQueueEntry >::iterator i(_defragQueue.begin());i!=_defragQueue.end();) {
 			if ((now - i->second.creationTime) > ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT) {
@@ -552,11 +605,11 @@ void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,int linkDes
 			// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
 			// It wouldn't hurt anything, just redundant and unnecessary.
 			SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
-			if ((!relayTo)||(relayTo->send(RR,fragment.data(),fragment.size(),Utils::now()) == Path::PATH_TYPE_NULL)) {
+			if ((!relayTo)||(!relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now()))) {
 				// Don't know peer or no direct path -- so relay via supernode
 				relayTo = RR->topology->getBestSupernode();
 				if (relayTo)
-					relayTo->send(RR,fragment.data(),fragment.size(),Utils::now());
+					relayTo->send(RR,fragment.data(),fragment.size(),RR->node->now());
 			}
 		} else {
 			TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
@@ -613,7 +666,7 @@ void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,int linkDes
 
 void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDesperation,const Buffer<4096> &data)
 {
-	SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromSock,fromAddr));
+	SharedPtr<IncomingPacket> packet(new IncomingPacket(data,fromAddr,linkDesperation));
 
 	Address source(packet->source());
 	Address destination(packet->destination());
@@ -626,18 +679,13 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,int linkDespera
 			packet->incrementHops();
 
 			SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
-			Path::Type relayedVia;
-			if ((relayTo)&&((relayedVia = relayTo->send(RR,packet->data(),packet->size(),Utils::now())) != Path::PATH_TYPE_NULL)) {
-				/* If both paths are UDP, attempt to invoke UDP NAT-t between peers
-				 * by sending VERB_RENDEZVOUS. Do not do this for TCP due to GitHub
-				 * issue #63. */
-				if ((fromSock->udp())&&(relayedVia == Path::PATH_TYPE_UDP))
-					unite(source,destination,false);
+			if ((relayTo)&&((relayTo->send(RR,packet->data(),packet->size(),RR->node->now())))) {
+				unite(source,destination,false);
 			} else {
 				// Don't know peer or no direct path -- so relay via supernode
 				relayTo = RR->topology->getBestSupernode(&source,1,true);
 				if (relayTo)
-					relayTo->send(RR,packet->data(),packet->size(),Utils::now());
+					relayTo->send(RR,packet->data(),packet->size(),RR->node->now());
 			}
 		} else {
 			TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@@ -693,15 +741,12 @@ void Switch::_handleBeacon(const InetAddress &fromAddr,int linkDesperation,const
 		return;
 	SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
 	if (peer) {
-		uint64_t now = Utils::now();
-		if (peer->haveUdpPath(fromAddr)) {
-			if ((now - peer->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)
-				peer->sendPing(RR,now);
-		} else {
-			if ((now - _lastBeacon) < ZT_MIN_BEACON_RESPONSE_INTERVAL)
-				return;
+		const uint64_t now = RR->node->now();
+		if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) {
 			_lastBeacon = now;
-			sendHELLO(peer,fromAddr);
+			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
+			outp.armor(peer->key(),false);
+			RR->node->putPacket(fromAddr,outp.data(),outp.size(),linkDesperation);
 		}
 	}
 }
@@ -713,8 +758,7 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
 		Packet outp(supernode->address(),RR->identity.address(),Packet::VERB_WHOIS);
 		addr.appendTo(outp);
 		outp.armor(supernode->key(),true);
-		uint64_t now = Utils::now();
-		if (supernode->send(RR,outp.data(),outp.size(),now) != Path::PATH_TYPE_NULL)
+		if (supernode->send(RR,outp.data(),outp.size(),RR->node->now()))
 			return supernode->address();
 	}
 	return Address();
@@ -725,14 +769,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 	SharedPtr<Peer> peer(RR->topology->getPeer(packet.destination()));
 
 	if (peer) {
-		uint64_t now = Utils::now();
+		const uint64_t now = RR->node->now();
 
 		SharedPtr<Peer> via;
-		if (peer->hasActiveDirectPath(now)) {
+		Path *viaPath;
+		if ((viaPath = peer->getBestPath(now))) {
 			via = peer;
 		} else {
 			via = RR->topology->getBestSupernode();
-			if (!via)
+			if (!(via)||(!(viaPath = via->getBestPath(now))))
 				return false;
 		}
 
@@ -743,7 +788,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 
 		tmp.armor(peer->key(),encrypt);
 
-		if (via->send(RR,tmp.data(),chunkSize,now) != Path::PATH_TYPE_NULL) {
+		if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
 			if (chunkSize < tmp.size()) {
 				// Too big for one bite, fragment the rest
 				unsigned int fragStart = chunkSize;
@@ -756,7 +801,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 				for(unsigned int fno=1;fno<totalFragments;++fno) {
 					chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
 					Packet::Fragment frag(tmp,fragStart,chunkSize,fno,totalFragments);
-					via->send(RR,frag.data(),frag.size(),now);
+					viaPath->send(RR,frag.data(),frag.size(),now);
 					fragStart += chunkSize;
 					remaining -= chunkSize;
 				}

+ 10 - 3
node/Switch.hpp

@@ -134,8 +134,9 @@ public:
 	 *
 	 * @param peer Peer to contact
 	 * @param atAddr Address of peer
+	 * @param linkDesperation Attempt up to given max desperation
 	 */
-	void contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr);
+	void contact(const SharedPtr<Peer> &peer,const InetAddress &atAddr,unsigned int maxDesperation);
 
 	/**
 	 * Request WHOIS on a given address
@@ -241,14 +242,20 @@ private:
 	struct ContactQueueEntry
 	{
 		ContactQueueEntry() {}
-		ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &a) :
+		ContactQueueEntry(const SharedPtr<Peer> &p,uint64_t ft,const InetAddress &a,unsigned int md) :
 			peer(p),
 			fireAtTime(ft),
-			inaddr(a) {}
+			inaddr(a),
+			maxDesperation(md),
+			currentDesperation(0),
+			strategyIteration(1) {} // start with 2nd strategy, zero desperation, since we've already tried 0/0
 
 		SharedPtr<Peer> peer;
 		uint64_t fireAtTime;
 		InetAddress inaddr;
+		unsigned int maxDesperation;
+		unsigned int currentDesperation;
+		unsigned int strategyIteration;
 	};
 	std::list<ContactQueueEntry> _contactQueue;
 	Mutex _contactQueue_m;