From 97cbd98bc5bb99bd1363ba1ae87074afcef926a4 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Sat, 13 Jul 2013 14:28:26 -0400 Subject: [PATCH] Compile fixes, integration of fast PRNG. --- node/CMWC4096.hpp | 10 +++++ node/Demarc.cpp | 4 +- node/Http.cpp | 2 +- node/Multicaster.hpp | 5 +-- node/Node.cpp | 4 ++ node/PacketDecoder.cpp | 1 + node/Peer.cpp | 42 +++++++++++++----- node/Peer.hpp | 88 +++++++++++++++++++++++++++---------- node/RuntimeEnvironment.hpp | 7 ++- node/Switch.cpp | 54 +++++++++++++---------- node/Topology.cpp | 3 +- 11 files changed, 154 insertions(+), 66 deletions(-) diff --git a/node/CMWC4096.hpp b/node/CMWC4096.hpp index 31ef2ca44..293518615 100644 --- a/node/CMWC4096.hpp +++ b/node/CMWC4096.hpp @@ -31,6 +31,8 @@ #include #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 diff --git a/node/Demarc.cpp b/node/Demarc.cpp index cf521707e..5efe55bc6 100644 --- a/node/Demarc.cpp +++ b/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; diff --git a/node/Http.cpp b/node/Http.cpp index 07ada6dc1..86ae2d251 100644 --- a/node/Http.cpp +++ b/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::iterator a(addrList->begin()); while (i++ != k) ++a; addr = &(*a); diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index 42c92aab9..187054f0a 100644 --- a/node/Multicaster.hpp +++ b/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()) / 4294967296.0; - if (skipThis <= skipWhatFraction) { + if (prng.nextDouble() <= skipWhatFraction) { --numEntriesPermittedToSkip; ++channelMemberEntry; continue; diff --git a/node/Node.cpp b/node/Node.cpp index 265b77e4f..3b18d7ba7 100644 --- a/node/Node.cpp +++ b/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"); diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 9ee8d91f3..ba1a90019 100644 --- a/node/PacketDecoder.cpp +++ b/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 propPeers[ZT_MULTICAST_PROPAGATION_BREADTH]; unsigned int np = _r->multicaster->pickNextPropagationPeers( + *(_r->prng), *(_r->topology), network->id(), mg, diff --git a/node/Peer.cpp b/node/Peer.cpp index d5037eb90..488fde0cc 100644 --- a/node/Peer.cpp +++ b/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; diff --git a/node/Peer.hpp b/node/Peer.hpp index ef2d58519..4cc5e0f39 100644 --- a/node/Peer.hpp +++ b/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 &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 @@ -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(p); p += sizeof(uint64_t); + _lastMulticastFrame = b.template at(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(p); p += sizeof(uint64_t); lastReceive = b.template at(p); p += sizeof(uint64_t); - lastUnicastFrame = b.template at(p); p += sizeof(uint64_t); lastFirewallOpener = b.template at(p); p += sizeof(uint64_t); localPort = Demarc::intToPort(b.template at(p)); p += sizeof(uint64_t); latency = b.template at(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 &a,ZeroTier::SharedPtr &b) + { + a.swap(b); + } +} + #endif diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index b39a584f9..5f76c8ac0 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -31,7 +31,6 @@ #include #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; diff --git a/node/Switch.cpp b/node/Switch.cpp index c2a961aea..6a4e114a8 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -101,6 +101,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c Multicaster::MulticastBloomFilter bloom; SharedPtr 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 &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 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 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;fsend(_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; diff --git a/node/Topology.cpp b/node/Topology.cpp index a4ba1e04f..e627e7672 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -28,6 +28,7 @@ #include #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()