Browse Source

Compile fixes, integration of fast PRNG.

Adam Ierymenko 12 years ago
parent
commit
97cbd98bc5
11 changed files with 154 additions and 66 deletions
  1. 10 0
      node/CMWC4096.hpp
  2. 2 2
      node/Demarc.cpp
  3. 1 1
      node/Http.cpp
  4. 2 3
      node/Multicaster.hpp
  5. 4 0
      node/Node.cpp
  6. 1 0
      node/PacketDecoder.cpp
  7. 31 11
      node/Peer.cpp
  8. 66 22
      node/Peer.hpp
  9. 3 4
      node/RuntimeEnvironment.hpp
  10. 32 22
      node/Switch.cpp
  11. 2 1
      node/Topology.cpp

+ 10 - 0
node/CMWC4096.hpp

@@ -31,6 +31,8 @@
 #include <stdint.h>
 #include "Utils.hpp"
 
+namespace ZeroTier {
+
 /** 
  * Complement Multiply With Carry random number generator
  *
@@ -72,10 +74,18 @@ public:
 		return ((((uint64_t)next32()) << 32) ^ (uint64_t)next32());
 	}
 
+	inline double nextDouble()
+		throw()
+	{
+		return ((double)(next32()) / 4294967296.0);
+	}
+
 private:
 	uint32_t Q[4096];
 	uint32_t c;
 	uint32_t i;
 };
 
+} // namespace ZeroTier
+
 #endif

+ 2 - 2
node/Demarc.cpp

@@ -143,7 +143,7 @@ Demarc::Port Demarc::pick(const InetAddress &to) const
 			}
 		}
 		if (possibilities.size())
-			return possibilities[_r->prng.next32() % possibilities.size()]->first;
+			return possibilities[_r->prng->next32() % possibilities.size()]->first;
 		else return NULL_PORT;
 	} catch ( ... ) {
 		return NULL_PORT;
@@ -174,7 +174,7 @@ Demarc::Port Demarc::send(Demarc::Port fromPort,const InetAddress &to,const void
 				}
 			}
 			if (possibilities.size())
-				pe = possibilities[_r->prng.next32() % possibilities.size()];
+				pe = possibilities[_r->prng->next32() % possibilities.size()];
 			else {
 				_ports_m.unlock();
 				return NULL_PORT;

+ 1 - 1
node/Http.cpp

@@ -173,7 +173,7 @@ void Http::Request::main()
 				addrList->sort();
 				addrList->unique();
 				unsigned int i = 0,k = 0;
-				k = _r->prng.next32() % addrList->size();
+				k = rand() % addrList->size();
 				std::list<InetAddress>::iterator a(addrList->begin());
 				while (i++ != k) ++a;
 				addr = &(*a);

+ 2 - 3
node/Multicaster.hpp

@@ -234,7 +234,7 @@ public:
 		// network graph likely to be hops away from the original origin of the
 		// message.
 		for(unsigned int i=0;i<ZT_MULTICAST_BLOOM_FILTER_DECAY_RATE;++i)
-			bf.decay();
+			bf.decay((unsigned int)prng.next32());
 
 		{
 			Mutex::Lock _l(_multicastMemberships_m);
@@ -261,8 +261,7 @@ public:
 					// Skip some fraction of entries so that our sampling will be randomly distributed,
 					// since there is no other good way to sample randomly from a map.
 					if (numEntriesPermittedToSkip) {
-						double skipThis = (double)(Utils::randomInt<uint32_t>()) / 4294967296.0;
-						if (skipThis <= skipWhatFraction) {
+						if (prng.nextDouble() <= skipWhatFraction) {
 							--numEntriesPermittedToSkip;
 							++channelMemberEntry;
 							continue;

+ 4 - 0
node/Node.cpp

@@ -65,6 +65,7 @@
 #include "MulticastGroup.hpp"
 #include "Mutex.hpp"
 #include "Multicaster.hpp"
+#include "CMWC4096.hpp"
 
 #include "../version.h"
 
@@ -120,6 +121,7 @@ Node::~Node()
 	delete impl->renv.multicaster;
 	delete impl->renv.demarc;
 	delete impl->renv.nc;
+	delete impl->renv.prng;
 	delete impl->renv.log;
 
 	delete impl;
@@ -153,6 +155,8 @@ Node::ReasonForTermination Node::run()
 
 		TRACE("initializing...");
 
+		_r->prng = new CMWC4096();
+
 		if (!_r->configAuthority.fromString(_r->configAuthorityIdentityStr))
 			return impl->terminateBecause(Node::NODE_UNRECOVERABLE_ERROR,"configuration authority identity is not valid");
 

+ 1 - 0
node/PacketDecoder.cpp

@@ -472,6 +472,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 								Multicaster::MulticastBloomFilter bloom(field(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_BLOOM_FILTER,ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BYTES));
 								SharedPtr<Peer> propPeers[ZT_MULTICAST_PROPAGATION_BREADTH];
 								unsigned int np = _r->multicaster->pickNextPropagationPeers(
+									*(_r->prng),
 									*(_r->topology),
 									network->id(),
 									mg,

+ 31 - 11
node/Peer.cpp

@@ -30,6 +30,11 @@
 namespace ZeroTier {
 
 Peer::Peer() :
+	_id(),
+	_ipv4p(),
+	_ipv6p(),
+	_lastUnicastFrame(0),
+	_lastMulticastFrame(0),
 	_vMajor(0),
 	_vMinor(0),
 	_vRevision(0),
@@ -40,6 +45,10 @@ Peer::Peer() :
 Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
 	throw(std::runtime_error) :
 	_id(peerIdentity),
+	_ipv4p(),
+	_ipv6p(),
+	_lastUnicastFrame(0),
+	_lastMulticastFrame(0),
 	_vMajor(0),
 	_vMinor(0),
 	_vRevision(0),
@@ -49,29 +58,31 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
 		throw std::runtime_error("new peer identity key agreement failed");
 }
 
-void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &fromAddr,unsigned int hops,Packet::Verb verb,uint64_t now)
+void Peer::onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &remoteAddr,unsigned int hops,Packet::Verb verb,uint64_t now)
 {
 	if (!hops) { // direct packet
-		WanPath *wp = (fromAddr.isV4() ? &_ipv4p : &_ipv6p);
-
+		WanPath *wp = (remoteAddr.isV4() ? &_ipv4p : &_ipv6p);
 		wp->lastReceive = now;
-		if (verb == Packet::VERB_FRAME)
-			wp->lastUnicastFrame = now;
 		wp->localPort = localPort;
 		if (!wp->fixed)
-			wp->addr = fromAddr;
+			wp->addr = remoteAddr;
+		_dirty = true;
+	}
 
+	if (verb == Packet::VERB_FRAME) {
+		_lastUnicastFrame = now;
+		_dirty = true;
+	} else if (verb == Packet::VERB_MULTICAST_FRAME) {
+		_lastMulticastFrame = now;
 		_dirty = true;
 	}
 }
 
-bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,bool relay,Packet::Verb verb,uint64_t now)
+bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now)
 {
 	if ((_ipv6p.isActive(now))||((!(_ipv4p.addr))&&(_ipv6p.addr))) {
 		if (_r->demarc->send(_ipv6p.localPort,_ipv6p.addr,data,len,-1)) {
 			_ipv6p.lastSend = now;
-			if (verb == Packet::VERB_FRAME)
-				_ipv6p.lastUnicastFrame = now;
 			_dirty = true;
 			return true;
 		}
@@ -80,8 +91,6 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,b
 	if (_ipv4p.addr) {
 		if (_r->demarc->send(_ipv4p.localPort,_ipv4p.addr,data,len,-1)) {
 			_ipv4p.lastSend = now;
-			if (verb == Packet::VERB_FRAME)
-				_ipv4p.lastUnicastFrame = now;
 			_dirty = true;
 			return true;
 		}
@@ -90,6 +99,17 @@ bool Peer::send(const RuntimeEnvironment *_r,const void *data,unsigned int len,b
 	return false;
 }
 
+void Peer::onSent(const RuntimeEnvironment *_r,bool relay,Packet::Verb verb,uint64_t now)
+{
+	if (verb == Packet::VERB_FRAME) {
+		_lastUnicastFrame = now;
+		_dirty = true;
+	} else if (verb == Packet::VERB_MULTICAST_FRAME) {
+		_lastMulticastFrame = now;
+		_dirty = true;
+	}
+}
+
 bool Peer::sendFirewallOpener(const RuntimeEnvironment *_r,uint64_t now)
 {
 	bool sent = false;

+ 66 - 22
node/Peer.hpp

@@ -53,15 +53,16 @@
 #define ZT_PEER_MAX_SERIALIZED_LENGTH ( \
 	64 + \
 	IDENTITY_MAX_BINARY_SERIALIZED_LENGTH + \
-	(( \
-		(sizeof(uint64_t) * 5) + \
+	( ( \
+		(sizeof(uint64_t) * 4) + \
 		sizeof(uint16_t) + \
 		1 + \
 		sizeof(uint16_t) + \
 		16 + \
 		1 \
 	) * 2) + \
-	64 \
+	sizeof(uint64_t) + \
+	sizeof(uint64_t) \
 )
 
 namespace ZeroTier {
@@ -110,32 +111,42 @@ public:
 	/**
 	 * Must be called on authenticated packet receive from this peer
 	 * 
+	 * This must be called only after a packet has passed authentication
+	 * checking. Packets that fail are silently discarded.
+	 *
 	 * @param _r Runtime environment
 	 * @param localPort Local port on which packet was received
-	 * @param fromAddr Internet address of sender
+	 * @param remoteAddr Internet address of sender
 	 * @param hops ZeroTier (not IP) hops
 	 * @param verb Packet verb
 	 * @param now Current time
 	 */
-	void onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &fromAddr,unsigned int hops,Packet::Verb verb,uint64_t now);
+	void onReceive(const RuntimeEnvironment *_r,Demarc::Port localPort,const InetAddress &remoteAddr,unsigned int hops,Packet::Verb verb,uint64_t now);
 
 	/**
-	 * Send a UDP packet to this peer
-	 * 
-	 * If the active link is timed out (no receives for ping timeout ms), then
-	 * the active link number is incremented after send. This causes sends to
-	 * cycle through links if there is no clear active link. This also happens
-	 * if the send fails for some reason.
+	 * Send a packet to this peer
 	 * 
 	 * @param _r Runtime environment
 	 * @param data Data to send
 	 * @param len Length of packet
-	 * @param relay This is a relay on behalf of another peer (verb is ignored)
-	 * @param verb Packet verb (if not relay)
 	 * @param now Current time
 	 * @return True if packet appears to have been sent, false on local failure
 	 */
-	bool send(const RuntimeEnvironment *_r,const void *data,unsigned int len,bool relay,Packet::Verb verb,uint64_t now);
+	bool send(const RuntimeEnvironment *_r,const void *data,unsigned int len,uint64_t now);
+
+	/**
+	 * Must be called after a packet is successfully sent to this peer
+	 *
+	 * Note that 'relay' means we've sent a packet *from* this node to this
+	 * peer by relaying it, not that we have relayed a packet from somewhere
+	 * else to this peer. In the latter case this is not called.
+	 *
+	 * @param _r Runtime environment
+	 * @param relay If true, packet was sent indirectly via a relay
+	 * @param verb Packet verb
+	 * @param now Current time
+	 */
+	void onSent(const RuntimeEnvironment *_r,bool relay,Packet::Verb verb,uint64_t now);
 
 	/**
 	 * Send firewall opener to active link
@@ -194,7 +205,25 @@ public:
 	uint64_t lastUnicastFrame() const
 		throw()
 	{
-		return std::max(_ipv4p.lastUnicastFrame,_ipv6p.lastUnicastFrame);
+		return _lastUnicastFrame;
+	}
+
+	/**
+	 * @return Time of most recent multicast frame
+	 */
+	uint64_t lastMulticastFrame() const
+		throw()
+	{
+		return _lastMulticastFrame;
+	}
+
+	/**
+	 * @return Time of most recent frame of any kind (unicast or multicast)
+	 */
+	uint64_t lastFrame() const
+		throw()
+	{
+		return std::max(_lastUnicastFrame,_lastMulticastFrame);
 	}
 
 	/**
@@ -340,11 +369,13 @@ public:
 	inline void serialize(Buffer<C> &b)
 		throw(std::out_of_range)
 	{
-		b.append((unsigned char)1); // version
+		b.append((unsigned char)2); // version
 		b.append(_keys,sizeof(_keys));
 		_id.serialize(b,false);
 		_ipv4p.serialize(b);
 		_ipv6p.serialize(b);
+		b.append(_lastUnicastFrame);
+		b.append(_lastMulticastFrame);
 	}
 
 	template<unsigned int C>
@@ -353,14 +384,19 @@ public:
 	{
 		unsigned int p = startAt;
 
-		if (b[p++] != 1)
+		if (b[p++] != 2)
 			throw std::invalid_argument("Peer: deserialize(): version mismatch");
 
 		memcpy(_keys,b.field(p,sizeof(_keys)),sizeof(_keys)); p += sizeof(_keys);
 		p += _id.deserialize(b,p);
 		p += _ipv4p.deserialize(b,p);
 		p += _ipv6p.deserialize(b,p);
+		_lastUnicastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
+		_lastMulticastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 
+		_vMajor = 0;
+		_vMinor = 0;
+		_vRevision = 0;
 		_dirty = false;
 
 		return (p - startAt);
@@ -400,7 +436,6 @@ private:
 		WanPath() :
 			lastSend(0),
 			lastReceive(0),
-			lastUnicastFrame(0),
 			lastFirewallOpener(0),
 			localPort(Demarc::ANY_PORT),
 			latency(0),
@@ -421,7 +456,6 @@ private:
 		{
 			b.append(lastSend);
 			b.append(lastReceive);
-			b.append(lastUnicastFrame);
 			b.append(lastFirewallOpener);
 			b.append(Demarc::portToInt(localPort));
 			b.append((uint16_t)latency);
@@ -451,7 +485,6 @@ private:
 
 			lastSend = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 			lastReceive = b.template at<uint64_t>(p); p += sizeof(uint64_t);
-			lastUnicastFrame = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 			lastFirewallOpener = b.template at<uint64_t>(p); p += sizeof(uint64_t);
 			localPort = Demarc::intToPort(b.template at<uint64_t>(p)); p += sizeof(uint64_t);
 			latency = b.template at<uint16_t>(p); p += sizeof(uint16_t);
@@ -477,9 +510,8 @@ private:
 
 		uint64_t lastSend;
 		uint64_t lastReceive;
-		uint64_t lastUnicastFrame;
 		uint64_t lastFirewallOpener;
-		Demarc::Port localPort; // ANY_PORT if not defined
+		Demarc::Port localPort; // ANY_PORT if not defined (size: uint64_t)
 		unsigned int latency; // 0 if never determined
 		InetAddress addr; // null InetAddress if path is undefined
 		bool fixed; // do not learn address from received packets
@@ -491,6 +523,9 @@ private:
 	WanPath _ipv4p;
 	WanPath _ipv6p;
 
+	uint64_t _lastUnicastFrame;
+	uint64_t _lastMulticastFrame;
+
 	// Fields below this line are not persisted with serialize()
 
 	unsigned int _vMajor,_vMinor,_vRevision;
@@ -501,4 +536,13 @@ private:
 
 } // namespace ZeroTier
 
+// Add a swap() for shared ptr's to peers to speed up peer sorts
+namespace std {
+	template<>
+	inline void swap(ZeroTier::SharedPtr<ZeroTier::Peer> &a,ZeroTier::SharedPtr<ZeroTier::Peer> &b)
+	{
+		a.swap(b);
+	}
+}
+
 #endif

+ 3 - 4
node/RuntimeEnvironment.hpp

@@ -31,7 +31,6 @@
 #include <string>
 #include "Identity.hpp"
 #include "Condition.hpp"
-#include "CMWC4096.hpp"
 
 namespace ZeroTier {
 
@@ -42,6 +41,7 @@ class Switch;
 class Topology;
 class SysEnv;
 class Multicaster;
+class CMWC4096;
 
 /**
  * Holds global state for an instance of ZeroTier::Node
@@ -61,6 +61,7 @@ public:
 	RuntimeEnvironment() :
 		identity(),
 		log((Logger *)0),
+		prng((CMWC4096 *)0),
 		nc((NodeConfig *)0),
 		demarc((Demarc *)0),
 		multicaster((Multicaster *)0),
@@ -78,13 +79,11 @@ public:
 	// signal() to prematurely interrupt main loop wait
 	Condition mainLoopWaitCondition;
 
-	// non-cryptographic fast PRNG
-	CMWC4096 prng;
-
 	Identity configAuthority;
 	Identity identity;
 
 	Logger *log; // may be null
+	CMWC4096 *prng;
 	NodeConfig *nc;
 	Demarc *demarc;
 	Multicaster *multicaster;

+ 32 - 22
node/Switch.cpp

@@ -101,6 +101,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		Multicaster::MulticastBloomFilter bloom;
 		SharedPtr<Peer> propPeers[ZT_MULTICAST_PROPAGATION_BREADTH];
 		unsigned int np = _r->multicaster->pickNextPropagationPeers(
+			*(_r->prng),
 			*(_r->topology),
 			network->id(),
 			mg,
@@ -187,15 +188,20 @@ void Switch::sendHELLO(const Address &dest)
 
 bool Switch::sendHELLO(const SharedPtr<Peer> &dest,Demarc::Port localPort,const InetAddress &addr)
 {
+	uint64_t now = Utils::now();
 	Packet outp(dest->address(),_r->identity.address(),Packet::VERB_HELLO);
 	outp.append((unsigned char)ZT_PROTO_VERSION);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MAJOR);
 	outp.append((unsigned char)ZEROTIER_ONE_VERSION_MINOR);
 	outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
-	outp.append(Utils::now());
+	outp.append(now);
 	_r->identity.serialize(outp,false);
 	outp.hmacSet(dest->macKey());
-	return _r->demarc->send(localPort,addr,outp.data(),outp.size(),-1);
+	if (_r->demarc->send(localPort,addr,outp.data(),outp.size(),-1)) {
+		dest->onSent(_r,false,Packet::VERB_HELLO,now);
+		return true;
+	}
+	return false;
 }
 
 bool Switch::unite(const Address &p1,const Address &p2,bool force)
@@ -249,7 +255,8 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 		}
 		outp.encrypt(p1p->cryptKey());
 		outp.hmacSet(p1p->macKey());
-		p1p->send(_r,outp.data(),outp.size(),false,Packet::VERB_RENDEZVOUS,now);
+		if (p1p->send(_r,outp.data(),outp.size(),now))
+			p1p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
 	}
 	{	// tell p2 where to find p1
 		Packet outp(p2,_r->identity.address(),Packet::VERB_RENDEZVOUS);
@@ -264,7 +271,8 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 		}
 		outp.encrypt(p2p->cryptKey());
 		outp.hmacSet(p2p->macKey());
-		p2p->send(_r,outp.data(),outp.size(),false,Packet::VERB_RENDEZVOUS,now);
+		if (p2p->send(_r,outp.data(),outp.size(),now))
+			p2p->onSent(_r,false,Packet::VERB_RENDEZVOUS,now);
 	}
 
 	return true;
@@ -443,12 +451,11 @@ void Switch::_handleRemotePacketFragment(Demarc::Port localPort,const InetAddres
 		// Fragment is not for us, so try to relay it
 		if (fragment.hops() < ZT_RELAY_MAX_HOPS) {
 			fragment.incrementHops();
-
 			SharedPtr<Peer> relayTo = _r->topology->getPeer(destination);
-			if ((!relayTo)||(!relayTo->send(_r,fragment.data(),fragment.size(),true,Packet::VERB_NOP,Utils::now()))) {
+			if ((!relayTo)||(!relayTo->send(_r,fragment.data(),fragment.size(),Utils::now()))) {
 				relayTo = _r->topology->getBestSupernode();
 				if (relayTo)
-					relayTo->send(_r,fragment.data(),fragment.size(),true,Packet::VERB_NOP,Utils::now());
+					relayTo->send(_r,fragment.data(),fragment.size(),Utils::now());
 			}
 		} else {
 			TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
@@ -516,18 +523,19 @@ void Switch::_handleRemotePacketHead(Demarc::Port localPort,const InetAddress &f
 			packet->incrementHops();
 
 			SharedPtr<Peer> relayTo = _r->topology->getPeer(destination);
-			if ((relayTo)&&(relayTo->send(_r,packet->data(),packet->size(),true,Packet::VERB_NOP,Utils::now()))) {
-				unite(source,destination,false); // periodically try to get them to talk directly
+			if ((relayTo)&&(relayTo->send(_r,packet->data(),packet->size(),Utils::now()))) {
+				// If we've relayed, this periodically tries to get them to
+				// talk directly to save our bandwidth.
+				unite(source,destination,false);
 			} else {
-				// Relay via a supernode if there's no direct path, but pass
-				// source to getBestSupernode() to avoid just in case this is
-				// being passed from another supernode so that we don't just
-				// pass it back to where it came from. This can happen if a
-				// supernode for some reason lacks a direct path to a peer that
-				// it wants to talk to, such as because of Internet weather.
+				// If we've received a packet not for us and we don't have
+				// a direct path to its recipient, pass it to (another)
+				// supernode. This can happen due to Internet weather -- the
+				// most direct supernode may not be reachable, yet another
+				// further away may be.
 				relayTo = _r->topology->getBestSupernode(&source,1,true);
 				if (relayTo)
-					relayTo->send(_r,packet->data(),packet->size(),true,Packet::VERB_NOP,Utils::now());
+					relayTo->send(_r,packet->data(),packet->size(),Utils::now());
 			}
 		} else {
 			TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet->source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@@ -584,8 +592,11 @@ Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlread
 		outp.append(addr.data(),ZT_ADDRESS_LENGTH);
 		outp.encrypt(supernode->cryptKey());
 		outp.hmacSet(supernode->macKey());
-		supernode->send(_r,outp.data(),outp.size(),false,Packet::VERB_WHOIS,Utils::now());
-		return supernode->address();
+		uint64_t now = Utils::now();
+		if (supernode->send(_r,outp.data(),outp.size(),now)) {
+			supernode->onSent(_r,false,Packet::VERB_WHOIS,now);
+			return supernode->address();
+		}
 	}
 	return Address();
 }
@@ -618,8 +629,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 			tmp.encrypt(peer->cryptKey());
 		tmp.hmacSet(peer->macKey());
 
-		Packet::Verb verb = packet.verb();
-		if (via->send(_r,tmp.data(),chunkSize,isRelay,verb,now)) {
+		if (via->send(_r,tmp.data(),chunkSize,now)) {
 			if (chunkSize < tmp.size()) {
 				// Too big for one bite, fragment the rest
 				unsigned int fragStart = chunkSize;
@@ -632,15 +642,15 @@ bool Switch::_trySend(const Packet &packet,bool encrypt)
 				for(unsigned int f=0;f<fragsRemaining;++f) {
 					chunkSize = std::min(remaining,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
 					Packet::Fragment frag(tmp,fragStart,chunkSize,f + 1,totalFragments);
-					if (!via->send(_r,frag.data(),frag.size(),isRelay,verb,now)) {
+					if (!via->send(_r,frag.data(),frag.size(),now)) {
 						TRACE("WARNING: packet send to %s failed on later fragment #%u (check IP layer buffer sizes?)",via->address().toString().c_str(),f + 1);
-						return false;
 					}
 					fragStart += chunkSize;
 					remaining -= chunkSize;
 				}
 			}
 
+			via->onSent(_r,isRelay,packet.verb(),now);
 			return true;
 		}
 		return false;

+ 2 - 1
node/Topology.cpp

@@ -28,6 +28,7 @@
 #include <algorithm>
 #include "Topology.hpp"
 #include "NodeConfig.hpp"
+#include "CMWC4096.hpp"
 
 namespace ZeroTier {
 
@@ -201,7 +202,7 @@ skip_and_try_next_supernode:
 	if (bestSupernode)
 		return bestSupernode;
 
-	return _supernodePeers[_r->prng.next32() % _supernodePeers.size()];
+	return _supernodePeers[_r->prng->next32() % _supernodePeers.size()];
 }
 
 void Topology::clean()