Browse Source

A bunch more refactoring to rip out obsolete stuff related to old root system and general cleanup.

Adam Ierymenko 6 years ago
parent
commit
521d371b5d

+ 152 - 0
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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * 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 <stdint.h>
+
+#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

+ 123 - 0
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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * 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<j;++i)
+			tmp.g[i] = Utils::hton(_subscriptions[i]);
+
+		_signatureLength = signer.sign(&tmp,(unsigned int)((_subscriptions.size() * sizeof(uint32_t)) + sizeof(uint64_t)),_signature,sizeof(_signature));
+		return (_signatureLength > 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<j;++i)
+			tmp.g[i] = Utils::hton(_subscriptions[i]);
+		return signer.verify(&tmp,(unsigned int)((_subscriptions.size() * sizeof(uint32_t)) + sizeof(uint64_t)),_signature,_signatureLength);
+	}
+
+	inline int64_t timestamp() const { return _ts; }
+	inline unsigned int count() const { return (unsigned int)_subscriptions.size(); }
+	inline bool contains(const MulticastGroup &mg) const { return std::binary_search(_subscriptions.begin(),_subscriptions.end(),mg.id32()); }
+
+	template<unsigned int C>
+	inline void serialize(Buffer<C> &b) const
+	{
+		b.append((uint64_t)_ts);
+		b.append((uint16_t)_subscriptions.size());
+		for(std::vector<uint32_t>::const_iterator i(_subscriptions.begin());i!=_subscriptions.end();++i)
+			b.append(*i);
+		b.append((uint16_t)_signatureLength);
+		b.append(_signature,_signatureLength);
+	}
+
+	template<unsigned int C>
+	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	{
+		unsigned int p = startAt;
+		_ts = (int64_t)(b.template at<uint64_t>(p)); p += 8;
+		_subscriptions.resize(b.template at<uint16_t>(p)); p += 2;
+		for(std::vector<uint32_t>::iterator i(_subscriptions.begin());i!=_subscriptions.end();++i) {
+			*i = b.template at<uint32_t>(p);
+			p += 4;
+		}
+		_signatureLength = b.template at<uint16_t>(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<uint32_t> _subscriptions;
+	unsigned int _signatureLength;
+	uint8_t _signature[ZT_SIGNATURE_BUFFER_SIZE];
+};
+
+} // namespace ZeroTier
+
+#endif

+ 1 - 1
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

+ 5 - 20
node/Hashtable.hpp

@@ -383,26 +383,11 @@ public:
 
 private:
 	template<typename O>
-	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()
 	{

+ 11 - 11
node/IncomingPacket.cpp

@@ -75,7 +75,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 			return _doHELLO(RR,tPtr,false);
 		}
 
-		const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,sourceAddress));
+		const SharedPtr<Peer> 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> peer(RR->topology->getPeer(tPtr,id.address()));
+	SharedPtr<Peer> 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<Peer>(new Peer(RR,RR->identity,id))));
+				RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->add(SharedPtr<Peer>(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> &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<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
+		const SharedPtr<Peer> rendezvousWith(RR->topology->get(with));
 		if (rendezvousWith) {
 			const unsigned int port = at<uint16_t>(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<Address>::iterator ra(recipients.begin());ra!=recipients.end();) {
-				SharedPtr<Peer> recipient(RR->topology->getPeer(tPtr,*ra));
+				SharedPtr<Peer> 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);

+ 1 - 0
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
 {

+ 5 - 0
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
 	 *

+ 22 - 11
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<InetAddress> &phy() const { return _physical; }
 	inline const std::vector<Identity> &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<Str> 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<Str>::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<virtualCount;++i)
 			p += _virtual[i].deserialize(b,p);
-		_signatureLen = b.template at<uint16_t>(p); p += 2;
+		_signatureLength = b.template at<uint16_t>(p); p += 2;
 		if (_signatureLength > ZT_SIGNATURE_BUFFER_SIZE)
 			throw ZT_EXCEPTION_INVALID_SERIALIZED_DATA_OVERFLOW;
 		memcpy(_signature,b.field(p,_signatureLength),_signatureLength);

+ 2 - 2
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;
 	}
 

+ 2 - 8
node/MulticastGroup.hpp

@@ -29,6 +29,7 @@
 
 #include <stdint.h>
 
+#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)); }

+ 0 - 4
node/Multicaster.cpp

@@ -243,10 +243,6 @@ void Multicaster::send(
 				Address explicitGatherPeers[16];
 				unsigned int numExplicitGatherPeers = 0;
 
-				SharedPtr<Peer> bestRoot(RR->topology->getUpstreamPeer());
-				if (bestRoot)
-					explicitGatherPeers[numExplicitGatherPeers++] = bestRoot->address();
-
 				explicitGatherPeers[numExplicitGatherPeers++] = network->controller();
 
 				Address ac[ZT_MAX_NETWORK_SPECIALISTS];

+ 5 - 33
node/Network.cpp

@@ -1246,15 +1246,12 @@ void Network::clean()
 				_multicastGroupsBehindMe.erase(*mg);
 		}
 	}
-
 	{
 		Address *a = (Address *)0;
 		Membership *m = (Membership *)0;
 		Hashtable<Address,Membership>::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<MulticastGroup> groups;
-	if (newMulticastGroup)
-		groups.push_back(*newMulticastGroup);
-	else groups = _allMulticastGroups();
-
-	std::vector<Address> 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<Address> upstreams(RR->topology->upstreamAddresses());
-		for(std::vector<Address>::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<Address>::const_iterator a(alwaysAnnounceTo.begin());a!=alwaysAnnounceTo.end();++a)
-			_announceMulticastGroupsTo(tPtr,*a,groups);
-	}
-
+	const std::vector<MulticastGroup> groups(_allMulticastGroups());
+	_announceMulticastGroupsTo(tPtr,controller(),groups);
 	{
 		Address *a = (Address *)0;
 		Membership *m = (Membership *)0;
 		Hashtable<Address,Membership>::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);
 		}
 	}

+ 4 - 4
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> &peer);
-	void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup);
+	void _sendUpdatesToMembers(void *tPtr);
 	void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
 	std::vector<MulticastGroup> _allMulticastGroups() const;
 

+ 1 - 1
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

+ 7 - 3
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<InetAddress> > 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<Path> > paths(pi->second->paths(_now));
 		SharedPtr<Path> bestp(pi->second->getAppropriatePath(_now,false));

+ 223 - 0
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 <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * 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<typename S>
+	inline Root(S dn,const uint8_t *const dnspk,const unsigned int dnspksize,const Identity &dflId,const std::vector<InetAddress> &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<typename I>
+	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<InetAddress> _defaultAddresses;
+	Str _dnsName;
+	Locator _lastFetchedLocator;
+	unsigned int _dnsPublicKeySize;
+	uint8_t _dnsPublicKey[ZT_ECC384_PUBLIC_KEY_SIZE];
+	Mutex _lock;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 23 - 0
node/Str.hpp

@@ -33,6 +33,8 @@
 #include "MAC.hpp"
 #include "InetAddress.hpp"
 
+#include <string>
+
 #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)); }

+ 23 - 13
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> peer(RR->topology->getPeer(tPtr,beaconAddr));
+			const SharedPtr<Peer> 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<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+						SharedPtr<Peer> 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<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+						SharedPtr<Peer> 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<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+								const SharedPtr<Peer> 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<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
+									const SharedPtr<Peer> sourcePeer(RR->topology->get(source));
 									if (sourcePeer)
 										relayTo->introduce(tPtr,now,sourcePeer);
 								}
 							}
+							*/
 						}
 					}
 				} else if ((reinterpret_cast<const uint8_t *>(data)[ZT_PACKET_IDX_FLAGS] & ZT_PROTO_FLAG_FRAGMENTED) != 0) {
@@ -402,7 +406,7 @@ void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &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<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
+		SharedPtr<Peer> 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<Peer> 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> &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> peer(RR->topology->getPeer(tPtr,destination));
+	const SharedPtr<Peer> 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<Peer> relay(RR->topology->getUpstreamPeer());
 			if ( (!relay) || (!(viaPath = relay->getAppropriatePath(now,false))) ) {
 				if (!(viaPath = peer->getAppropriatePath(now,true)))
 					return false;
 			}
+			*/
 		}
 	} else {
 		return false;

+ 43 - 54
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<Peer> addPeer(void *tPtr,const SharedPtr<Peer> &peer)
+	inline SharedPtr<Peer> add(const SharedPtr<Peer> &peer)
 	{
 		SharedPtr<Peer> np;
 		{
@@ -91,13 +93,28 @@ public:
 	 * @param zta ZeroTier address of peer
 	 * @return Peer or NULL if not found
 	 */
-	inline SharedPtr<Peer> getPeer(void *tPtr,const Address &zta) const
+	inline SharedPtr<Peer> get(const Address &zta)
 	{
-		if (zta == RR->identity.address())
+		if (zta == _myIdentity.address())
 			return SharedPtr<Peer>();
-		Mutex::Lock _l(_peers_m);
+
+		Mutex::Lock l1(_peers_m);
 		const SharedPtr<Peer> *const ap = _peers.get(zta);
-		return ((ap) ? *ap : SharedPtr<Peer>());
+		if (ap)
+			return *ap;
+
+		Mutex::Lock l2(_roots_m);
+		for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
+			if (r->address() == zta) {
+				try {
+					SharedPtr<Peer> rp(new Peer(RR,_myIdentity,r->id()));
+					_peers[zta] = rp;
+					return rp;
+				} catch ( ... ) {}
+			}
+		}
+
+		return SharedPtr<Peer>();
 	}
 
 	/**
@@ -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<Peer> *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<Peer> getPeerNoCache(const Address &zta)
-	{
-		Mutex::Lock _l(_peers_m);
-		const SharedPtr<Peer> *const ap = _peers.get(zta);
-		if (ap)
-			return *ap;
-		return SharedPtr<Peer>();
-	}
-
 	/**
 	 * Get a Path object for a given local and remote physical address, creating if needed
 	 *
@@ -153,36 +151,24 @@ public:
 		return p;
 	}
 
-	inline SharedPtr<Peer> getUpstreamPeer() const
-	{
-		// TODO
-		return SharedPtr<Peer>();
-	}
-
-	inline bool isUpstream(const Identity &id) const
+	/**
+	 * @param id Identity to check
+	 * @return True if this identity corresponds to a root
+	 */
+	inline bool isRoot(const Identity &id) const
 	{
-		// TODO
+		Mutex::Lock l(_roots_m);
+		for(std::vector<Root>::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<InetAddress> > &eps) const
-	{
-		// TODO
-	}
-
-	inline std::vector<Address> upstreamAddresses() const
-	{
-		// TODO
-		return std::vector<Address>();
-	}
-
-	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<InetAddress,ZT_PhysicalPathConfiguration> _physicalPathConfig[ZT_MAX_CONFIGURABLE_PATHS];
 	unsigned int _numConfiguredPhysicalPaths;
+	std::vector<Root> _roots;
 	Hashtable< Address,SharedPtr<Peer> > _peers;
-	Mutex _peers_m;
 	Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
+	Mutex _roots_m;
+	Mutex _peers_m;
 	Mutex _paths_m;
 };