diff --git a/attic/MulticastGroup.hpp b/attic/MulticastGroup.hpp new file mode 100644 index 000000000..e77f96d04 --- /dev/null +++ b/attic/MulticastGroup.hpp @@ -0,0 +1,152 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_MULTICASTGROUP_HPP +#define ZT_MULTICASTGROUP_HPP + +#include + +#include "Constants.hpp" +#include "MAC.hpp" +#include "InetAddress.hpp" +#include "Utils.hpp" + +namespace ZeroTier { + +/** + * A multicast group composed of a multicast MAC and a 32-bit ADI field + * + * ADI stands for additional distinguishing information. ADI is primarily for + * adding additional information to broadcast (ff:ff:ff:ff:ff:ff) memberships, + * since straight-up broadcast won't scale. Right now it's zero except for + * IPv4 ARP, where it holds the IPv4 address itself to make ARP into a + * selective multicast query that can scale. + * + * In the future we might add some kind of plugin architecture that can add + * ADI for things like mDNS (multicast DNS) to improve the selectivity of + * those protocols. + * + * MulticastGroup behaves as an immutable value object. + */ +class MulticastGroup +{ +public: + inline MulticastGroup() : + _mac(), + _adi(0) + { + } + + inline MulticastGroup(const MAC &m,uint32_t a) : + _mac(m), + _adi(a) + { + } + + /** + * Derive the multicast group used for address resolution (ARP/NDP) for an IP + * + * @param ip IP address (port field is ignored) + * @return Multicast group for ARP/NDP + */ + static inline MulticastGroup deriveMulticastGroupForAddressResolution(const InetAddress &ip) + { + if (ip.isV4()) { + // IPv4 wants broadcast MACs, so we shove the V4 address itself into + // the Multicast Group ADI field. Making V4 ARP work is basically why + // ADI was added, as well as handling other things that want mindless + // Ethernet broadcast to all. + return MulticastGroup(MAC(0xffffffffffffULL),Utils::ntoh(*((const uint32_t *)ip.rawIpData()))); + } else if (ip.isV6()) { + // IPv6 is better designed in this respect. We can compute the IPv6 + // multicast address directly from the IP address, and it gives us + // 24 bits of uniqueness. Collisions aren't likely to be common enough + // to care about. + const unsigned char *a = (const unsigned char *)ip.rawIpData(); + return MulticastGroup(MAC(0x33,0x33,0xff,a[13],a[14],a[15]),0); + } + return MulticastGroup(); + } + + /** + * @return Multicast address + */ + inline const MAC &mac() const { return _mac; } + + /** + * @return Additional distinguishing information + */ + inline uint32_t adi() const { return _adi; } + + /** + * Compute a 32-bit randomized identifier for this group + * + * This is a 32-bit fnv1a hash of the MAC and ADI. It's part of the protocol as it's + * used to generate unique identifiers for multicast groups for multicast lookup, so + * don't change it lightly. + */ + inline uint32_t id32() const + { + uint32_t h = 0x811c9dc5; + const uint64_t m = _mac.toInt(); + const uint32_t p = 0x1000193; + h ^= (uint32_t)(m >> 40) & 0xff; h *= p; + h ^= (uint32_t)(m >> 32) & 0xff; h *= p; + h ^= (uint32_t)(m >> 24) & 0xff; h *= p; + h ^= (uint32_t)(m >> 16) & 0xff; h *= p; + h ^= (uint32_t)(m >> 8) & 0xff; h *= p; + h ^= (uint32_t)m & 0xff; h *= p; + h ^= _adi >> 24; h *= p; + h ^= (_adi >> 16) & 0xff; h *= p; + h ^= (_adi >> 8) & 0xff; h *= p; + h ^= _adi & 0xff; h *= p; + return h; + } + + inline unsigned long hashCode() const { return (_mac.hashCode() + (unsigned long)_adi); } + + inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); } + inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); } + inline bool operator<(const MulticastGroup &g) const + { + if (_mac < g._mac) + return true; + else if (_mac == g._mac) + return (_adi < g._adi); + return false; + } + inline bool operator>(const MulticastGroup &g) const { return (g < *this); } + inline bool operator<=(const MulticastGroup &g) const { return !(g < *this); } + inline bool operator>=(const MulticastGroup &g) const { return !(*this < g); } + +private: + MAC _mac; + uint32_t _adi; +}; + +} // namespace ZeroTier + +#endif diff --git a/attic/MulticastSubscriptions.hpp b/attic/MulticastSubscriptions.hpp new file mode 100644 index 000000000..0a73150a4 --- /dev/null +++ b/attic/MulticastSubscriptions.hpp @@ -0,0 +1,123 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_MULTICASTSUBSCRIPTIONS_HPP +#define ZT_MULTICASTSUBSCRIPTIONS_HPP + +#include "Constants.hpp" +#include "MulticastGroup.hpp" +#include "Identity.hpp" +#include "Buffer.hpp" + +namespace ZeroTier { + +/** + * A compact collection of multicast subscriptions identified by 32-bit hash values + */ +class MulticastSubscriptions +{ +public: + inline MulticastSubscriptions() : _signatureLength(0) {} + + inline void add(const MulticastGroup &mg) + { + if (_subscriptions.size() < ZT_MAX_MULTICAST_SUBSCRIPTIONS) + _subscriptions.push_back(mg.id32()); + } + + inline bool sign(const Identity &signer,const int64_t ts) + { + _ts = ts; + std::sort(_subscriptions.begin(),_subscriptions.end()); + _subscriptions.erase(std::unique(_subscriptions.begin(),_subscriptions.end()),_subscriptions.end()); + + _SRec tmp; + tmp.ts = Utils::hton((uint64_t)ts); + for(unsigned long i=0,j=(unsigned long)_subscriptions.size();i 0); + } + + inline bool verify(const Identity &signer) + { + if ((_signatureLength == 0)||(_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)) + return false; + _SRec tmp; + tmp.ts = Utils::hton((uint64_t)_ts); + for(unsigned long i=0,j=(unsigned long)_subscriptions.size();i + inline void serialize(Buffer &b) const + { + b.append((uint64_t)_ts); + b.append((uint16_t)_subscriptions.size()); + for(std::vector::const_iterator i(_subscriptions.begin());i!=_subscriptions.end();++i) + b.append(*i); + b.append((uint16_t)_signatureLength); + b.append(_signature,_signatureLength); + } + + template + inline unsigned int deserialize(const Buffer &b,unsigned int startAt = 0) + { + unsigned int p = startAt; + _ts = (int64_t)(b.template at(p)); p += 8; + _subscriptions.resize(b.template at(p)); p += 2; + for(std::vector::iterator i(_subscriptions.begin());i!=_subscriptions.end();++i) { + *i = b.template at(p); + p += 4; + } + _signatureLength = b.template at(p); p += 2; + if (_signatureLength > ZT_SIGNATURE_BUFFER_SIZE) + throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; + memcpy(_signature,b.field(p,_signatureLength),_signatureLength); p += _signatureLength; + return (p - startAt); + } + +private: + ZT_PACKED_STRUCT(struct _SRec { + uint64_t ts; + uint32_t g[ZT_MAX_MULTICAST_SUBSCRIPTIONS]; + }); + + int64_t _ts; + std::vector _subscriptions; + unsigned int _signatureLength; + uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE]; +}; + +} // namespace ZeroTier + +#endif diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h index 2da12598f..8972a4507 100644 --- a/include/ZeroTierOne.h +++ b/include/ZeroTierOne.h @@ -130,7 +130,7 @@ extern "C" { /** * Maximum number of multicast group subscriptions per network */ -#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 4096 +#define ZT_MAX_NETWORK_MULTICAST_SUBSCRIPTIONS 2048 /** * Rules engine revision ID, which specifies rules engine capabilities diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp index a3feff8b7..00d05de45 100644 --- a/node/Hashtable.hpp +++ b/node/Hashtable.hpp @@ -383,26 +383,11 @@ public: private: template - static inline unsigned long _hc(const O &obj) - { - return (unsigned long)obj.hashCode(); - } - static inline unsigned long _hc(const uint64_t i) - { - return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses - } - static inline unsigned long _hc(const uint32_t i) - { - return ((unsigned long)i * (unsigned long)0x9e3779b1); - } - static inline unsigned long _hc(const uint16_t i) - { - return ((unsigned long)i * (unsigned long)0x9e3779b1); - } - static inline unsigned long _hc(const int i) - { - return ((unsigned long)i * (unsigned long)0x9e3379b1); - } + static inline unsigned long _hc(const O &obj) { return (unsigned long)obj.hashCode(); } + static inline unsigned long _hc(const uint64_t i) { return (unsigned long)(i ^ (i >> 32)); } + static inline unsigned long _hc(const uint32_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); } + static inline unsigned long _hc(const uint16_t i) { return ((unsigned long)i * (unsigned long)0x9e3779b1); } + static inline unsigned long _hc(const int i) { return ((unsigned long)i * (unsigned long)0x9e3379b1); } inline void _grow() { diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index 8e3803a67..f39c78281 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -75,7 +75,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr) return _doHELLO(RR,tPtr,false); } - const SharedPtr peer(RR->topology->getPeer(tPtr,sourceAddress)); + const SharedPtr peer(RR->topology->get(sourceAddress)); if (peer) { if (!trusted) { if (!dearmor(peer->key())) { @@ -170,7 +170,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const Shar case Packet::ERROR_IDENTITY_COLLISION: // This is a trusted upstream telling us our 5-digit ID is taken. This // causes the node to generate another. - if (RR->topology->isUpstream(peer->identity())) + if (RR->topology->isRoot(peer->identity())) RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION); break; @@ -283,7 +283,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool return true; } - SharedPtr peer(RR->topology->getPeer(tPtr,id.address())); + SharedPtr peer(RR->topology->get(id.address())); if (peer) { // We already have an identity with this address -- check for collisions if (!alreadyAuthenticated) { @@ -351,7 +351,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool return true; } - peer = RR->topology->addPeer(tPtr,newPeer); + peer = RR->topology->add(newPeer); // Continue at // VALID } @@ -363,7 +363,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool if (ptr < size()) { ptr += externalSurfaceAddress.deserialize(*this,ptr); if ((externalSurfaceAddress)&&(hops() == 0)) - RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now); + RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(id),now); } // Send OK(HELLO) with an echo of the packet's timestamp and some of the same @@ -451,13 +451,13 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); if ((externalSurfaceAddress)&&(hops() == 0)) - RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now()); + RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isRoot(peer->identity()),RR->node->now()); } break; case Packet::VERB_WHOIS: - if (RR->topology->isUpstream(peer->identity())) { + if (RR->topology->isRoot(peer->identity())) { const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY); - RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr(new Peer(RR,RR->identity,id)))); + RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr(new Peer(RR,RR->identity,id)))); } break; @@ -550,9 +550,9 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const Shar bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr &peer) { - if (RR->topology->isUpstream(peer->identity())) { + if (RR->topology->isRoot(peer->identity())) { const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - const SharedPtr rendezvousWith(RR->topology->getPeer(tPtr,with)); + const SharedPtr rendezvousWith(RR->topology->get(with)); if (rendezvousWith) { const unsigned int port = at(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT); const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN]; @@ -1021,7 +1021,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr, const bool amAnchor = (std::find(anchors.begin(),anchors.end(),RR->identity.address()) != anchors.end()); for(std::list
::iterator ra(recipients.begin());ra!=recipients.end();) { - SharedPtr recipient(RR->topology->getPeer(tPtr,*ra)); + SharedPtr recipient(RR->topology->get(*ra)); if ((recipient)&&((recipient->remoteVersionProtocol() < 10)||(amAnchor))) { Packet outp(*ra,RR->identity.address(),Packet::VERB_MULTICAST_FRAME); outp.append(field(ZT_PACKET_IDX_PAYLOAD,recipientsOffset - ZT_PACKET_IDX_PAYLOAD),recipientsOffset - ZT_PACKET_IDX_PAYLOAD); diff --git a/node/InetAddress.cpp b/node/InetAddress.cpp index 15d2f8780..0439351a4 100644 --- a/node/InetAddress.cpp +++ b/node/InetAddress.cpp @@ -38,6 +38,7 @@ namespace ZeroTier { const InetAddress InetAddress::LO4((const void *)("\x7f\x00\x00\x01"),4,0); const InetAddress InetAddress::LO6((const void *)("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01"),16,0); +const InetAddress InetAddress::NIL; InetAddress::IpScope InetAddress::ipScope() const { diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp index 729b1ac5e..c733792a3 100644 --- a/node/InetAddress.hpp +++ b/node/InetAddress.hpp @@ -64,6 +64,11 @@ struct InetAddress : public sockaddr_storage */ static const InetAddress LO6; + /** + * Null address + */ + static const InetAddress NIL; + /** * IP address scope * diff --git a/node/Locator.hpp b/node/Locator.hpp index 6fb7c2e72..a26a5b1d3 100644 --- a/node/Locator.hpp +++ b/node/Locator.hpp @@ -58,10 +58,11 @@ namespace ZeroTier { class Locator { public: - inline Locator() : _signatureLength(0) {} + inline Locator() : _ts(0),_signatureLength(0) {} inline const Identity &id() const { return _id; } inline const Identity &signer() const { return ((_signedBy) ? _signedBy : _id); } + inline int64_t timestamp() const { return _ts; } inline const std::vector &phy() const { return _physical; } inline const std::vector &virt() const { return _virtual; } @@ -186,10 +187,9 @@ public: /** * Decode TXT records * - * The supplied TXT records must be sorted in ascending natural sort order prior - * to calling this method. The iterators supplied must be read iterators that - * point to string objects supporting the c_str() method, which can be Str or - * std::string. + * TXT records can be provided as an iterator over std::string, Str, or char * + * values, and TXT records can be provided in any order. Any oversize or empty + * entries will be ignored. * * This method checks the decoded locator's signature using the supplied DNS TXT * record signing public key. False is returned if the TXT records are invalid, @@ -202,11 +202,22 @@ public: uint8_t dec[256],s384[48]; Buffer<65536> *tmp = nullptr; try { - tmp = new Buffer<65536>(); + std::vector txtRecords; while (start != end) { - tmp->append(dec,Utils::b64d(start->c_str(),dec,sizeof(dec))); + try { + Str ts(start); + if (ts.length() > 2) + txtRecords.push_back(ts); + } catch ( ... ) {} // skip any records that trigger out of bounds exceptions ++start; } + if (txtRecords.empty()) + return false; + std::sort(txtRecords.begin(),txtRecords.end()); + + tmp = new Buffer<65536>(); + for(std::vector::const_iterator i(txtRecords.begin());i!=txtRecords.end();++i) + tmp->append(dec,Utils::b64d(i->c_str() + 2,dec,sizeof(dec))); if (tmp->size() <= ZT_ECC384_SIGNATURE_SIZE) { delete tmp; @@ -219,8 +230,8 @@ public: } deserialize(*tmp,0); - delete tmp; + return verify(); } catch ( ... ) { if (tmp) delete tmp; @@ -235,10 +246,10 @@ public: b.append((uint8_t)0); // version/flags, currently 0 b.append((uint64_t)_ts); - _id.serialise(b,false); + _id.serialize(b,false); if (_signedBy) { b.append((uint8_t)1); // number of signers, current max is 1 - _signedBy.serialize(b,false); + _signedBy.serialize(b,false); // be sure not to include private key! } else { b.append((uint8_t)0); // signer is _id } @@ -282,7 +293,7 @@ public: _virtual.resize(virtualCount); for(unsigned int i=0;i(p); p += 2; + _signatureLength = b.template at(p); p += 2; if (_signatureLength > ZT_SIGNATURE_BUFFER_SIZE) throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW; memcpy(_signature,b.field(p,_signatureLength),_signatureLength); diff --git a/node/MAC.hpp b/node/MAC.hpp index d0c49c0bc..da02f6409 100644 --- a/node/MAC.hpp +++ b/node/MAC.hpp @@ -191,7 +191,7 @@ public: * @param i Value from 0 to 5 (inclusive) * @return Byte at said position (address interpreted in big-endian order) */ - inline unsigned char operator[](unsigned int i) const { return (unsigned char)((_m >> (40 - (i * 8))) & 0xff); } + inline uint8_t operator[](unsigned int i) const { return (uint8_t)(_m >> (40 - (i * 8))); } /** * @return 6, which is the number of bytes in a MAC, for container compliance @@ -230,7 +230,7 @@ public: } inline MAC &operator=(const uint64_t m) { - _m = m; + _m = m & 0xffffffffffffULL; return *this; } diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp index 866d648fb..f870bee58 100644 --- a/node/MulticastGroup.hpp +++ b/node/MulticastGroup.hpp @@ -29,6 +29,7 @@ #include +#include "Constants.hpp" #include "MAC.hpp" #include "InetAddress.hpp" #include "Utils.hpp" @@ -90,17 +91,10 @@ public: return MulticastGroup(); } - /** - * @return Multicast address - */ inline const MAC &mac() const { return _mac; } - - /** - * @return Additional distinguishing information - */ inline uint32_t adi() const { return _adi; } - inline unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); } + inline unsigned long hashCode() const { return (_mac.hashCode() + (unsigned long)_adi); } inline bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); } inline bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); } diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index f64738dea..56a800a45 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -243,10 +243,6 @@ void Multicaster::send( Address explicitGatherPeers[16]; unsigned int numExplicitGatherPeers = 0; - SharedPtr bestRoot(RR->topology->getUpstreamPeer()); - if (bestRoot) - explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address(); - explicitGatherPeers[numExplicitGatherPeers++] = network->controller(); Address ac[ZT_MAX_NETWORK_SPECIALISTS]; diff --git a/node/Network.cpp b/node/Network.cpp index 0b89d487d..fb7780753 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1246,15 +1246,12 @@ void Network::clean() _multicastGroupsBehindMe.erase(*mg); } } - { Address *a = (Address *)0; Membership *m = (Membership *)0; Hashtable::Iterator i(_memberships); while (i.next(a,m)) { - if (!RR->topology->getPeerNoCache(*a)) - _memberships.erase(*a); - else m->clean(now,_config); + m->clean(now,_config); } } } @@ -1403,42 +1400,17 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const } } -void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup) +void Network::_sendUpdatesToMembers(void *tPtr) { // Assumes _lock is locked - const int64_t now = RR->node->now(); - - std::vector groups; - if (newMulticastGroup) - groups.push_back(*newMulticastGroup); - else groups = _allMulticastGroups(); - - std::vector
alwaysAnnounceTo; - - if ((newMulticastGroup)||((now - _lastAnnouncedMulticastGroupsUpstream) >= ZT_MULTICAST_ANNOUNCE_PERIOD)) { - if (!newMulticastGroup) - _lastAnnouncedMulticastGroupsUpstream = now; - - alwaysAnnounceTo = _config.alwaysContactAddresses(); - if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),controller()) == alwaysAnnounceTo.end()) - alwaysAnnounceTo.push_back(controller()); - const std::vector
upstreams(RR->topology->upstreamAddresses()); - for(std::vector
::const_iterator a(upstreams.begin());a!=upstreams.end();++a) { - if (std::find(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a) == alwaysAnnounceTo.end()) - alwaysAnnounceTo.push_back(*a); - } - std::sort(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end()); - - for(std::vector
::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a) - _announceMulticastGroupsTo(tPtr,*a,groups); - } - + const std::vector groups(_allMulticastGroups()); + _announceMulticastGroupsTo(tPtr,controller(),groups); { Address *a = (Address *)0; Membership *m = (Membership *)0; Hashtable::Iterator i(_memberships); while (i.next(a,m)) { - if ( ( m->multicastLikeGate(now) || (newMulticastGroup) ) && (m->isAllowedOnNetwork(_config)) && (!std::binary_search(alwaysAnnounceTo.begin(),alwaysAnnounceTo.end(),*a)) ) + if (m->isAllowedOnNetwork(_config)) _announceMulticastGroupsTo(tPtr,*a,groups); } } diff --git a/node/Network.hpp b/node/Network.hpp index 1f25122da..4c7af8da4 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -193,10 +193,10 @@ public: Mutex::Lock _l(_lock); if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) { _myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg); - _sendUpdatesToMembers(tPtr,&mg); + _sendUpdatesToMembers(tPtr); } } - + /** * Unsubscribe from a multicast group * @@ -295,7 +295,7 @@ public: inline void sendUpdatesToMembers(void *tPtr) { Mutex::Lock _l(_lock); - _sendUpdatesToMembers(tPtr,(const MulticastGroup *)0); + _sendUpdatesToMembers(tPtr); } /** @@ -432,7 +432,7 @@ private: ZT_VirtualNetworkStatus _status() const; void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked bool _gate(const SharedPtr &peer); - void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup); + void _sendUpdatesToMembers(void *tPtr); void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector &allMulticastGroups); std::vector _allMulticastGroups() const; diff --git a/node/NetworkConfig.hpp b/node/NetworkConfig.hpp index 2c8aea3ac..d25c67dbd 100644 --- a/node/NetworkConfig.hpp +++ b/node/NetworkConfig.hpp @@ -104,7 +104,7 @@ namespace ZeroTier { #define ZT_NETWORKCONFIG_DICT_CAPACITY (1024 + (sizeof(ZT_VirtualNetworkRule) * ZT_MAX_NETWORK_RULES) + (sizeof(Capability) * ZT_MAX_NETWORK_CAPABILITIES) + (sizeof(Tag) * ZT_MAX_NETWORK_TAGS) + (sizeof(CertificateOfOwnership) * ZT_MAX_CERTIFICATES_OF_OWNERSHIP)) // Dictionary capacity needed for max size network meta-data -#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 1024 +#define ZT_NETWORKCONFIG_METADATA_DICT_CAPACITY 2048 // Network config version #define ZT_NETWORKCONFIG_VERSION 7 diff --git a/node/Node.cpp b/node/Node.cpp index 217dd33fd..b8301c58b 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -126,7 +126,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,int64 m += sws; RR->mc = new (m) Multicaster(RR); m += mcs; - RR->topology = new (m) Topology(RR,tptr); + RR->topology = new (m) Topology(RR,RR->identity); m += topologys; RR->sa = new (m) SelfAwareness(RR); } catch ( ... ) { @@ -190,6 +190,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame( } else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND; } +/* // Function object used to traverse the peer list, check peer status, and ping // those that need pinging. struct _PingPeersThatNeedPing @@ -254,6 +255,7 @@ struct _PingPeersThatNeedPing bool online; }; +*/ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64_t *nextBackgroundTaskDeadline) { @@ -282,6 +284,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 _localControllerAuthorizations_m.unlock(); } +/* // (1) Get peers we should remain connected to and (2) get networks that need config. Hashtable< Address,std::vector > alwaysContact; RR->topology->getAlwaysContact(alwaysContact); @@ -322,6 +325,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 _online = pfunc.online; if (oldOnline != _online) postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE); +*/ } catch ( ... ) { return ZT_RESULT_FATAL_ERROR_INTERNAL; } @@ -337,7 +341,7 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,int64_t now,volatile int64 if ((now - _lastHousekeepingRun) >= ZT_HOUSEKEEPING_PERIOD) { _lastHousekeepingRun = now; try { - RR->topology->doPeriodicTasks(tptr,now); + RR->topology->doPeriodicTasks(now); RR->sa->clean(now); RR->mc->clean(now); } catch ( ... ) { @@ -454,7 +458,7 @@ ZT_PeerList *Node::peers() const p->latency = pi->second->latency(_now); if (p->latency >= 0xffff) p->latency = -1; - p->role = RR->topology->role(pi->second->identity().address()); + p->role = RR->topology->isRoot(pi->second->identity()) ? ZT_PEER_ROLE_PLANET : ZT_PEER_ROLE_LEAF; std::vector< SharedPtr > paths(pi->second->paths(_now)); SharedPtr bestp(pi->second->getAppropriatePath(_now,false)); diff --git a/node/Root.hpp b/node/Root.hpp new file mode 100644 index 000000000..0b6b4f231 --- /dev/null +++ b/node/Root.hpp @@ -0,0 +1,223 @@ +/* + * ZeroTier One - Network Virtualization Everywhere + * Copyright (C) 2011-2019 ZeroTier, Inc. https://www.zerotier.com/ + * + * 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 . + * + * -- + * + * You can be released from the requirements of the license by purchasing + * a commercial license. Buying such a license is mandatory as soon as you + * develop commercial closed-source software that incorporates or links + * directly against ZeroTier software without disclosing the source code + * of your own application. + */ + +#ifndef ZT_ROOT_HPP +#define ZT_ROOT_HPP + +#include "Constants.hpp" +#include "Str.hpp" +#include "ECC384.hpp" +#include "Locator.hpp" +#include "InetAddress.hpp" +#include "Utils.hpp" +#include "Identity.hpp" +#include "Mutex.hpp" + +namespace ZeroTier { + +/** + * A root entry pointing to a node capable of global identity lookup and indirect transit + * + * Root entries point to DNS records that contain TXT entries that decode to Locator objects + * pointing to actual root nodes. A default root identity and static addresses can also be + * provided as fallback if DNS is not available. + * + * Note that root identities can change if DNS returns a different result, but that DNS entries + * are authenticated using their own signature scheme. This allows a root DNS name to serve + * up different roots based on factors like location or relative load of different roots. + * + * It's also possible to create a root with no DNS and no DNS validator public key. This root + * will be a static entry pointing to a single root identity and set of physical addresses. + * + * This object is thread-safe and may be concurrently accessed and updated. + */ +class Root +{ +public: + inline Root() : _dnsPublicKeySize(0) {} + inline Root(const Root &r) { *this = r; } + + /** + * Create a new root entry + * + * @param dn DNS name + * @param dnspk DNS public key for record validation + * @param dnspksize Size of DNS public key (currently always the size of a NIST P-384 point compressed public key) + * @param dflId Default identity if DNS is not available + * @param dflAddrs Default IP addresses if DNS is not available + */ + template + inline Root(S dn,const uint8_t *const dnspk,const unsigned int dnspksize,const Identity &dflId,const std::vector &dflAddrs) : + _defaultIdentity(dflId), + _defaultAddresses(dflAddrs), + _dnsName(dn), + _dnsPublicKeySize(dnspksize) + { + if (dnspksize != 0) { + if (dnspksize > sizeof(_dnsPublicKey)) + throw ZT_EXCEPTION_INVALID_ARGUMENT; + memcpy(_dnsPublicKey,dnspk,dnspksize); + } + } + + inline Root &operator=(const Root &r) + { + Mutex::Lock l(_lock); + Mutex::Lock rl(r._lock); + _defaultIdentity = r._defaultIdentity; + _defaultAddresses = r._defaultAddresses; + _dnsName = r._dnsName; + _lastFetchedLocator = r._lastFetchedLocator; + _dnsPublicKeySize = r._dnsPublicKeySize; + memcpy(_dnsPublicKey,r._dnsPublicKey,_dnsPublicKeySize); + return *this; + } + + /** + * @return Current identity (either default or latest locator) + */ + inline const Identity id() const + { + Mutex::Lock l(_lock); + if (_lastFetchedLocator.id()) + return _lastFetchedLocator.id(); + return _defaultIdentity; + } + + /** + * @param id Identity to check + * @return True if identity equals this root's current identity + */ + inline bool is(const Identity &id) const + { + Mutex::Lock l(_lock); + return ((_lastFetchedLocator.id()) ? (id == _lastFetchedLocator.id()) : (id == _defaultIdentity)); + } + + /** + * @return Current ZeroTier address (either default or latest locator) + */ + inline const Address address() const + { + Mutex::Lock l(_lock); + if (_lastFetchedLocator.id()) + return _lastFetchedLocator.id().address(); + return _defaultIdentity.address(); + } + + /** + * @return DNS name for this root (or empty string if none) + */ + inline const Str dnsName() const + { + Mutex::Lock l(_lock); + return _dnsName; + } + + /** + * @return Latest locator + */ + inline Locator locator() const + { + Mutex::Lock l(_lock); + return _lastFetchedLocator; + } + + /** + * @return Timestamp of latest retrieved locator + */ + inline int64_t locatorTimestamp() const + { + Mutex::Lock l(_lock); + return _lastFetchedLocator.timestamp(); + } + + /** + * Pick a random physical address + * + * @return Physical address or InetAddress::NIL if none are available + */ + inline const InetAddress randomPhysicalAddress() const + { + Mutex::Lock l(_lock); + if (_lastFetchedLocator.phy().empty()) { + if (_defaultAddresses.empty()) + return InetAddress::NIL; + return _defaultAddresses[(unsigned long)Utils::random() % (unsigned long)_defaultAddresses.size()]; + } + return _lastFetchedLocator.phy()[(unsigned long)Utils::random() % (unsigned long)_lastFetchedLocator.phy().size()]; + } + + /** + * Update locator, returning true if new locator is valid and newer than existing + */ + inline bool updateLocator(const Locator &loc) + { + if (!loc.verify()) + return false; + Mutex::Lock l(_lock); + if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) { + _lastFetchedLocator = loc; + return true; + } + return false; + } + + /** + * Update this root's locator from a series of TXT records + */ + template + inline bool updateLocatorFromTxt(I start,I end) + { + try { + Mutex::Lock l(_lock); + if (_dnsPublicKeySize != ZT_ECC384_PUBLIC_KEY_SIZE) + return false; + Locator loc; + if (!loc.decodeTxtRecords(start,end,_dnsPublicKey)) + return false; + if ((loc.phy().size() > 0)&&(loc.timestamp() > _lastFetchedLocator.timestamp())) { + _lastFetchedLocator = loc; + return true; + } + return false; + } catch ( ... ) {} + return false; + } + +private: + Identity _defaultIdentity; + std::vector _defaultAddresses; + Str _dnsName; + Locator _lastFetchedLocator; + unsigned int _dnsPublicKeySize; + uint8_t _dnsPublicKey[ZT_ECC384_PUBLIC_KEY_SIZE]; + Mutex _lock; +}; + +} // namespace ZeroTier + +#endif diff --git a/node/Str.hpp b/node/Str.hpp index 7f2974461..a85a29035 100644 --- a/node/Str.hpp +++ b/node/Str.hpp @@ -33,6 +33,8 @@ #include "MAC.hpp" #include "InetAddress.hpp" +#include + #define ZT_STR_CAPACITY 254 namespace ZeroTier { @@ -58,6 +60,10 @@ public: _s[0] = 0; (*this) << s; } + inline Str(const std::string &s) + { + *this = s; + } inline Str &operator=(const Str &s) { @@ -71,6 +77,19 @@ public: _s[0] = 0; return ((*this) << s); } + inline Str &operator=(const std::string &s) + { + if (s.length() > ZT_STR_CAPACITY) { + _l = 0; + _s[0] = 0; + throw ZT_EXCEPTION_OUT_OF_BOUNDS; + } else { + _l = (uint8_t)s.length(); + memcpy(_s,s.data(),s.length()); + _s[s.length()] = 0; + } + return *this; + } inline char operator[](const unsigned int i) const { @@ -82,6 +101,7 @@ public: inline void clear() { _l = 0; _s[0] = 0; } inline const char *c_str() const { return _s; } inline unsigned int length() const { return (unsigned int)_l; } + inline bool empty() const { return (_l == 0); } inline iterator begin() { return (iterator)_s; } inline iterator end() { return (iterator)(_s + (unsigned long)_l); } inline const_iterator begin() const { return (const_iterator)_s; } @@ -113,6 +133,7 @@ public: } _s[(unsigned long)(_l++)] = c; _s[(unsigned long)_l] = 0; + return *this; } inline Str &operator<<(const unsigned long n) { @@ -142,6 +163,8 @@ public: return ((*this) << a.toString(tmp)); } + inline operator bool() const { return (_l != 0); } + inline bool operator==(const Str &s) const { return ((_l == s._l)&&(strcmp(_s,s._s) == 0)); } inline bool operator!=(const Str &s) const { return ((_l != s._l)||(strcmp(_s,s._s) != 0)); } inline bool operator<(const Str &s) const { return ((_l < s._l)&&(strcmp(_s,s._s) < 0)); } diff --git a/node/Switch.cpp b/node/Switch.cpp index ce4ed0280..22521d614 100644 --- a/node/Switch.cpp +++ b/node/Switch.cpp @@ -73,7 +73,7 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre return; if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localSocket,fromAddr)) return; - const SharedPtr peer(RR->topology->getPeer(tPtr,beaconAddr)); + const SharedPtr peer(RR->topology->get(beaconAddr)); if (peer) { // we'll only respond to beacons from known peers if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses _lastBeaconResponse = now; @@ -96,12 +96,13 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre // Note: we don't bother initiating NAT-t for fragments, since heads will set that off. // It wouldn't hurt anything, just redundant and unnecessary. - SharedPtr relayTo = RR->topology->getPeer(tPtr,destination); + SharedPtr relayTo = RR->topology->get(destination); if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) { // Don't know peer or no direct path -- so relay via someone upstream - relayTo = RR->topology->getUpstreamPeer(); - if (relayTo) - relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true); + // TODO + //relayTo = RR->topology->getUpstreamPeer(); + //if (relayTo) + // relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true); } } } else { @@ -163,22 +164,25 @@ void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddre Packet packet(data,len); if (packet.hops() < ZT_RELAY_MAX_HOPS) { packet.incrementHops(); - SharedPtr relayTo = RR->topology->getPeer(tPtr,destination); + SharedPtr relayTo = RR->topology->get(destination); if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) { if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { - const SharedPtr sourcePeer(RR->topology->getPeer(tPtr,source)); + const SharedPtr sourcePeer(RR->topology->get(source)); if (sourcePeer) relayTo->introduce(tPtr,now,sourcePeer); } } else { + // TODO + /* relayTo = RR->topology->getUpstreamPeer(); if ((relayTo)&&(relayTo->address() != source)) { if (relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true)) { - const SharedPtr sourcePeer(RR->topology->getPeer(tPtr,source)); + const SharedPtr sourcePeer(RR->topology->get(source)); if (sourcePeer) relayTo->introduce(tPtr,now,sourcePeer); } } + */ } } } else if ((reinterpret_cast(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) { @@ -402,7 +406,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr &network,const // Destination is another ZeroTier peer on the same network Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this - SharedPtr toPeer(RR->topology->getPeer(tPtr,toZT)); + SharedPtr toPeer(RR->topology->get(toZT)); if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId,qosBucket)) { RR->t->outgoingNetworkFrameDropped(tPtr,network,from,to,etherType,vlanId,len,"filter blocked"); @@ -763,7 +767,7 @@ void Switch::send(void *tPtr,Packet &packet,bool encrypt) } _txQueue.push_back(TXQueueEntry(dest,RR->node->now(),packet,encrypt)); } - if (!RR->topology->getPeer(tPtr,dest)) + if (!RR->topology->get(dest)) requestWhois(tPtr,RR->node->now(),dest); } } @@ -781,6 +785,8 @@ void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr) else last = now; } + // TODO + /* const SharedPtr upstream(RR->topology->getUpstreamPeer()); if (upstream) { Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS); @@ -788,6 +794,7 @@ void Switch::requestWhois(void *tPtr,const int64_t now,const Address &addr) RR->node->expectReplyTo(outp.packetId()); send(tPtr,outp,true); } + */ } void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr &peer) @@ -840,7 +847,7 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now) } else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) { _txQueue.erase(txi++); } else { - if (!RR->topology->getPeer(tPtr,txi->dest)) + if (!RR->topology->get(txi->dest)) needWhois.push_back(txi->dest); ++txi; } @@ -857,7 +864,7 @@ unsigned long Switch::doTimerTasks(void *tPtr,int64_t now) rq->timestamp = 0; } else { const Address src(rq->frag0.source()); - if (!RR->topology->getPeer(tPtr,src)) + if (!RR->topology->get(src)) requestWhois(tPtr,now,src); } } @@ -905,16 +912,19 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt) const int64_t now = RR->node->now(); const Address destination(packet.destination()); - const SharedPtr peer(RR->topology->getPeer(tPtr,destination)); + const SharedPtr peer(RR->topology->get(destination)); if (peer) { viaPath = peer->getAppropriatePath(now,false); if (!viaPath) { + // TODO + /* peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known const SharedPtr relay(RR->topology->getUpstreamPeer()); if ( (!relay) || (!(viaPath = relay->getAppropriatePath(now,false))) ) { if (!(viaPath = peer->getAppropriatePath(now,true))) return false; } + */ } } else { return false; diff --git a/node/Topology.hpp b/node/Topology.hpp index 650cb44e6..be40b438a 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -45,6 +45,7 @@ #include "Mutex.hpp" #include "InetAddress.hpp" #include "Hashtable.hpp" +#include "Root.hpp" namespace ZeroTier { @@ -56,8 +57,9 @@ class RuntimeEnvironment; class Topology { public: - inline Topology(const RuntimeEnvironment *renv,void *tPtr) : + inline Topology(const RuntimeEnvironment *renv,const Identity &myId) : RR(renv), + _myIdentity(myId), _numConfiguredPhysicalPaths(0) {} inline ~Topology() {} @@ -71,7 +73,7 @@ public: * @param peer Peer to add * @return New or existing peer (should replace 'peer') */ - inline SharedPtr addPeer(void *tPtr,const SharedPtr &peer) + inline SharedPtr add(const SharedPtr &peer) { SharedPtr np; { @@ -91,13 +93,28 @@ public: * @param zta ZeroTier address of peer * @return Peer or NULL if not found */ - inline SharedPtr getPeer(void *tPtr,const Address &zta) const + inline SharedPtr get(const Address &zta) { - if (zta == RR->identity.address()) + if (zta == _myIdentity.address()) return SharedPtr(); - Mutex::Lock _l(_peers_m); + + Mutex::Lock l1(_peers_m); const SharedPtr *const ap = _peers.get(zta); - return ((ap) ? *ap : SharedPtr()); + if (ap) + return *ap; + + Mutex::Lock l2(_roots_m); + for(std::vector::const_iterator r(_roots.begin());r!=_roots.end();++r) { + if (r->address() == zta) { + try { + SharedPtr rp(new Peer(RR,_myIdentity,r->id())); + _peers[zta] = rp; + return rp; + } catch ( ... ) {} + } + } + + return SharedPtr(); } /** @@ -107,8 +124,8 @@ public: */ inline Identity getIdentity(void *tPtr,const Address &zta) { - if (zta == RR->identity.address()) { - return RR->identity; + if (zta == _myIdentity.address()) { + return _myIdentity; } else { Mutex::Lock _l(_peers_m); const SharedPtr *const ap = _peers.get(zta); @@ -118,25 +135,6 @@ public: return Identity(); } - /** - * Get a peer only if it is presently in memory (no disk cache) - * - * This also does not update the lastUsed() time for peers, which means - * that it won't prevent them from falling out of RAM. This is currently - * used in the Cluster code to update peer info without forcing all peers - * across the entire cluster to remain in memory cache. - * - * @param zta ZeroTier address - */ - inline SharedPtr getPeerNoCache(const Address &zta) - { - Mutex::Lock _l(_peers_m); - const SharedPtr *const ap = _peers.get(zta); - if (ap) - return *ap; - return SharedPtr(); - } - /** * Get a Path object for a given local and remote physical address, creating if needed * @@ -153,36 +151,24 @@ public: return p; } - inline SharedPtr getUpstreamPeer() const + /** + * @param id Identity to check + * @return True if this identity corresponds to a root + */ + inline bool isRoot(const Identity &id) const { - // TODO - return SharedPtr(); - } - - inline bool isUpstream(const Identity &id) const - { - // TODO + Mutex::Lock l(_roots_m); + for(std::vector::const_iterator r(_roots.begin());r!=_roots.end();++r) { + if (r->is(id)) + return true; + } return false; } - inline ZT_PeerRole role(const Address &ztaddr) const - { - // TODO - return ZT_PEER_ROLE_LEAF; - } - - inline void getAlwaysContact(Hashtable< Address,std::vector > &eps) const - { - // TODO - } - - inline std::vector
upstreamAddresses() const - { - // TODO - return std::vector
(); - } - - inline void doPeriodicTasks(void *tPtr,int64_t now) + /** + * Do periodic tasks such as database cleanup + */ + inline void doPeriodicTasks(int64_t now) { { Mutex::Lock _l1(_peers_m); @@ -357,11 +343,14 @@ public: private: const RuntimeEnvironment *const RR; + const Identity _myIdentity; std::pair _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS]; unsigned int _numConfiguredPhysicalPaths; + std::vector _roots; Hashtable< Address,SharedPtr > _peers; - Mutex _peers_m; Hashtable< Path::HashKey,SharedPtr > _paths; + Mutex _roots_m; + Mutex _peers_m; Mutex _paths_m; };