From 93bb934d4e77fd1436ebe81336d396fa769aa59b Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 14:08:13 -0700 Subject: Some cleanup, docs, and Path -> Path > RemotePath refactor. --- node/Node.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Node.cpp b/node/Node.cpp index 8cdc6d62..8eacf753 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -388,10 +388,10 @@ ZT1_PeerList *Node::peers() const p->latency = pi->second->latency(); p->role = RR->topology->isRoot(pi->second->identity()) ? ZT1_PEER_ROLE_ROOT : ZT1_PEER_ROLE_LEAF; - std::vector paths(pi->second->paths()); - Path *bestPath = pi->second->getBestPath(_now); + std::vector paths(pi->second->paths()); + RemotePath *bestPath = pi->second->getBestPath(_now); p->pathCount = 0; - for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { + for(std::vector::iterator path(paths.begin());path!=paths.end();++path) { memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage)); p->paths[p->pathCount].lastSend = path->lastSend(); p->paths[p->pathCount].lastReceive = path->lastReceived(); -- cgit v1.2.3 From 79e9a8bcc26491a43577082ec7edb86020f7ec00 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:28:48 -0700 Subject: Almost everything for GitHub issue #180 except direct path map setup. --- node/Constants.hpp | 10 ---------- node/IncomingPacket.cpp | 30 ++++++++++++++++++++++++++++++ node/Node.cpp | 16 +--------------- node/Node.hpp | 1 - node/Packet.hpp | 21 +-------------------- node/Peer.cpp | 6 +++++- node/Switch.cpp | 24 ++---------------------- node/Switch.hpp | 2 -- 8 files changed, 39 insertions(+), 71 deletions(-) (limited to 'node/Node.cpp') diff --git a/node/Constants.hpp b/node/Constants.hpp index 1f0d8426..d15fef13 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -304,16 +304,6 @@ */ #define ZT_ANTIRECURSION_HISTORY_SIZE 16 -/** - * How often to send LAN beacons - */ -#define ZT_BEACON_INTERVAL 30000 - -/** - * Do not respond to any beacon more often than this - */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500 - /** * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7634f54f..87669ba7 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -885,6 +885,36 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr &peer) { + try { + unsigned int count = at(ZT_PACKET_IDX_PAYLOAD); + unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; + + while (count) { // if ptr overflows Buffer will throw + unsigned int flags = (*this)[ptr++]; + /*int metric = (*this)[ptr++];*/ ++ptr; + unsigned int extLen = at(ptr); ptr += 2; + ptr += extLen; // unused right now + unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; + switch(addrType) { + case 4: { + InetAddress a(field(ptr,4),4,at(ptr + 4)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + case 6: { + InetAddress a(field(ptr,16),16,at(ptr + 16)); + if ((flags & (0x01 | 0x02)) == 0) + peer->attemptToContactAt(RR,a,RR->node->now()); + } break; + } + ptr += addrLen; + } + } catch (std::exception &exc) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what()); + } catch ( ... ) { + TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str()); + } return true; } diff --git a/node/Node.cpp b/node/Node.cpp index 8eacf753..7c70db97 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -78,8 +78,7 @@ Node::Node( _networks_m(), _now(now), _lastPingCheck(0), - _lastHousekeepingRun(0), - _lastBeacon(0) + _lastHousekeepingRun(0) { _newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR; _newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR; @@ -269,19 +268,6 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next _online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT); if (oldOnline != _online) postEvent(_online ? ZT1_EVENT_ONLINE : ZT1_EVENT_OFFLINE); - - // Send LAN beacons - if ((now - _lastBeacon) >= ZT_BEACON_INTERVAL) { - _lastBeacon = now; - char beacon[13]; - void *p = beacon; - *(reinterpret_cast(p)) = RR->prng->next32(); - p = beacon + 4; - *(reinterpret_cast(p)) = RR->prng->next32(); - RR->identity.address().copyTo(beacon + 8,5); - RR->antiRec->logOutgoingZT(beacon,13); - putPacket(ZT_DEFAULTS.v4Broadcast,beacon,13); - } } catch ( ... ) { return ZT1_RESULT_FATAL_ERROR_INTERNAL; } diff --git a/node/Node.hpp b/node/Node.hpp index 5c7cfae2..cd8d68fc 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -251,7 +251,6 @@ private: uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; - uint64_t _lastBeacon; unsigned int _newestVersionSeen[3]; // major, minor, revision bool _online; }; diff --git a/node/Packet.hpp b/node/Packet.hpp index 9b86709a..50471b06 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -233,16 +233,6 @@ */ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD -/** - * DEPRECATED: length of LAN beacon packets - */ -#define ZT_PROTO_BEACON_LENGTH 13 - -/** - * DEPRECATED: index of address in a LAN beacon - */ -#define ZT_PROTO_BEACON_IDX_ADDRESS 8 - // Destination address types from HELLO, OK(HELLO), and other message types #define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0 #define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused @@ -388,16 +378,6 @@ namespace ZeroTier { * * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever * sent in the clear, as it's the "here is my public key" message. - * - * Beacon format and beacon packets: - * <[8] 8 random bytes> - * <[5] sender ZT address> - * - * A beacon is a 13-byte packet containing only the address of the sender. - * Receiving peers may or may not respond to beacons with a HELLO or other - * message to initiate direct communication. - * - * Beacons may be used for direct LAN announcement or NAT traversal. */ class Packet : public Buffer { @@ -901,6 +881,7 @@ public: * <[2] length of extended path characteristics or 0 for none> * <[...] extended path characteristics> * <[1] address type> + * <[1] address length in bytes> * <[...] address> * * Path record flags: diff --git a/node/Peer.cpp b/node/Peer.cpp index ec04357a..225fbdef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -210,9 +210,12 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector &dps,uint64_t now,bool force) { - if (((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force)) { + if ((!dps.empty())&&(((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { _lastDirectPathPush = now; + TRACE("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + printf("pushing %u direct paths to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + std::vector::const_iterator p(dps.begin()); while (p != dps.end()) { Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); @@ -254,6 +257,7 @@ void Peer::pushDirectPaths(const RuntimeEnvironment *RR,const std::vector outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); outp.append((uint16_t)0); outp.append(addressType); + outp.append((addressType == 4) ? 6 : 18); outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); outp.append((uint16_t)p->address().port()); diff --git a/node/Switch.cpp b/node/Switch.cpp index 7600624f..6a0b07cd 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -49,8 +49,7 @@ namespace ZeroTier { Switch::Switch(const RuntimeEnvironment *renv) : - RR(renv), - _lastBeacon(0) + RR(renv) { } @@ -61,9 +60,7 @@ Switch::~Switch() void Switch::onRemotePacket(const InetAddress &fromAddr,const void *data,unsigned int len) { try { - if (len == ZT_PROTO_BEACON_LENGTH) { - _handleBeacon(fromAddr,Buffer(data,len)); - } else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { + if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) { if (((const unsigned char *)data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) { _handleRemotePacketFragment(fromAddr,data,len); } else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) { @@ -696,23 +693,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat } } -void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer &data) -{ - Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - if (beaconAddr == RR->identity.address()) - return; - SharedPtr peer(RR->topology->getPeer(beaconAddr)); - if (peer) { - const uint64_t now = RR->node->now(); - if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) { - _lastBeacon = now; - Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP); - outp.armor(peer->key(),false); - RR->node->putPacket(fromAddr,outp.data(),outp.size()); - } - } -} - Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted) { SharedPtr root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); diff --git a/node/Switch.hpp b/node/Switch.hpp index 0ba4c138..af6e5938 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -183,12 +183,10 @@ public: private: void _handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len); void _handleRemotePacketHead(const InetAddress &fromAddr,const void *data,unsigned int len); - void _handleBeacon(const InetAddress &fromAddr,const Buffer &data); Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted); bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid); const RuntimeEnvironment *const RR; - volatile uint64_t _lastBeacon; // Outsanding WHOIS requests and how many retries they've undergone struct WhoisRequest -- cgit v1.2.3 From 235f4762b700174c795b28de7d4fe2f70cddbcd8 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Mon, 6 Jul 2015 15:51:04 -0700 Subject: Plumbing for local interface addresses -- GitHub issue #180 --- include/ZeroTierOne.h | 32 ++++++++++++++++++++++++++++++++ node/Node.cpp | 28 ++++++++++++++++++++++++++++ node/Node.hpp | 5 +++++ node/Path.hpp | 21 +++++++-------------- node/RemotePath.hpp | 16 ++++++++++++---- 5 files changed, 84 insertions(+), 18 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index b6ff69ab..4790795e 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -628,6 +628,15 @@ typedef struct unsigned long peerCount; } ZT1_PeerList; +/** + * Local interface trust levels + */ +typedef enum { + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0, + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 1, + ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 2 +} ZT1_LocalInterfaceAddressTrust; + /** * An instance of a ZeroTier One node (opaque) */ @@ -958,6 +967,29 @@ ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node); */ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr); +/** + * Add a local interface address + * + * Local interface addresses may be added if you want remote peers + * with whom you have a trust relatinship (e.g. common network membership) + * to receive information about these endpoints as potential endpoints for + * direct communication. + * + * Take care that these are never ZeroTier interface addresses, otherwise + * strange things might happen or they simply won't work. + * + * @param addr Local interface address + * @param metric Local interface metric + * @param trust How much do you trust the local network under this interface? + * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall + */ +void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + +/** + * Clear local interface addresses + */ +void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node); + /** * Set a network configuration master instance for this node * diff --git a/node/Node.cpp b/node/Node.cpp index 7c70db97..057dc285 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,6 +426,20 @@ void Node::freeQueryResult(void *qr) ::free(qr); } +void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); + std::sort(_directPaths.begin(),_directPaths.end()); + _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); +} + +void Node::clearLocalInterfaceAddresses() +{ + Mutex::Lock _l(_directPaths_m); + _directPaths.clear(); +} + void Node::setNetconfMaster(void *networkControllerInstance) { RR->localNetworkController = reinterpret_cast(networkControllerInstance); @@ -679,6 +693,20 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } +void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + try { + reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) {} +} + +void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) +{ + try { + reinterpret_cast(node)->clearLocalInterfaceAddresses(); + } catch ( ... ) {} +} + void ZT1_version(int *major,int *minor,int *revision,unsigned long *featureFlags) { if (major) *major = ZEROTIER_ONE_VERSION_MAJOR; diff --git a/node/Node.hpp b/node/Node.hpp index cd8d68fc..1c260545 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -104,6 +104,8 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); + void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); // Internal functions ------------------------------------------------------ @@ -172,6 +174,9 @@ public: return nw; } + /** + * @return Potential direct paths to me a.k.a. local interface addresses + */ inline std::vector directPaths() const { Mutex::Lock _l(_directPaths_m); diff --git a/node/Path.hpp b/node/Path.hpp index b43b3f6d..80b9a3c0 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -37,28 +37,27 @@ namespace ZeroTier { class Path { public: + // Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h enum Trust { - TRUST_NORMAL, - TRUST_PRIVACY, - TRUST_ULTIMATE + TRUST_NORMAL = 0, + TRUST_PRIVACY = 1, + TRUST_ULTIMATE = 2 }; Path() : _addr(), _metric(0), _trust(TRUST_NORMAL), - _reliable(false), - _fixed(false) + _reliable(false) { } - Path(const InetAddress &addr,int metric,Trust trust,bool reliable,bool fixed) : + Path(const InetAddress &addr,int metric,Trust trust,bool reliable) : _addr(addr), _metric(metric), _trust(trust), - _reliable(reliable), - _fixed(fixed) + _reliable(reliable) { } @@ -82,11 +81,6 @@ public: */ inline bool reliable() const throw() { return _reliable; } - /** - * @return Is this a fixed path? - */ - inline bool fixed() const throw() { return _fixed; } - /** * @return True if address is non-NULL */ @@ -105,7 +99,6 @@ protected: int _metric; // negative == blacklisted Trust _trust; bool _reliable; - bool _fixed; }; } // namespace ZeroTier diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp index 47c8916d..5592c8e1 100644 --- a/node/RemotePath.hpp +++ b/node/RemotePath.hpp @@ -52,18 +52,25 @@ public: RemotePath() : Path(), _lastSend(0), - _lastReceived(0) {} + _lastReceived(0), + _fixed(false) {} RemotePath(const InetAddress &addr,bool fixed) : - Path(addr,0,TRUST_NORMAL,false,fixed), + Path(addr,0,TRUST_NORMAL,false), _lastSend(0), - _lastReceived(0) {} + _lastReceived(0), + _fixed(fixed) {} inline uint64_t lastSend() const throw() { return _lastSend; } inline uint64_t lastReceived() const throw() { return _lastReceived; } /** - * @param f New value of parent 'fixed' field + * @return Is this a fixed path? + */ + inline bool fixed() const throw() { return _fixed; } + + /** + * @param f New value of fixed flag */ inline void setFixed(const bool f) throw() @@ -127,6 +134,7 @@ public: private: uint64_t _lastSend; uint64_t _lastReceived; + bool _fixed; }; } // namespace ZeroTier -- cgit v1.2.3 From c863ff3f02e9d68eb9bea32160d252eaddb7f1f5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 08:54:48 -0700 Subject: A bunch of comments and cleanup, including some to yesterday's direct path pushing changes. Move path viability check to one place, and stop trying to use link-local addresses since they are not reliable. --- include/ZeroTierOne.h | 9 ++++++- node/IncomingPacket.cpp | 66 +++++++++++++++++++++++++++++++++++-------------- node/Node.cpp | 24 +++++++++++------- node/Node.hpp | 2 +- node/Packet.hpp | 7 +++--- node/Path.hpp | 33 +++++++++++++++++++++++++ service/OneService.cpp | 32 +++--------------------- 7 files changed, 112 insertions(+), 61 deletions(-) (limited to 'node/Node.cpp') diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 4790795e..446bbc77 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -978,12 +978,19 @@ void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr); * Take care that these are never ZeroTier interface addresses, otherwise * strange things might happen or they simply won't work. * + * This returns a boolean indicating whether or not the address was + * accepted. ZeroTier will only communicate over certain address types + * and (for IP) address classes. Thus it's safe to just dump your OS's + * entire remote IP list (excluding ZeroTier interface IPs) into here + * and let ZeroTier determine which addresses it will use. + * * @param addr Local interface address * @param metric Local interface metric * @param trust How much do you trust the local network under this interface? * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall + * @return Boolean: non-zero if address was accepted and added */ -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); /** * Clear local interface addresses diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 80159194..7e883221 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -134,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr break; case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: { + /* Note: certificates are public so it's safe to push them to anyone + * who asks. We won't communicate unless we also get a certificate + * from the remote that agrees. */ SharedPtr network(RR->node->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if (network) { SharedPtr nconf(network->config2()); @@ -170,8 +173,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) { + /* Note: this is the only packet ever sent in the clear, and it's also + * the only packet that we authenticate via a different path. Authentication + * occurs here and is based on the validity of the identity and the + * integrity of the packet's MAC, but it must be done after we check + * the identity since HELLO is a mechanism for learning new identities + * in the first place. */ + try { const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION]; + if (protoVersion < ZT_PROTO_VERSION_MIN) { + TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION]; const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION]; const unsigned int vRevision = at(ZT_PROTO_VERB_HELLO_IDX_REVISION); @@ -179,6 +194,10 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) Identity id; unsigned int destAddrPtr = id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY) + ZT_PROTO_VERB_HELLO_IDX_IDENTITY; + if (source() != id.address()) { + TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } InetAddress destAddr; if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field @@ -193,16 +212,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } } - if (source() != id.address()) { - TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - - if (protoVersion < ZT_PROTO_VERSION_MIN) { - TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str()); - return true; - } - SharedPtr peer(RR->topology->getPeer(id.address())); if (peer) { // We already have an identity with this address -- check for collisions @@ -245,12 +254,14 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR) } else { // We don't already have an identity with this address -- validate and learn it + // Check identity proof of work if (!id.locallyValidate()) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str()); return true; } + // Check packet integrity and authentication SharedPtr newPeer(new Peer(RR->identity,id)); if (!dearmor(newPeer->key())) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); @@ -554,14 +565,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr

validateAndAddMembershipCertificate(com); + if (com.hasRequiredFields()) { + if (!network->validateAndAddMembershipCertificate(com)) + comFailed = true; // technically this check is redundant to isAllowed(), but do it anyway for thoroughness + } } - if (!network->isAllowed(peer->address())) { + if ((comFailed)||(!network->isAllowed(peer->address()))) { TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id()); _sendErrorNeedCertificate(RR,peer,network->id()); return true; @@ -647,8 +661,21 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment ptr += com.deserialize(*this,ptr); if (com.hasRequiredFields()) { SharedPtr network(RR->node->network(com.networkId())); - if (network) - network->validateAndAddMembershipCertificate(com); + if (network) { + if (network->validateAndAddMembershipCertificate(com)) { + if ((network->isAllowed(peer->address()))&&(network->peerNeedsOurMembershipCertificate(peer->address(),RR->node->now()))) { + // If peer passed our check and we haven't sent it our cert yet, respond + // and push our cert as well for instant authorization setup. + SharedPtr nconf(network->config2()); + if ((nconf)&&(nconf->com())) { + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + nconf->com().serialize(outp); + outp.armor(peer->key(),true); + RR->node->putPacket(_remoteAddress,outp.data(),outp.size()); + } + } + } + } } } @@ -890,24 +917,25 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2; while (count) { // if ptr overflows Buffer will throw + // TODO: properly handle blacklisting, support other features... see Packet.hpp. + unsigned int flags = (*this)[ptr++]; /*int metric = (*this)[ptr++];*/ ++ptr; unsigned int extLen = at(ptr); ptr += 2; ptr += extLen; // unused right now unsigned int addrType = (*this)[ptr++]; + unsigned int addrLen = (*this)[ptr++]; switch(addrType) { case 4: { InetAddress a(field(ptr,4),4,at(ptr + 4)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; case 6: { InetAddress a(field(ptr,16),16,at(ptr + 16)); - if ((flags & (0x01 | 0x02)) == 0) { + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) peer->attemptToContactAt(RR,a,RR->node->now()); - } } break; } ptr += addrLen; diff --git a/node/Node.cpp b/node/Node.cpp index 057dc285..d8bd8910 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -426,12 +426,16 @@ void Node::freeQueryResult(void *qr) ::free(qr); } -void Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) -{ - Mutex::Lock _l(_directPaths_m); - _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); - std::sort(_directPaths.begin(),_directPaths.end()); - _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + if (Path::isAddressValidForPath(*(reinterpret_cast(addr)))) { + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast(addr)),metric,(Path::Trust)trust,reliable != 0)); + std::sort(_directPaths.begin(),_directPaths.end()); + _directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end()); + return 1; + } + return 0; } void Node::clearLocalInterfaceAddresses() @@ -693,11 +697,13 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } -void ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) { try { - reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); - } catch ( ... ) {} + return reinterpret_cast(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) { + return 0; + } } void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) diff --git a/node/Node.hpp b/node/Node.hpp index 1c260545..fe31576c 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -104,7 +104,7 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); - void addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); diff --git a/node/Packet.hpp b/node/Packet.hpp index 51a241ba..9787edb7 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -678,9 +678,10 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * Certificate contains network ID, peer it was issued for, etc. - * - * OK/ERROR are not generated. + * OK/ERROR are not generated, but the recipient should push its network + * membership certificate if the certificate the peer pushed is valid + * and agrees and if it hasn't done so in too long. This ensures instant + * network authentication setup between valid and authorized peers. */ VERB_NETWORK_MEMBERSHIP_CERTIFICATE = 10, diff --git a/node/Path.hpp b/node/Path.hpp index 80b9a3c0..cd21444b 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -94,6 +94,39 @@ public: inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); } inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); } + /** + * Check whether this address is valid for a ZeroTier path + * + * This checks the address type and scope against address types and scopes + * that we currently support for ZeroTier communication. + * + * @param a Address to check + * @return True if address is good for ZeroTier path use + */ + static inline bool isAddressValidForPath(const InetAddress &a) + throw() + { + if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) { + switch(a.ipScope()) { + /* Note: we don't do link-local at the moment. Unfortunately these + * cause several issues. The first is that they usually require a + * device qualifier, which we don't handle yet and can't portably + * push in PUSH_DIRECT_PATHS. The second is that some OSes assign + * these very ephemerally or otherwise strangely. So we'll use + * private, pseudo-private, shared (e.g. carrier grade NAT), or + * global IP addresses. */ + case InetAddress::IP_SCOPE_PRIVATE: + case InetAddress::IP_SCOPE_PSEUDOPRIVATE: + case InetAddress::IP_SCOPE_SHARED: + case InetAddress::IP_SCOPE_GLOBAL: + return true; + default: + return false; + } + } + return false; + } + protected: InetAddress _addr; int _metric; // negative == blacklisted diff --git a/service/OneService.cpp b/service/OneService.cpp index 527ac1b0..bde59d56 100644 --- a/service/OneService.cpp +++ b/service/OneService.cpp @@ -591,20 +591,8 @@ public: } if (!isZT) { InetAddress ip(ifa->ifa_addr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); } } ifa = ifa->ifa_next; @@ -637,20 +625,8 @@ public: PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress; while (ua) { InetAddress ip(ua->Address.lpSockaddr); - if ((ip.ss_family == AF_INET)||(ip.ss_family == AF_INET6)) { - switch(ip.ipScope()) { - case InetAddress::IP_SCOPE_LINK_LOCAL: - case InetAddress::IP_SCOPE_PRIVATE: - case InetAddress::IP_SCOPE_PSEUDOPRIVATE: - case InetAddress::IP_SCOPE_SHARED: - case InetAddress::IP_SCOPE_GLOBAL: - ip.setPort(_port); - _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); - break; - default: - break; - } - } + ip.setPort(_port); + _node->addLocalInterfaceAddress(reinterpret_cast(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0); ua = ua->Next; } } -- cgit v1.2.3 From 3f567a07ca0cda5c3370105d88cb939ad62b4b7d Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 7 Jul 2015 10:49:50 -0700 Subject: Save a little bit of RAM by getting rid of overkill CMWC4096 non-crypto PRNG and replacing it with a simple non-crypto PRNG that just uses Salsa20. --- node/CMWC4096.hpp | 91 --------------------------------------------- node/Multicaster.cpp | 5 +-- node/Node.cpp | 22 +++++++++-- node/Node.hpp | 10 +++++ node/RuntimeEnvironment.hpp | 3 -- node/Switch.cpp | 5 +-- 6 files changed, 32 insertions(+), 104 deletions(-) delete mode 100644 node/CMWC4096.hpp (limited to 'node/Node.cpp') diff --git a/node/CMWC4096.hpp b/node/CMWC4096.hpp deleted file mode 100644 index b62d7d67..00000000 --- a/node/CMWC4096.hpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * ZeroTier One - Network Virtualization Everywhere - * Copyright (C) 2011-2015 ZeroTier, Inc. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - * -- - * - * ZeroTier may be used and distributed under the terms of the GPLv3, which - * are available at: http://www.gnu.org/licenses/gpl-3.0.html - * - * If you would like to embed ZeroTier into a commercial application or - * redistribute it in a modified binary form, please contact ZeroTier Networks - * LLC. Start here: http://www.zerotier.com/ - */ - -#ifndef ZT_CMWC4096_HPP -#define ZT_CMWC4096_HPP - -#include -#include "Utils.hpp" - -namespace ZeroTier { - -/** - * Complement Multiply With Carry random number generator - * - * Based on original code posted to Usenet in the public domain by - * George Marsaglia. Period is approximately 2^131086. - * - * This is not used for cryptographic purposes but for a very fast - * and high-quality PRNG elsewhere in the code. - */ -class CMWC4096 -{ -public: - /** - * Construct and initialize from secure random source - */ - CMWC4096() - throw() - { - Utils::getSecureRandom(Q,sizeof(Q)); - Utils::getSecureRandom(&c,sizeof(c)); - c %= 809430660; - i = 4095; - } - - inline uint32_t next32() - throw() - { - uint32_t __i = ++i & 4095; - const uint64_t t = (18782ULL * (uint64_t)Q[__i]) + (uint64_t)c; - c = (uint32_t)(t >> 32); - uint32_t x = c + (uint32_t)t; - const uint32_t p = (uint32_t)(x < c); x += p; c += p; - return (Q[__i] = 0xfffffffe - x); - } - - inline uint64_t next64() - throw() - { - 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/Multicaster.cpp b/node/Multicaster.cpp index 77ea2e66..3c105236 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -35,7 +35,6 @@ #include "Switch.hpp" #include "Packet.hpp" #include "Peer.hpp" -#include "CMWC4096.hpp" #include "C25519.hpp" #include "CertificateOfMembership.hpp" @@ -97,7 +96,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const // will return different subsets of a large multicast group. k = 0; while ((added < limit)&&(k < gs->second.members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) { - rptr = (unsigned int)RR->prng->next32(); + rptr = (unsigned int)RR->node->prng(); restart_member_scan: a = gs->second.members[rptr % (unsigned int)gs->second.members.size()].address.toInt(); @@ -171,7 +170,7 @@ void Multicaster::send( for(unsigned long i=0;i0;--i) { - unsigned long j = RR->prng->next32() % (i + 1); + unsigned long j = (unsigned long)RR->node->prng() % (i + 1); unsigned long tmp = indexes[j]; indexes[j] = indexes[i]; indexes[i] = tmp; diff --git a/node/Node.cpp b/node/Node.cpp index d8bd8910..3df34aec 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -37,7 +37,6 @@ #include "Node.hpp" #include "RuntimeEnvironment.hpp" #include "NetworkController.hpp" -#include "CMWC4096.hpp" #include "Switch.hpp" #include "Multicaster.hpp" #include "AntiRecursion.hpp" @@ -76,6 +75,7 @@ Node::Node( _eventCallback(eventCallback), _networks(), _networks_m(), + _prngStreamPtr(0), _now(now), _lastPingCheck(0), _lastHousekeepingRun(0) @@ -85,6 +85,15 @@ Node::Node( _newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION; _online = false; + // Use Salsa20 alone as a high-quality non-crypto PRNG + { + char foo[32]; + Utils::getSecureRandom(foo,32); + _prng.init(foo,256,foo,8); + memset(_prngStream,0,sizeof(_prngStream)); + _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + } + std::string idtmp(dataStoreGet("identity.secret")); if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) { TRACE("identity.secret not found, generating..."); @@ -103,7 +112,6 @@ Node::Node( } try { - RR->prng = new CMWC4096(); RR->sw = new Switch(RR); RR->mc = new Multicaster(RR); RR->antiRec = new AntiRecursion(); @@ -115,7 +123,6 @@ Node::Node( delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; throw; } @@ -146,7 +153,6 @@ Node::~Node() delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; } ZT1_ResultCode Node::processWirePacket( @@ -510,6 +516,14 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...) } #endif // ZT_TRACE +uint64_t Node::prng() +{ + unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t))); + if (!p) + _prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream)); + return _prngStream[p]; +} + } // namespace ZeroTier /****************************************************************************/ diff --git a/node/Node.hpp b/node/Node.hpp index fe31576c..579d3a57 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -44,6 +44,7 @@ #include "MAC.hpp" #include "Network.hpp" #include "Path.hpp" +#include "Salsa20.hpp" #undef TRACE #ifdef ZT_TRACE @@ -219,6 +220,11 @@ public: void postTrace(const char *module,unsigned int line,const char *fmt,...); #endif + /** + * @return Next 64-bit random number (not for cryptographic use) + */ + uint64_t prng(); + private: inline SharedPtr _network(uint64_t nwid) const { @@ -253,6 +259,10 @@ private: Mutex _backgroundTasksLock; + unsigned int _prngStreamPtr; + Salsa20 _prng; + uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream + uint64_t _now; uint64_t _lastPingCheck; uint64_t _lastHousekeepingRun; diff --git a/node/RuntimeEnvironment.hpp b/node/RuntimeEnvironment.hpp index 228040e7..e5d1f446 100644 --- a/node/RuntimeEnvironment.hpp +++ b/node/RuntimeEnvironment.hpp @@ -38,7 +38,6 @@ namespace ZeroTier { class NodeConfig; class Switch; class Topology; -class CMWC4096; class Node; class Multicaster; class AntiRecursion; @@ -55,7 +54,6 @@ public: node(n), identity(), localNetworkController((NetworkController *)0), - prng((CMWC4096 *)0), sw((Switch *)0), mc((Multicaster *)0), antiRec((AntiRecursion *)0), @@ -83,7 +81,6 @@ public: * These are constant and never null after startup unless indicated. */ - CMWC4096 *prng; Switch *sw; Multicaster *mc; AntiRecursion *antiRec; diff --git a/node/Switch.cpp b/node/Switch.cpp index 62fa02cd..4fd5d769 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -42,7 +42,6 @@ #include "InetAddress.hpp" #include "Topology.hpp" #include "Peer.hpp" -#include "CMWC4096.hpp" #include "AntiRecursion.hpp" #include "Packet.hpp" @@ -236,7 +235,7 @@ void Switch::onLocalEthernet(const SharedPtr &network,const MAC &from,c while (numBridges < ZT_MAX_BRIDGE_SPAM) { if (ab == nconf->activeBridges().end()) ab = nconf->activeBridges().begin(); - if (((unsigned long)RR->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0) { + if (((unsigned long)RR->node->prng() % (unsigned long)nconf->activeBridges().size()) == 0) { bridges[numBridges++] = *ab; ++ab; } else ++ab; @@ -327,7 +326,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force) * the order we make each attempted NAT-t favor one or the other going * first, meaning if it doesn't succeed the first time it might the second * and so forth. */ - unsigned int alt = RR->prng->next32() & 1; + unsigned int alt = (unsigned int)RR->node->prng() & 1; unsigned int completed = alt + 2; while (alt != completed) { if ((alt & 1) == 0) { -- cgit v1.2.3