diff options
Diffstat (limited to 'node')
-rw-r--r-- | node/CMWC4096.hpp | 91 | ||||
-rw-r--r-- | node/Constants.hpp | 13 | ||||
-rw-r--r-- | node/IncomingPacket.cpp | 94 | ||||
-rw-r--r-- | node/IncomingPacket.hpp | 1 | ||||
-rw-r--r-- | node/InetAddress.cpp | 8 | ||||
-rw-r--r-- | node/Multicaster.cpp | 19 | ||||
-rw-r--r-- | node/Multicaster.hpp | 9 | ||||
-rw-r--r-- | node/Network.cpp | 56 | ||||
-rw-r--r-- | node/Network.hpp | 6 | ||||
-rw-r--r-- | node/Node.cpp | 78 | ||||
-rw-r--r-- | node/Node.hpp | 26 | ||||
-rw-r--r-- | node/OutboundMulticast.cpp | 3 | ||||
-rw-r--r-- | node/Packet.cpp | 2 | ||||
-rw-r--r-- | node/Packet.hpp | 169 | ||||
-rw-r--r-- | node/Path.hpp | 172 | ||||
-rw-r--r-- | node/Peer.cpp | 77 | ||||
-rw-r--r-- | node/Peer.hpp | 37 | ||||
-rw-r--r-- | node/RemotePath.hpp | 142 | ||||
-rw-r--r-- | node/RuntimeEnvironment.hpp | 3 | ||||
-rw-r--r-- | node/SelfAwareness.cpp | 2 | ||||
-rw-r--r-- | node/Switch.cpp | 185 | ||||
-rw-r--r-- | node/Switch.hpp | 15 | ||||
-rw-r--r-- | node/Topology.cpp | 2 | ||||
-rw-r--r-- | node/Utils.hpp | 18 |
24 files changed, 742 insertions, 486 deletions
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 <http://www.gnu.org/licenses/>. - * - * -- - * - * 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 <stdint.h> -#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/Constants.hpp b/node/Constants.hpp index ac9dbc99..d15fef13 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -305,19 +305,14 @@ #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 + * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) */ -#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500 +#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000 /** - * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding) + * Interval between direct path pushes in milliseconds */ -#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000 +#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000 /** * Sanity limit on maximum bridge routes diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 7e2bcdaa..6c3a0932 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -84,6 +84,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR) case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer); case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer); case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer); + case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer); } } else { RR->sw->requestWhois(source()); @@ -133,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> 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> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); if (network) { SharedPtr<NetworkConfig> nconf(network->config2()); @@ -152,7 +156,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> } break; case Packet::ERROR_UNWANTED_MULTICAST: { - // TODO: unsubscribe + uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD); + MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14)); + TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str()); + RR->mc->remove(nwid,mg,peer->address()); } break; default: break; @@ -169,8 +176,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> 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<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION); @@ -178,6 +197,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 @@ -192,16 +215,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> peer(RR->topology->getPeer(id.address())); if (peer) { // We already have an identity with this address -- check for collisions @@ -244,12 +257,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<Peer> newPeer(new Peer(RR->identity,id)); if (!dearmor(newPeer->key())) { RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress); @@ -428,7 +443,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS); SharedPtr<Network> network(RR->node->network(nwid)); if ((network)&&(com.hasRequiredFields())) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } if ((flags & 0x02) != 0) { @@ -553,14 +568,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS]; unsigned int comLen = 0; + bool comFailed = false; if ((flags & 0x01) != 0) { CertificateOfMembership com; comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM); - if (com.hasRequiredFields()) - network->addMembershipCertificate(com,false); + 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; @@ -595,9 +613,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; } - } - - if (to != network->mac()) { + } else if (to != network->mac()) { if (!network->permitsBridging(RR->identity.address())) { TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id()); return true; @@ -649,7 +665,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment if (com.hasRequiredFields()) { SharedPtr<Network> network(RR->node->network(com.networkId())); if (network) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } } @@ -807,7 +823,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share CertificateOfMembership com; offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM); if (com.hasRequiredFields()) - network->addMembershipCertificate(com,false); + network->validateAndAddMembershipCertificate(com); } // Check membership after we've read any included COM, since @@ -884,6 +900,44 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share return true; } +bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer) +{ + try { + unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD); + 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<uint16_t>(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<uint16_t>(ptr + 4)); + 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<uint16_t>(ptr + 16)); + if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) ) + 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; +} + void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid) { Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR); diff --git a/node/IncomingPacket.hpp b/node/IncomingPacket.hpp index 174fa38d..3bf7737d 100644 --- a/node/IncomingPacket.hpp +++ b/node/IncomingPacket.hpp @@ -121,6 +121,7 @@ private: bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); + bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer); // Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid); diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 83de36d4..91bfbed6 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -91,7 +91,13 @@ InetAddress::IpScope InetAddress::ipScope() const const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr); if ((ip[0] & 0xf0) == 0xf0) { if (ip[0] == 0xff) return IP_SCOPE_MULTICAST; // ff00::/8 - if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) return IP_SCOPE_LINK_LOCAL; // fe80::/10 + if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) { + unsigned int k = 2; + while ((!ip[k])&&(k < 15)) ++k; + if ((k == 15)&&(ip[15] == 0x01)) + return IP_SCOPE_LOOPBACK; // fe80::1/128 + else return IP_SCOPE_LINK_LOCAL; // fe80::/10 + } if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE; // fc00::/7 } unsigned int k = 0; diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 77ea2e66..33424e4a 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" @@ -62,6 +61,20 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m } } +void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member) +{ + Mutex::Lock _l(_groups_m); + std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::iterator g(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg))); + if (g != _groups.end()) { + for(std::vector<MulticastGroupMember>::iterator m(g->second.members.begin());m!=g->second.members.end();++m) { + if (m->address == member) { + g->second.members.erase(m); + break; + } + } + } +} + unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const { unsigned char *p; @@ -97,7 +110,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 +184,7 @@ void Multicaster::send( for(unsigned long i=0;i<gs.members.size();++i) indexes[i] = i; for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--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/Multicaster.hpp b/node/Multicaster.hpp index c6c93b1f..0dd199f9 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -107,6 +107,15 @@ public: void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown); /** + * Remove a multicast group member (if present) + * + * @param nwid Network ID + * @param mg Multicast group + * @param member Member to unsubscribe + */ + void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member); + + /** * Append gather results to a packet by choosing registered multicast recipients at random * * This appends the following fields to the packet: diff --git a/node/Network.cpp b/node/Network.cpp index 4414e4d1..adc8e1b8 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -264,56 +264,58 @@ void Network::requestConfiguration() outp.append((uint64_t)_config->revision()); else outp.append((uint64_t)0); } - RR->sw->send(outp,true,_id); + RR->sw->send(outp,true,0); } -void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept) +bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert) { if (!cert) // sanity check - return; + return false; Mutex::Lock _l(_lock); CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()]; // Nothing to do if the cert hasn't changed -- we get duplicates due to zealous cert pushing if (old == cert) - return; + return true; // but if it's a duplicate of one we already accepted, return is 'true' // Check signature, log and return if cert is invalid - if (!forceAccept) { - if (cert.signedBy() != controller()) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; + if (cert.signedBy() != controller()) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signer + } + + if (cert.signedBy() == RR->identity.address()) { + + // We are the controller: RR->identity.address() == controller() == cert.signedBy() + // So, verify that we signed th cert ourself + if (!cert.verify(RR->identity)) { + TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } - if (cert.signedBy() == RR->identity.address()) { - // We are the controller: RR->identity.address() == controller() == cert.signedBy() - // So, verify that we signed th cert ourself - if (!cert.verify(RR->identity)) { - TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } - } else { + } else { - SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy())); + SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy())); - if (!signer) { - // This would be rather odd, since this is our controller... could happen - // if we get packets before we've gotten config. - RR->sw->requestWhois(cert.signedBy()); - return; - } + if (!signer) { + // This would be rather odd, since this is our controller... could happen + // if we get packets before we've gotten config. + RR->sw->requestWhois(cert.signedBy()); + return false; // signer unknown + } - if (!cert.verify(signer->identity())) { - TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); - return; - } + if (!cert.verify(signer->identity())) { + TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str()); + return false; // invalid signature } } // If we made it past authentication, update cert if (cert.revision() != old.revision()) old = cert; + + return true; } bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now) diff --git a/node/Network.hpp b/node/Network.hpp index daa4554e..d7320d46 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -179,12 +179,12 @@ public: * Add or update a membership certificate * * @param cert Certificate of membership - * @param forceAccept If true, accept without validating signature + * @return True if certificate was accepted as valid */ - void addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept); + bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert); /** - * Check if we should push membership certificate to a peer, and update last pushed + * Check if we should push membership certificate to a peer, AND update last pushed * * If we haven't pushed a cert to this peer in a long enough time, this returns * true and updates the last pushed time. Otherwise it returns false. diff --git a/node/Node.cpp b/node/Node.cpp index 8cdc6d62..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,16 +75,25 @@ Node::Node( _eventCallback(eventCallback), _networks(), _networks_m(), + _prngStreamPtr(0), _now(now), _lastPingCheck(0), - _lastHousekeepingRun(0), - _lastBeacon(0) + _lastHousekeepingRun(0) { _newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR; _newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR; _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..."); @@ -104,7 +112,6 @@ Node::Node( } try { - RR->prng = new CMWC4096(); RR->sw = new Switch(RR); RR->mc = new Multicaster(RR); RR->antiRec = new AntiRecursion(); @@ -116,7 +123,6 @@ Node::Node( delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; throw; } @@ -147,7 +153,6 @@ Node::~Node() delete RR->antiRec; delete RR->mc; delete RR->sw; - delete RR->prng; } ZT1_ResultCode Node::processWirePacket( @@ -269,19 +274,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<uint32_t *>(p)) = RR->prng->next32(); - p = beacon + 4; - *(reinterpret_cast<uint32_t *>(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; } @@ -388,10 +380,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<Path> paths(pi->second->paths()); - Path *bestPath = pi->second->getBestPath(_now); + std::vector<RemotePath> paths(pi->second->paths()); + RemotePath *bestPath = pi->second->getBestPath(_now); p->pathCount = 0; - for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) { + for(std::vector<RemotePath>::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(); @@ -440,6 +432,24 @@ void Node::freeQueryResult(void *qr) ::free(qr); } +int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + if (Path::isAddressValidForPath(*(reinterpret_cast<const InetAddress *>(addr)))) { + Mutex::Lock _l(_directPaths_m); + _directPaths.push_back(Path(*(reinterpret_cast<const InetAddress *>(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() +{ + Mutex::Lock _l(_directPaths_m); + _directPaths.clear(); +} + void Node::setNetconfMaster(void *networkControllerInstance) { RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance); @@ -506,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 /****************************************************************************/ @@ -693,6 +711,22 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance) } catch ( ... ) {} } +int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable) +{ + try { + return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust,reliable); + } catch ( ... ) { + return 0; + } +} + +void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node) +{ + try { + reinterpret_cast<ZeroTier::Node *>(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 2d2898b5..579d3a57 100644 --- a/node/Node.hpp +++ b/node/Node.hpp @@ -43,6 +43,8 @@ #include "Mutex.hpp" #include "MAC.hpp" #include "Network.hpp" +#include "Path.hpp" +#include "Salsa20.hpp" #undef TRACE #ifdef ZT_TRACE @@ -103,6 +105,8 @@ public: ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const; ZT1_VirtualNetworkList *networks() const; void freeQueryResult(void *qr); + int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable); + void clearLocalInterfaceAddresses(); void setNetconfMaster(void *networkControllerInstance); // Internal functions ------------------------------------------------------ @@ -171,6 +175,15 @@ public: return nw; } + /** + * @return Potential direct paths to me a.k.a. local interface addresses + */ + inline std::vector<Path> directPaths() const + { + Mutex::Lock _l(_directPaths_m); + return _directPaths; + } + inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); } inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); } inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,(const void *)0,0,0); } @@ -207,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> _network(uint64_t nwid) const { @@ -236,12 +254,18 @@ private: std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks; Mutex _networks_m; + std::vector<Path> _directPaths; + Mutex _directPaths_m; + 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; - uint64_t _lastBeacon; unsigned int _newestVersionSeen[3]; // major, minor, revision bool _online; }; diff --git a/node/OutboundMulticast.cpp b/node/OutboundMulticast.cpp index f62046be..46116c07 100644 --- a/node/OutboundMulticast.cpp +++ b/node/OutboundMulticast.cpp @@ -104,7 +104,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA { if (_haveCom) { SharedPtr<Network> network(RR->node->network(_nwid)); - if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) { + if ((network)&&(network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now()))) { _packetWithCom.newInitializationVector(); _packetWithCom.setDestination(toAddr); //TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str()); @@ -112,6 +112,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA return; } } + //TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str()); _packetNoCom.newInitializationVector(); _packetNoCom.setDestination(toAddr); diff --git a/node/Packet.cpp b/node/Packet.cpp index 4d58815e..2c73a087 100644 --- a/node/Packet.cpp +++ b/node/Packet.cpp @@ -51,7 +51,7 @@ const char *Packet::verbString(Verb v) case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER"; case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY"; - case VERB_PHYSICAL_ADDRESS_PUSH: return "PHYSICAL_ADDRESS_PUSH"; + case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS"; } return "(unknown)"; } diff --git a/node/Packet.hpp b/node/Packet.hpp index d9f8d888..e84306c2 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -71,13 +71,14 @@ /** * Maximum hop count allowed by packet structure (3 bits, 0-7) * - * This is not necessarily the maximum hop counter after which - * relaying is no longer performed. + * This is a protocol constant. It's the maximum allowed by the length + * of the hop counter -- three bits. See node/Constants.hpp for the + * pragmatic forwarding limit, which is typically lower. */ #define ZT_PROTO_MAX_HOPS 7 /** - * Cipher suite: Curve25519/Poly1305/Salsa20/12 without payload encryption + * Cipher suite: Curve25519/Poly1305/Salsa20/12/NOCRYPT * * This specifies Poly1305 MAC using a 32-bit key derived from the first * 32 bytes of a Salsa20/12 keystream as in the Salsa20/12 cipher suite, @@ -103,9 +104,7 @@ * * This message is encrypted with the latest negotiated ephemeral (PFS) * key pair and cipher suite. If authentication fails, VERB_SET_EPHEMERAL_KEY - * may be sent to renegotiate ephemeral keys. To prevent attacks, this - * attempted renegotiation should be limited to some sane rate such as - * once per second. + * may be sent to renegotiate ephemeral keys. */ #define ZT_PROTO_CIPHER_SUITE__EPHEMERAL 7 @@ -113,7 +112,7 @@ * DEPRECATED payload encrypted flag, will be removed for re-use soon. * * This has been replaced by the two-bit cipher suite selection field where - * a value of 0 indicated unencrypted (but authenticated) messages. + * a value of 0 indicates unencrypted (but authenticated) messages. */ #define ZT_PROTO_FLAG_ENCRYPTED 0x80 @@ -132,11 +131,68 @@ /** * Rounds used for Salsa20 encryption in ZT + * + * Discussion: + * + * DJB (Salsa20's designer) designed Salsa20 with a significant margin of 20 + * rounds, but has said repeatedly that 12 is likely sufficient. So far (as of + * July 2015) there are no published attacks against 12 rounds, let alone 20. + * + * In cryptography, a "break" means something different from what it means in + * common discussion. If a cipher is 256 bits strong and someone finds a way + * to reduce key search to 254 bits, this constitues a "break" in the academic + * literature. 254 bits is still far beyond what can be leveraged to accomplish + * a "break" as most people would understand it -- the actual decryption and + * reading of traffic. + * + * Nevertheless, "attacks only get better" as cryptographers like to say. As + * a result, they recommend not using anything that's shown any weakness even + * if that weakness is so far only meaningful to academics. It may be a sign + * of a deeper problem. + * + * So why choose a lower round count? + * + * Turns out the speed difference is nontrivial. On a Macbook Pro (Core i3) 20 + * rounds of SSE-optimized Salsa20 achieves ~508mb/sec/core, while 12 rounds + * hits ~832mb/sec/core. ZeroTier is designed for multiple objectives: + * security, simplicity, and performance. In this case a deference was made + * for performance. + * + * Meta discussion: + * + * The cipher is not the thing you should be paranoid about. + * + * I'll qualify that. If the cipher is known to be weak, like RC4, or has a + * key size that is too small, like DES, then yes you should worry about + * the cipher. + * + * But if the cipher is strong and your adversary is anyone other than the + * intelligence apparatus of a major superpower, you are fine in that + * department. + * + * Go ahead. Search for the last ten vulnerabilities discovered in SSL. Not + * a single one involved the breaking of a cipher. Now broaden your search. + * Look for issues with SSH, IPSec, etc. The only cipher-related issues you + * will find might involve the use of RC4 or MD5, algorithms with known + * issues or small key/digest sizes. But even weak ciphers are difficult to + * exploit in the real world -- you usually need a lot of data and a lot of + * compute time. No, virtually EVERY security vulnerability you will find + * involves a problem with the IMPLEMENTATION not with the cipher. + * + * A flaw in ZeroTier's protocol or code is incredibly, unbelievably + * more likely than a flaw in Salsa20 or any other cipher or cryptographic + * primitive it uses. We're talking odds of dying in a car wreck vs. odds of + * being personally impacted on the head by a meteorite. Nobody without a + * billion dollar budget is going to break into your network by actually + * cracking Salsa20/12 (or even /8) in the field. + * + * So stop worrying about the cipher unless you are, say, the Kremlin and your + * adversary is the NSA and the GCHQ. In that case... well that's above my + * pay grade. I'll just say defense in depth. */ #define ZT_PROTO_SALSA20_ROUNDS 12 -// Indices of fields in normal packet header -- do not change as this -// might require both code rework and will break compatibility. +// Field indexes in packet header #define ZT_PACKET_IDX_IV 0 #define ZT_PACKET_IDX_DEST 8 #define ZT_PACKET_IDX_SOURCE 13 @@ -147,16 +203,19 @@ /** * Packet buffer size (can be changed) + * + * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic + * packet fragment limit, times the default UDP MTU. Most packets won't be + * this big. */ #define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU) /** - * Minimum viable packet length (also length of header) + * Minimum viable packet length (a.k.a. header length) */ #define ZT_PROTO_MIN_PACKET_LENGTH ZT_PACKET_IDX_PAYLOAD -// Indexes of fields in fragment header -- also can't be changed without -// breaking compatibility. +// Indexes of fields in fragment header #define ZT_PACKET_FRAGMENT_IDX_PACKET_ID 0 #define ZT_PACKET_FRAGMENT_IDX_DEST 8 #define ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR 13 @@ -165,7 +224,7 @@ #define ZT_PACKET_FRAGMENT_IDX_PAYLOAD 16 /** - * Value found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR in fragments + * Magic number found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR */ #define ZT_PACKET_FRAGMENT_INDICATOR ZT_ADDRESS_RESERVED_PREFIX @@ -174,24 +233,17 @@ */ #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD -/** - * Length of LAN beacon packets - */ -#define ZT_PROTO_BEACON_LENGTH 13 - -/** - * Index of address in a LAN beacon - */ -#define ZT_PROTO_BEACON_IDX_ADDRESS 8 - -// Destination address types from HELLO and OK(HELLO) +// 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_ETHERNET 1 +#define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1 // reserved but unused +#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 2 // future use +#define ZT_PROTO_DEST_ADDRESS_TYPE_BLUETOOTH 3 // future use #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4 +#define ZT_PROTO_DEST_ADDRESS_TYPE_LTE_DIRECT 5 // future use #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6 // Ephemeral key record flags -#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 +#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01 // future use // Ephemeral key record symmetric cipher types #define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_SALSA2012_POLY1305 0x01 @@ -326,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<ZT_PROTO_MAX_PACKET_LENGTH> { @@ -636,7 +678,8 @@ public: * <[...] serialized certificate of membership> * [ ... additional certificates may follow ...] * - * Certificate contains network ID, peer it was issued for, etc. + * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may + * be pushed at any other time to keep exchanged certificates up to date. * * OK/ERROR are not generated. */ @@ -680,10 +723,8 @@ public: /* Network configuration refresh request: * <[...] array of 64-bit network IDs> * - * This message can be sent by the network configuration master node - * to request that nodes refresh their network configuration. It can - * thus be used to "push" updates so that network config changes will - * take effect quickly. + * This can be sent by the network controller to inform a node that it + * should now make a NETWORK_CONFIG_REQUEST. * * It does not generate an OK or ERROR message, and is treated only as * a hint to refresh now. @@ -769,7 +810,7 @@ public: */ VERB_MULTICAST_FRAME = 14, - /* Ephemeral (PFS) key push: + /* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET) * <[2] flags (unused and reserved, must be 0)> * <[2] length of padding / extra field section> * <[...] padding / extra field section> @@ -826,21 +867,51 @@ public: VERB_SET_EPHEMERAL_KEY = 15, /* Push of potential endpoints for direct communication: + * <[2] 16-bit number of paths> + * <[...] paths> + * + * Path record format: * <[1] flags> - * <[2] number of addresses> - * <[...] address types and addresses> + * <[1] metric from 0 (highest priority) to 255 (lowest priority)> + * <[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: + * 0x01 - Forget this path if it is currently known + * 0x02 - Blacklist this path, do not use + * 0x04 - Reliable path (no NAT keepalives, etc. are necessary) + * 0x08 - Disable encryption (trust: privacy) + * 0x10 - Disable encryption and authentication (trust: ultimate) * * Address types and addresses are of the same format as the destination * address type and address in HELLO. * * The receiver may, upon receiving a push, attempt to establish a - * direct link to one or more of the indicated addresses. Senders should - * only send address pushes to peers that they have some relationship - * with such as a shared network membership or a mutual trust. + * direct link to one or more of the indicated addresses. It is the + * responsibility of the sender to limit which peers it pushes direct + * paths to to those with whom it has a trust relationship. The receiver + * must obey any restrictions provided such as exclusivity or blacklists. + * OK responses to this message are optional. * - * OK/ERROR are not generated. + * Note that a direct path push does not imply that learned paths can't + * be used unless they are blacklisted explicitly or unless flag 0x01 + * is set. + * + * Only a subset of this functionality is currently implemented: basic + * path pushing and learning. Metrics, most flags, and OK responses are + * not yet implemented as of 1.0.4. + * + * OK response payload: + * <[2] 16-bit number of active direct paths we already have> + * <[2] 16-bit number of paths in push that we don't already have> + * <[2] 16-bit number of new paths we are trying (or will try)> + * + * ERROR is presently not sent. */ - VERB_PHYSICAL_ADDRESS_PUSH = 16 + VERB_PUSH_DIRECT_PATHS = 16 }; /** diff --git a/node/Path.hpp b/node/Path.hpp index 393b7225..cd21444b 100644 --- a/node/Path.hpp +++ b/node/Path.hpp @@ -28,144 +28,65 @@ #ifndef ZT_PATH_HPP #define ZT_PATH_HPP -#include <stdint.h> -#include <string.h> - -#include <stdexcept> -#include <string> -#include <algorithm> - #include "Constants.hpp" -#include "Node.hpp" #include "InetAddress.hpp" #include "Utils.hpp" -#include "AntiRecursion.hpp" -#include "RuntimeEnvironment.hpp" namespace ZeroTier { -/** - * WAN address and protocol for reaching a peer - * - * This structure is volatile and memcpy-able, and depends on - * InetAddress being similarly safe. - */ class Path { public: - Path() : - _addr(), - _lastSend(0), - _lastReceived(0), - _fixed(false) {} - - Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); } - - Path(const InetAddress &addr,bool fixed) : - _addr(addr), - _lastSend(0), - _lastReceived(0), - _fixed(fixed) {} - - inline void init(const InetAddress &addr,bool fixed) - { - _addr = addr; - _lastSend = 0; - _lastReceived = 0; - _fixed = fixed; - } - - inline Path &operator=(const Path &p) + // Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h + enum Trust { - if (this != &p) - memcpy(this,&p,sizeof(Path)); - return *this; - } - - inline const InetAddress &address() const throw() { return _addr; } - - inline uint64_t lastSend() const throw() { return _lastSend; } - inline uint64_t lastReceived() const throw() { return _lastReceived; } + TRUST_NORMAL = 0, + TRUST_PRIVACY = 1, + TRUST_ULTIMATE = 2 + }; - /** - * Called when a packet is sent to this path - * - * This is called automatically by Path::send(). - * - * @param t Time of send - */ - inline void sent(uint64_t t) - throw() + Path() : + _addr(), + _metric(0), + _trust(TRUST_NORMAL), + _reliable(false) { - _lastSend = t; } - /** - * Called when a packet is received from this path - * - * @param t Time of receive - */ - inline void received(uint64_t t) - throw() + Path(const InetAddress &addr,int metric,Trust trust,bool reliable) : + _addr(addr), + _metric(metric), + _trust(trust), + _reliable(reliable) { - _lastReceived = t; } /** - * @return Is this a fixed path? + * @return Physical address */ - inline bool fixed() const throw() { return _fixed; } + inline const InetAddress &address() const throw() { return _addr; } /** - * @param f New value of fixed path flag + * @return Metric (higher == worse) or negative if path is blacklisted */ - inline void setFixed(bool f) throw() { _fixed = f; } + inline int metric() const throw() { return _metric; } /** - * @param now Current time - * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms + * @return Path trust level */ - inline bool active(uint64_t now) const - throw() - { - return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); - } + inline Trust trust() const throw() { return _trust; } /** - * Send a packet via this path - * - * @param RR Runtime environment - * @param data Packet data - * @param len Packet length - * @param now Current time - * @return True if transport reported success + * @return True if path is considered reliable (no NAT keepalives etc. are needed) */ - inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) - { - if (RR->node->putPacket(_addr,data,len)) { - sent(now); - RR->antiRec->logOutgoingZT(data,len); - return true; - } - return false; - } + inline bool reliable() const throw() { return _reliable; } /** - * @param now Current time - * @return Human-readable address and other information about this path + * @return True if address is non-NULL */ - inline std::string toString(uint64_t now) const - { - char tmp[1024]; - Utils::snprintf(tmp,sizeof(tmp),"%s(%s)", - _addr.toString().c_str(), - ((_fixed) ? "fixed" : (active(now) ? "active" : "inactive")) - ); - return std::string(tmp); - } - inline operator bool() const throw() { return (_addr); } + // Comparisons are by address only inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); } inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); } inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); } @@ -173,11 +94,44 @@ 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); } -private: + /** + * 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; - uint64_t _lastSend; - uint64_t _lastReceived; - bool _fixed; + int _metric; // negative == blacklisted + Trust _trust; + bool _reliable; }; } // namespace ZeroTier diff --git a/node/Peer.cpp b/node/Peer.cpp index 96caa72c..84aa8bef 100644 --- a/node/Peer.cpp +++ b/node/Peer.cpp @@ -46,6 +46,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity) _lastMulticastFrame(0), _lastAnnouncedTo(0), _lastPathConfirmationSent(0), + _lastDirectPathPush(0), _vMajor(0), _vMinor(0), _vRevision(0), @@ -86,7 +87,7 @@ void Peer::received( if (!pathIsConfirmed) { if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) { // Learn paths if they've been confirmed via a HELLO - Path *slot = (Path *)0; + RemotePath *slot = (RemotePath *)0; if (np < ZT1_MAX_PEER_NETWORK_PATHS) { // Add new path slot = &(_paths[np++]); @@ -101,7 +102,7 @@ void Peer::received( } } if (slot) { - slot->init(remoteAddr,false); + *slot = RemotePath(remoteAddr,false); slot->received(now); _numPaths = np; pathIsConfirmed = true; @@ -193,7 +194,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &at void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) { - Path *const bestPath = getBestPath(now); + RemotePath *const bestPath = getBestPath(now); if ((bestPath)&&(bestPath->active(now))) { if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) { TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str()); @@ -207,7 +208,73 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now) } } -void Peer::addPath(const Path &newp) +void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force) +{ + if ((((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) { + _lastDirectPathPush = now; + + std::vector<Path> dps(RR->node->directPaths()); + TRACE("pushing %u direct paths (local interface addresses) to %s",(unsigned int)dps.size(),_id.address().toString().c_str()); + + std::vector<Path>::const_iterator p(dps.begin()); + while (p != dps.end()) { + Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS); + outp.addSize(2); // leave room for count + + unsigned int count = 0; + while ((p != dps.end())&&((outp.size() + 24) < ZT_PROTO_MAX_PACKET_LENGTH)) { + uint8_t addressType = 4; + switch(p->address().ss_family) { + case AF_INET: + break; + case AF_INET6: + addressType = 6; + break; + default: + ++p; + continue; + } + + uint8_t flags = 0; + if (p->metric() < 0) + flags |= (0x01 | 0x02); // forget and blacklist + else { + if (p->reliable()) + flags |= 0x04; // no NAT keepalives and such + switch(p->trust()) { + default: + break; + case Path::TRUST_PRIVACY: + flags |= 0x08; // no encryption + break; + case Path::TRUST_ULTIMATE: + flags |= (0x08 | 0x10); // no encryption, no authentication (redundant but go ahead and set both) + break; + } + } + + outp.append(flags); + outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0)); + outp.append((uint16_t)0); + outp.append(addressType); + outp.append((uint8_t)((addressType == 4) ? 6 : 18)); + outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16)); + outp.append((uint16_t)p->address().port()); + + ++count; + ++p; + } + + if (count) { + outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count); + outp.armor(_key,true); + path->send(RR,outp.data(),outp.size(),now); + } + } + } +} + +void Peer::addPath(const RemotePath &newp) { unsigned int np = _numPaths; @@ -218,7 +285,7 @@ void Peer::addPath(const Path &newp) } } - Path *slot = (Path *)0; + RemotePath *slot = (RemotePath *)0; if (np < ZT1_MAX_PEER_NETWORK_PATHS) { // Add new path slot = &(_paths[np++]); diff --git a/node/Peer.hpp b/node/Peer.hpp index 8d8b7cb4..f5118794 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -40,7 +40,7 @@ #include "../include/ZeroTierOne.h" #include "RuntimeEnvironment.hpp" -#include "Path.hpp" +#include "RemotePath.hpp" #include "Address.hpp" #include "Utils.hpp" #include "Identity.hpp" @@ -53,11 +53,7 @@ namespace ZeroTier { /** - * Peer on P2P Network - * - * This struture is not locked, volatile, and memcpy-able. NonCopyable - * semantics are just there to prevent bugs, not because it isn't safe - * to copy. + * Peer on P2P Network (virtual layer 1) */ class Peer : NonCopyable { @@ -130,9 +126,9 @@ public: * @param now Current time * @return Best path or NULL if there are no active (or fixed) direct paths */ - inline Path *getBestPath(uint64_t now) + inline RemotePath *getBestPath(uint64_t now) { - Path *bestPath = (Path *)0; + RemotePath *bestPath = (RemotePath *)0; uint64_t lrMax = 0; for(unsigned int p=0,np=_numPaths;p<np;++p) { if ((_paths[p].active(now))&&(_paths[p].lastReceived() >= lrMax)) { @@ -152,14 +148,14 @@ public: * @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) + inline RemotePath *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) { - Path *bestPath = getBestPath(now); + RemotePath *bestPath = getBestPath(now); if (bestPath) { if (bestPath->send(RR,data,len,now)) return bestPath; } - return (Path *)0; + return (RemotePath *)0; } /** @@ -183,11 +179,21 @@ public: void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now); /** + * Push direct paths if we haven't done so in [rate limit] milliseconds + * + * @param RR Runtime environment + * @param path Remote path to use to send the push + * @param now Current time + * @param force If true, push regardless of rate limit + */ + void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force); + + /** * @return All known direct paths to this peer */ - inline std::vector<Path> paths() const + inline std::vector<RemotePath> paths() const { - std::vector<Path> pp; + std::vector<RemotePath> pp; for(unsigned int p=0,np=_numPaths;p<np;++p) pp.push_back(_paths[p]); return pp; @@ -295,7 +301,7 @@ public: * * @param p New path to add */ - void addPath(const Path &newp); + void addPath(const RemotePath &newp); /** * Clear paths @@ -412,12 +418,13 @@ private: uint64_t _lastMulticastFrame; uint64_t _lastAnnouncedTo; uint64_t _lastPathConfirmationSent; + uint64_t _lastDirectPathPush; uint16_t _vProto; uint16_t _vMajor; uint16_t _vMinor; uint16_t _vRevision; Identity _id; - Path _paths[ZT1_MAX_PEER_NETWORK_PATHS]; + RemotePath _paths[ZT1_MAX_PEER_NETWORK_PATHS]; unsigned int _numPaths; unsigned int _latency; diff --git a/node/RemotePath.hpp b/node/RemotePath.hpp new file mode 100644 index 00000000..5592c8e1 --- /dev/null +++ b/node/RemotePath.hpp @@ -0,0 +1,142 @@ +/* + * 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 <http://www.gnu.org/licenses/>. + * + * -- + * + * 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_REMOTEPATH_HPP +#define ZT_REMOTEPATH_HPP + +#include <stdint.h> +#include <string.h> + +#include <stdexcept> +#include <algorithm> + +#include "Path.hpp" +#include "Node.hpp" +#include "AntiRecursion.hpp" +#include "RuntimeEnvironment.hpp" + +namespace ZeroTier { + +/** + * Path to a remote peer + * + * This extends Path to include status information about path activity. + */ +class RemotePath : public Path +{ +public: + RemotePath() : + Path(), + _lastSend(0), + _lastReceived(0), + _fixed(false) {} + + RemotePath(const InetAddress &addr,bool fixed) : + Path(addr,0,TRUST_NORMAL,false), + _lastSend(0), + _lastReceived(0), + _fixed(fixed) {} + + inline uint64_t lastSend() const throw() { return _lastSend; } + inline uint64_t lastReceived() const throw() { return _lastReceived; } + + /** + * @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() + { + _fixed = f; + } + + /** + * Called when a packet is sent to this remote path + * + * This is called automatically by RemotePath::send(). + * + * @param t Time of send + */ + inline void sent(uint64_t t) + throw() + { + _lastSend = t; + } + + /** + * Called when a packet is received from this remote path + * + * @param t Time of receive + */ + inline void received(uint64_t t) + throw() + { + _lastReceived = t; + } + + /** + * @param now Current time + * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms + */ + inline bool active(uint64_t now) const + throw() + { + return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) ); + } + + /** + * Send a packet via this path + * + * @param RR Runtime environment + * @param data Packet data + * @param len Packet length + * @param now Current time + * @return True if transport reported success + */ + inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now) + { + if (RR->node->putPacket(_addr,data,len)) { + sent(now); + RR->antiRec->logOutgoingZT(data,len); + return true; + } + return false; + } + +private: + uint64_t _lastSend; + uint64_t _lastReceived; + bool _fixed; +}; + +} // namespace ZeroTier + +#endif 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/SelfAwareness.cpp b/node/SelfAwareness.cpp index 9f7c41d7..00015788 100644 --- a/node/SelfAwareness.cpp +++ b/node/SelfAwareness.cpp @@ -120,7 +120,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi // they are still considered alive so that we will re-establish direct links. SharedPtr<Peer> sn(RR->topology->getBestRoot()); if (sn) { - Path *snp = sn->getBestPath(now); + RemotePath *snp = sn->getBestPath(now); if (snp) { for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) { if ((*p)->alive(now)) { diff --git a/node/Switch.cpp b/node/Switch.cpp index 236c1e66..4fd5d769 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -42,15 +42,30 @@ #include "InetAddress.hpp" #include "Topology.hpp" #include "Peer.hpp" -#include "CMWC4096.hpp" #include "AntiRecursion.hpp" #include "Packet.hpp" namespace ZeroTier { +#ifdef ZT_TRACE +static const char *etherTypeName(const unsigned int etherType) +{ + switch(etherType) { + case ZT_ETHERTYPE_IPV4: return "IPV4"; + case ZT_ETHERTYPE_ARP: return "ARP"; + case ZT_ETHERTYPE_RARP: return "RARP"; + case ZT_ETHERTYPE_ATALK: return "ATALK"; + case ZT_ETHERTYPE_AARP: return "AARP"; + case ZT_ETHERTYPE_IPX_A: return "IPX_A"; + case ZT_ETHERTYPE_IPX_B: return "IPX_B"; + case ZT_ETHERTYPE_IPV6: return "IPV6"; + } + return "UNKNOWN"; +} +#endif // ZT_TRACE + Switch::Switch(const RuntimeEnvironment *renv) : - RR(renv), - _lastBeacon(0) + RR(renv) { } @@ -61,9 +76,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<ZT_PROTO_BEACON_LENGTH>(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) { @@ -165,42 +178,34 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c if (to[0] == MAC::firstOctetForNetwork(network->id())) { // Destination is another ZeroTier peer on the same network - Address toZT(to.toAddress(network->id())); - if (network->isAllowed(toZT)) { - if (network->peerNeedsOurMembershipCertificate(toZT,RR->node->now())) { - // TODO: once there are no more <1.0.0 nodes around, we can - // bundle this with EXT_FRAME instead of sending two packets. - Packet outp(toZT,RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); + Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this + const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now()); + if ((fromBridged)||(includeCom)) { + Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); + outp.append(network->id()); + if (includeCom) { + outp.append((unsigned char)0x01); // 0x01 -- COM included nconf->com().serialize(outp); - send(outp,true,network->id()); - } - - if (fromBridged) { - // EXT_FRAME is used for bridging or if we want to include a COM - Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME); - outp.append(network->id()); - outp.append((unsigned char)0); - to.appendTo(outp); - from.appendTo(outp); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); } else { - // FRAME is a shorter version that can be used when there's no bridging and no COM - Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); - outp.append(network->id()); - outp.append((uint16_t)etherType); - outp.append(data,len); - outp.compress(); - send(outp,true,network->id()); + outp.append((unsigned char)0x00); } - - //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged); + to.appendTo(outp); + from.appendTo(outp); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } else { - TRACE("%.16llx: UNICAST: %s -> %s etherType==%s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType)); + Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME); + outp.append(network->id()); + outp.append((uint16_t)etherType); + outp.append(data,len); + outp.compress(); + send(outp,true,network->id()); } + //TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom); + return; } @@ -210,22 +215,19 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c Address bridges[ZT_MAX_BRIDGE_SPAM]; unsigned int numBridges = 0; + /* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */ bridges[0] = network->findBridgeTo(to); - if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) { - // We have a known bridge route for this MAC. + if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->permitsBridging(bridges[0]))) { + /* We have a known bridge route for this MAC, send it there. */ ++numBridges; } else if (!nconf->activeBridges().empty()) { /* If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active - * bridges. This is similar to what many switches do -- if they do not - * know which port corresponds to a MAC, they send it to all ports. If - * there aren't any active bridges, numBridges will stay 0 and packet - * is dropped. */ + * bridges. If someone responds, we'll learn the route. */ std::vector<Address>::const_iterator ab(nconf->activeBridges().begin()); if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) { // If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all while (ab != nconf->activeBridges().end()) { - if (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + bridges[numBridges++] = *ab; ++ab; } } else { @@ -233,9 +235,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &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 (network->isAllowed(*ab)) // config sanity check - bridges[numBridges++] = *ab; + if (((unsigned long)RR->node->prng() % (unsigned long)nconf->activeBridges().size()) == 0) { + bridges[numBridges++] = *ab; ++ab; } else ++ab; } @@ -245,7 +246,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c for(unsigned int b=0;b<numBridges;++b) { Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME); outp.append(network->id()); - outp.append((unsigned char)0); + if (network->peerNeedsOurMembershipCertificate(bridges[b],RR->node->now())) { + outp.append((unsigned char)0x01); // 0x01 -- COM included + nconf->com().serialize(outp); + } else { + outp.append((unsigned char)0); + } to.appendTo(outp); from.appendTo(outp); outp.append((uint16_t)etherType); @@ -320,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) { @@ -529,22 +535,6 @@ unsigned long Switch::doTimerTasks(uint64_t now) return nextDelay; } -const char *Switch::etherTypeName(const unsigned int etherType) - throw() -{ - switch(etherType) { - case ZT_ETHERTYPE_IPV4: return "IPV4"; - case ZT_ETHERTYPE_ARP: return "ARP"; - case ZT_ETHERTYPE_RARP: return "RARP"; - case ZT_ETHERTYPE_ATALK: return "ATALK"; - case ZT_ETHERTYPE_AARP: return "AARP"; - case ZT_ETHERTYPE_IPX_A: return "IPX_A"; - case ZT_ETHERTYPE_IPX_B: return "IPX_B"; - case ZT_ETHERTYPE_IPV6: return "IPV6"; - } - return "UNKNOWN"; -} - void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len) { Packet::Fragment fragment(data,len); @@ -687,23 +677,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat } } -void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEACON_LENGTH> &data) -{ - Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - if (beaconAddr == RR->identity.address()) - return; - SharedPtr<Peer> 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<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false)); @@ -724,32 +697,43 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (peer) { const uint64_t now = RR->node->now(); - Path *viaPath = peer->getBestPath(now); + SharedPtr<Network> network; + SharedPtr<NetworkConfig> nconf; + if (nwid) { + network = RR->node->network(nwid); + if (!network) + return false; // we probably just left this network, let its packets die + nconf = network->config2(); + if (!nconf) + return false; // sanity check: unconfigured network? why are we trying to talk to it? + } + + RemotePath *viaPath = peer->getBestPath(now); + SharedPtr<Peer> relay; if (!viaPath) { - SharedPtr<Peer> relay; - - if (nwid) { - SharedPtr<Network> network(RR->node->network(nwid)); - if (network) { - SharedPtr<NetworkConfig> nconf(network->config2()); - if (nconf) { - unsigned int latency = ~((unsigned int)0); - for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { - if (r->first != peer->address()) { - SharedPtr<Peer> rp(RR->topology->getPeer(r->first)); - if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) - rp.swap(relay); - } - } + // See if this network has a preferred relay (if packet has an associated network) + if (nconf) { + unsigned int latency = ~((unsigned int)0); + for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) { + if (r->first != peer->address()) { + SharedPtr<Peer> rp(RR->topology->getPeer(r->first)); + if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency)) + rp.swap(relay); } } } + // Otherwise relay off a root server if (!relay) relay = RR->topology->getBestRoot(); if (!(relay)||(!(viaPath = relay->getBestPath(now)))) - return false; + return false; // no paths, no root servers? + } + + if ((network)&&(relay)&&(network->isAllowed(peer->address()))) { + // Push hints for direct connectivity to this peer if we are relaying + peer->pushDirectPaths(RR,viaPath,now,false); } Packet tmp(packet); @@ -761,7 +745,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) if (viaPath->send(RR,tmp.data(),chunkSize,now)) { if (chunkSize < tmp.size()) { - // Too big for one bite, fragment the rest + // Too big for one packet, fragment the rest unsigned int fragStart = chunkSize; unsigned int remaining = tmp.size() - chunkSize; unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH)); @@ -777,6 +761,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid) remaining -= chunkSize; } } + return true; } } else { diff --git a/node/Switch.hpp b/node/Switch.hpp index 0ba4c138..89c9a56b 100644 --- a/node/Switch.hpp +++ b/node/Switch.hpp @@ -108,10 +108,14 @@ public: * * Needless to say, the packet's source must be this node. Otherwise it * won't be encrypted right. (This is not used for relaying.) + * + * The network ID should only be specified for frames and other actual + * network traffic. Other traffic such as controller requests and regular + * protocol messages should specify zero. * * @param packet Packet to send * @param encrypt Encrypt packet payload? (always true except for HELLO) - * @param nwid Network ID or 0 if message is not related to a specific network + * @param nwid Related network ID or 0 if message is not in-network traffic */ void send(const Packet &packet,bool encrypt,uint64_t nwid); @@ -173,22 +177,13 @@ public: */ unsigned long doTimerTasks(uint64_t now); - /** - * @param etherType Ethernet type ID - * @return Human-readable name - */ - static const char *etherTypeName(const unsigned int etherType) - throw(); - 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<ZT_PROTO_BEACON_LENGTH> &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 diff --git a/node/Topology.cpp b/node/Topology.cpp index 2b1cc31f..b255080e 100644 --- a/node/Topology.cpp +++ b/node/Topology.cpp @@ -62,7 +62,7 @@ void Topology::setRootServers(const std::map< Identity,std::vector<InetAddress> if (!p) p = SharedPtr<Peer>(new Peer(RR->identity,i->first)); for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j) - p->addPath(Path(*j,true)); + p->addPath(RemotePath(*j,true)); p->use(now); _rootPeers.push_back(p); } diff --git a/node/Utils.hpp b/node/Utils.hpp index bd567cf5..70918eb5 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -60,20 +60,10 @@ public: static inline bool secureEq(const void *a,const void *b,unsigned int len) throw() { - const char *p1 = (const char *)a; - const char *p2 = (const char *)b; - uint64_t diff = 0; - - while (len >= 8) { - diff |= (*((const uint64_t *)p1) ^ *((const uint64_t *)p2)); - p1 += 8; - p2 += 8; - len -= 8; - } - while (len--) - diff |= (uint64_t)(*p1++ ^ *p2++); - - return (diff == 0ULL); + char diff = 0; + for(unsigned int i=0;i<len;++i) + diff |= ( (reinterpret_cast<const char *>(a))[i] ^ (reinterpret_cast<const char *>(b))[i] ); + return (diff == 0); } /** |