Sfoglia il codice sorgente

Add notion of upstream that is separate from root in Topology, etc.

Adam Ierymenko 8 anni fa
parent
commit
bf8d71e82c
7 ha cambiato i file con 321 aggiunte e 55 eliminazioni
  1. 67 0
      attic/CertificateOfTrust.cpp
  2. 155 0
      attic/CertificateOfTrust.hpp
  3. 3 7
      node/IncomingPacket.cpp
  4. 14 5
      node/Packet.hpp
  5. 61 26
      node/Topology.cpp
  6. 20 17
      node/Topology.hpp
  7. 1 0
      objects.mk

+ 67 - 0
attic/CertificateOfTrust.cpp

@@ -0,0 +1,67 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016  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/>.
+ */
+
+#include "CertificateOfTrust.hpp"
+
+#include "RuntimeEnvironment.hpp"
+#include "Topology.hpp"
+#include "Switch.hpp"
+
+namespace ZeroTier {
+
+bool CertificateOfTrust::create(uint64_t ts,uint64_t rls,const Identity &iss,const Identity &tgt,Level l)
+{
+	if ((!iss)||(!iss.hasPrivate()))
+		return false;
+
+	_timestamp = ts;
+	_roles = rls;
+	_issuer = iss.address();
+	_target = tgt;
+	_level = l;
+
+	Buffer<sizeof(Identity) + 64> tmp;
+	tmp.append(_timestamp);
+	tmp.append(_roles);
+	_issuer.appendTo(tmp);
+	_target.serialize(tmp,false);
+	tmp.append((uint16_t)_level);
+	_signature = iss.sign(tmp.data(),tmp.size());
+
+	return true;
+}
+
+int CertificateOfTrust::verify(const RuntimeEnvironment *RR) const
+{
+	const Identity id(RR->topology->getIdentity(_issuer));
+	if (!id) {
+		RR->sw->requestWhois(_issuer);
+		return 1;
+	}
+
+	Buffer<sizeof(Identity) + 64> tmp;
+	tmp.append(_timestamp);
+	tmp.append(_roles);
+	_issuer.appendTo(tmp);
+	_target.serialize(tmp,false);
+	tmp.append((uint16_t)_level);
+
+	return (id.verify(tmp.data(),tmp.size(),_signature) ? 0 : -1);
+}
+
+} // namespace ZeroTier

+ 155 - 0
attic/CertificateOfTrust.hpp

@@ -0,0 +1,155 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2016  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/>.
+ */
+
+#ifndef ZT_CERTIFICATEOFTRUST_HPP
+#define ZT_CERTIFICATEOFTRUST_HPP
+
+#include "Constants.hpp"
+#include "Identity.hpp"
+#include "C25519.hpp"
+#include "Buffer.hpp"
+
+namespace ZeroTier {
+
+class RuntimeEnvironment;
+
+/**
+ * Certificate of peer to peer trust
+ */
+class CertificateOfTrust
+{
+public:
+	/**
+	 * Trust levels, with 0 indicating anti-trust
+	 */
+	enum Level
+	{
+		/**
+		 * Negative trust is reserved for informing peers that another peer is misbehaving, etc. Not currently used.
+		 */
+		LEVEL_NEGATIVE = 0,
+
+		/**
+		 * Default trust -- for most peers
+		 */
+		LEVEL_DEFAULT = 1,
+
+		/**
+		 * Above normal trust, e.g. common network membership
+		 */
+		LEVEL_MEDIUM = 25,
+
+		/**
+		 * High trust -- e.g. an upstream or a controller
+		 */
+		LEVEL_HIGH = 50,
+
+		/**
+		 * Right now ultimate is only for roots
+		 */
+		LEVEL_ULTIMATE = 100
+	};
+
+	/**
+	 * Role bit masks
+	 */
+	enum Role
+	{
+		/**
+		 * Target is permitted to represent issuer on the network as a federated root / relay
+		 */
+		ROLE_UPSTREAM = 0x00000001
+	};
+
+	CertificateOfTrust() :
+		_timestamp(0),
+		_roles(0),
+		_issuer(),
+		_target(),
+		_level(LEVEL_DEFAULT),
+		_signature() {}
+
+	/**
+	 * Create and sign this certificate of trust
+	 *
+	 * @param ts Cert timestamp
+	 * @param rls Roles bitmap
+	 * @param iss Issuer identity (must have secret key!)
+	 * @param tgt Target identity
+	 * @param l Trust level
+	 * @return True on successful signature
+	 */
+	bool create(uint64_t ts,uint64_t rls,const Identity &iss,const Identity &tgt,Level l);
+
+	/**
+	 * Verify this COT and its signature
+	 *
+	 * @param RR Runtime environment for looking up peers
+	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
+	 */
+	int verify(const RuntimeEnvironment *RR) const;
+
+	inline bool roleUpstream() const { return ((_roles & (uint64_t)ROLE_UPSTREAM) != 0); }
+
+	inline uint64_t timestamp() const { return _timestamp; }
+	inline uint64_t roles() const { return _roles; }
+	inline const Address &issuer() const { return _issuer; }
+	inline const Identity &target() const { return _target; }
+	inline Level level() const { return _level; }
+
+	inline operator bool() const { return (_issuer); }
+
+	template<unsigned int C>
+	inline void serialize(Buffer<C> &b) const
+	{
+		b.append(_timestamp);
+		b.append(_roles);
+		_issuer.appendTo(b);
+		_target.serialize(b);
+		b.append((uint16_t)_level);
+		b.append((uint8_t)1); // 1 == ed25519 signature
+		b.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
+		b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+		b.append((uint16_t)0); // length of additional fields
+	}
+
+	template<unsigned int C>
+	inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+	{
+		unsigned int p = startAt;
+		_timestamp = b.template at<uint64_t>(p); p += 8;
+		_roles = b.template at<uint64_t>(p); p += 8;
+		_issuer.setTo(b.field(p,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); p += ZT_ADDRESS_LENGTH;
+		p += _target.deserialize(b,p);
+		_level = b.template at<uint16_t>(p); p += 2;
+		p += b.template at<uint16_t>(p); p += 2;
+		return (p - startAt);
+	}
+
+private:
+	uint64_t _timestamp;
+	uint64_t _roles;
+	Address _issuer;
+	Identity _target;
+	Level _level;
+	C25519::Signature _signature;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 3 - 7
node/IncomingPacket.cpp

@@ -160,7 +160,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 
 			case Packet::ERROR_IDENTITY_COLLISION:
 				// FIXME: for federation this will need a payload with a signature or something.
-				if (RR->topology->isRoot(peer->identity()))
+				if (RR->topology->isUpstream(peer->identity()))
 					RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
 				break;
 
@@ -508,11 +508,7 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				id.serialize(outp,false);
 				++count;
 			} else {
-				// If I am not the root and don't know this identity, ask upstream. Downstream
-				// peer may re-request in the future and if so we will be able to provide it.
-				if (!RR->topology->amRoot())
-					RR->sw->requestWhois(addr);
-
+				RR->sw->requestWhois(addr);
 #ifdef ZT_ENABLE_CLUSTER
 				// Distribute WHOIS queries across a cluster if we do not know the ID.
 				// This may result in duplicate OKs to the querying peer, which is fine.
@@ -666,7 +662,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 				}
 			}
 
-			if ((flags & 0x10) != 0) {
+			if ((flags & 0x10) != 0) { // ACK requested
 				Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 				outp.append((uint8_t)Packet::VERB_EXT_FRAME);
 				outp.append((uint64_t)packetId());

+ 14 - 5
node/Packet.hpp

@@ -617,10 +617,8 @@ public:
 		 *   <[1] protocol address length (4 for IPv4, 16 for IPv6)>
 		 *   <[...] protocol address (network byte order)>
 		 *
-		 * This is sent by a relaying node to initiate NAT traversal between two
-		 * peers that are communicating by way of indirect relay. The relay will
-		 * send this to both peers at the same time on a periodic basis, telling
-		 * each where it might find the other on the network.
+		 * An upstream node can send this to inform both sides of a relay of
+		 * information they might use to establish a direct connection.
 		 *
 		 * Upon receipt a peer sends HELLO to establish a direct link.
 		 *
@@ -1051,7 +1049,18 @@ public:
 		 * OK or ERROR and has no special semantics outside of whatever the user
 		 * (via the ZeroTier core API) chooses to give it.
 		 */
-		VERB_USER_MESSAGE = 0x14
+		VERB_USER_MESSAGE = 0x14,
+
+		/**
+		 * Information related to federation and mesh-like behavior:
+		 *   <[2] 16-bit length of Dictionary>
+		 *   <[...] topology definition info Dictionary>
+		 *
+		 * This message can carry information that can be used to define topology
+		 * and implement "mesh-like" behavior. It can optionally generate OK or
+		 * ERROR, and these carry the same payload.
+		 */
+		VERB_TOPOLOGY_HINT = 0x15
 	};
 
 	/**

+ 61 - 26
node/Topology.cpp

@@ -111,9 +111,8 @@ SharedPtr<Peer> Topology::getPeer(const Address &zta)
 	{
 		Mutex::Lock _l(_lock);
 		const SharedPtr<Peer> *const ap = _peers.get(zta);
-		if (ap) {
+		if (ap)
 			return *ap;
-		}
 	}
 
 	try {
@@ -158,7 +157,7 @@ void Topology::saveIdentity(const Identity &id)
 	}
 }
 
-SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
+SharedPtr<Peer> Topology::getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid)
 {
 	const uint64_t now = RR->node->now();
 	Mutex::Lock _l(_lock);
@@ -189,22 +188,25 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
 		const SharedPtr<Peer> *bestOverall = (const SharedPtr<Peer> *)0;
 		const SharedPtr<Peer> *bestNotAvoid = (const SharedPtr<Peer> *)0;
 
-		for(std::vector< SharedPtr<Peer> >::const_iterator r(_rootPeers.begin());r!=_rootPeers.end();++r) {
-			bool avoiding = false;
-			for(unsigned int i=0;i<avoidCount;++i) {
-				if (avoid[i] == (*r)->address()) {
-					avoiding = true;
-					break;
+		for(std::vector<Address>::const_iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
+			const SharedPtr<Peer> *const p = _peers.get(*a);
+			if (p) {
+				bool avoiding = false;
+				for(unsigned int i=0;i<avoidCount;++i) {
+					if (avoid[i] == (*p)->address()) {
+						avoiding = true;
+						break;
+					}
+				}
+				const unsigned int q = (*p)->relayQuality(now);
+				if (q <= bestQualityOverall) {
+					bestQualityOverall = q;
+					bestOverall = &(*p);
+				}
+				if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
+					bestQualityNotAvoid = q;
+					bestNotAvoid = &(*p);
 				}
-			}
-			const unsigned int q = (*r)->relayQuality(now);
-			if (q <= bestQualityOverall) {
-				bestQualityOverall = q;
-				bestOverall = &(*r);
-			}
-			if ((!avoiding)&&(q <= bestQualityNotAvoid)) {
-				bestQualityNotAvoid = q;
-				bestNotAvoid = &(*r);
 			}
 		}
 
@@ -219,9 +221,34 @@ SharedPtr<Peer> Topology::getBestRoot(const Address *avoid,unsigned int avoidCou
 	return SharedPtr<Peer>();
 }
 
+bool Topology::isRoot(const Identity &id) const
+{
+	Mutex::Lock _l(_lock);
+	return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
+}
+
 bool Topology::isUpstream(const Identity &id) const
 {
-	return isRoot(id);
+	Mutex::Lock _l(_lock);
+	return (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),id.address()) != _upstreamAddresses.end());
+}
+
+void Topology::setUpstream(const Address &a,bool upstream)
+{
+	Mutex::Lock _l(_lock);
+	if (std::find(_rootAddresses.begin(),_rootAddresses.end(),a) == _rootAddresses.end()) {
+		if (upstream) {
+			if (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),a) == _upstreamAddresses.end())
+				_upstreamAddresses.push_back(a);
+		} else {
+			std::vector<Address> ua;
+			for(std::vector<Address>::iterator i(_upstreamAddresses.begin());i!=_upstreamAddresses.end();++i) {
+				if (a != *i)
+					ua.push_back(*i);
+			}
+			_upstreamAddresses.swap(ua);
+		}
+	}
 }
 
 bool Topology::worldUpdateIfValid(const World &newWorld)
@@ -249,7 +276,7 @@ void Topology::clean(uint64_t now)
 		Address *a = (Address *)0;
 		SharedPtr<Peer> *p = (SharedPtr<Peer> *)0;
 		while (i.next(a,p)) {
-			if ( (!(*p)->isAlive(now)) && (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end()) )
+			if ( (!(*p)->isAlive(now)) && (std::find(_upstreamAddresses.begin(),_upstreamAddresses.end(),*a) == _upstreamAddresses.end()) )
 				_peers.erase(*a);
 		}
 	}
@@ -280,25 +307,33 @@ Identity Topology::_getIdentity(const Address &zta)
 void Topology::_setWorld(const World &newWorld)
 {
 	// assumed _lock is locked (or in constructor)
+
+	std::vector<Address> ua;
+	for(std::vector<Address>::iterator a(_upstreamAddresses.begin());a!=_upstreamAddresses.end();++a) {
+		if (std::find(_rootAddresses.begin(),_rootAddresses.end(),*a) == _rootAddresses.end())
+			ua.push_back(*a);
+	}
+
 	_world = newWorld;
-	_amRoot = false;
 	_rootAddresses.clear();
-	_rootPeers.clear();
+	_amRoot = false;
+
 	for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
 		_rootAddresses.push_back(r->identity.address());
+		if (std::find(ua.begin(),ua.end(),r->identity.address()) == ua.end())
+			ua.push_back(r->identity.address());
 		if (r->identity.address() == RR->identity.address()) {
 			_amRoot = true;
 		} else {
 			SharedPtr<Peer> *rp = _peers.get(r->identity.address());
-			if (rp) {
-				_rootPeers.push_back(*rp);
-			} else {
+			if (!rp) {
 				SharedPtr<Peer> newrp(new Peer(RR,RR->identity,r->identity));
 				_peers.set(r->identity.address(),newrp);
-				_rootPeers.push_back(newrp);
 			}
 		}
 	}
+
+	_upstreamAddresses.swap(ua);
 }
 
 } // namespace ZeroTier

+ 20 - 17
node/Topology.hpp

@@ -125,35 +125,27 @@ public:
 	void saveIdentity(const Identity &id);
 
 	/**
-	 * Get the current favorite root server
+	 * Get the current best upstream peer
 	 *
 	 * @return Root server with lowest latency or NULL if none
 	 */
-	inline SharedPtr<Peer> getBestRoot() { return getBestRoot((const Address *)0,0,false); }
+	inline SharedPtr<Peer> getUpstreamPeer() { return getUpstreamPeer((const Address *)0,0,false); }
 
 	/**
-	 * Get the best root server, avoiding root servers listed in an array
-	 *
-	 * This will get the best root server (lowest latency, etc.) but will
-	 * try to avoid the listed root servers, only using them if no others
-	 * are available.
+	 * Get the current best upstream peer, avoiding those in the supplied avoid list
 	 *
 	 * @param avoid Nodes to avoid
 	 * @param avoidCount Number of nodes to avoid
 	 * @param strictAvoid If false, consider avoided root servers anyway if no non-avoid root servers are available
 	 * @return Root server or NULL if none available
 	 */
-	SharedPtr<Peer> getBestRoot(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
+	SharedPtr<Peer> getUpstreamPeer(const Address *avoid,unsigned int avoidCount,bool strictAvoid);
 
 	/**
 	 * @param id Identity to check
 	 * @return True if this is a designated root server in this world
 	 */
-	inline bool isRoot(const Identity &id) const
-	{
-		Mutex::Lock _l(_lock);
-		return (std::find(_rootAddresses.begin(),_rootAddresses.end(),id.address()) != _rootAddresses.end());
-	}
+	bool isRoot(const Identity &id) const;
 
 	/**
 	 * @param id Identity to check
@@ -161,6 +153,16 @@ public:
 	 */
 	bool isUpstream(const Identity &id) const;
 
+	/**
+	 * Set whether or not an address is upstream
+	 *
+	 * If the address is a root this does nothing, since roots are fixed.
+	 *
+	 * @param a Target address
+	 * @param upstream New upstream status
+	 */
+	void setUpstream(const Address &a,bool upstream);
+
 	/**
 	 * @return Vector of root server addresses
 	 */
@@ -175,7 +177,8 @@ public:
 	 */
 	inline std::vector<Address> upstreamAddresses() const
 	{
-		return rootAddresses();
+		Mutex::Lock _l(_lock);
+		return _upstreamAddresses;
 	}
 
 	/**
@@ -342,9 +345,9 @@ private:
 	Hashtable< Address,SharedPtr<Peer> > _peers;
 	Hashtable< Path::HashKey,SharedPtr<Path> > _paths;
 
-	std::vector< Address > _rootAddresses;
-	std::vector< SharedPtr<Peer> > _rootPeers;
-	bool _amRoot;
+	std::vector< Address > _upstreamAddresses; // includes roots
+	std::vector< Address > _rootAddresses; // only roots
+	bool _amRoot; // am I a root?
 
 	Mutex _lock;
 };

+ 1 - 0
objects.mk

@@ -4,6 +4,7 @@ OBJS=\
 	node/C25519.o \
 	node/Capability.o \
 	node/CertificateOfMembership.o \
+	node/CertificateOfTrust.o \
 	node/Cluster.o \
 	node/Identity.o \
 	node/IncomingPacket.o \