瀏覽代碼

More World stuff, and mkworld.

Adam Ierymenko 9 年之前
父節點
當前提交
cae58f43f1
共有 13 個文件被更改,包括 281 次插入105 次删除
  1. 1 3
      include/ZeroTierOne.h
  2. 5 1
      make-mac.mk
  3. 153 0
      mkworld.cpp
  4. 0 1
      node/IncomingPacket.cpp
  5. 3 21
      node/Node.cpp
  6. 1 2
      node/Node.hpp
  7. 4 2
      node/Packet.hpp
  8. 49 29
      node/Topology.cpp
  9. 20 2
      node/Topology.hpp
  10. 40 20
      node/World.hpp
  11. 1 15
      one.cpp
  12. 3 6
      service/OneService.cpp
  13. 1 3
      service/OneService.hpp

+ 1 - 3
include/ZeroTierOne.h

@@ -1023,7 +1023,6 @@ typedef int (*ZT_WirePacketSendFunction)(
  * @param dataStorePutFunction Function called to put objects in persistent storage
  * @param virtualNetworkConfigFunction Function to be called when virtual LANs are created, deleted, or their config parameters change
  * @param eventCallback Function to receive status updates and non-fatal error notices
- * @param overrideRootTopology Alternative root server topology or NULL for default (mostly for test/debug use)
  * @return OK (0) or error code if a fatal error condition has occurred
  */
 enum ZT_ResultCode ZT_Node_new(
@@ -1035,8 +1034,7 @@ enum ZT_ResultCode ZT_Node_new(
 	ZT_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
-	ZT_EventCallback eventCallback,
-	const char *overrideRootTopology);
+	ZT_EventCallback eventCallback);
 
 /**
  * Delete a node and free all resources it consumes

+ 5 - 1
make-mac.mk

@@ -79,6 +79,10 @@ selftest: $(OBJS) selftest.o
 	$(CXX) $(CXXFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
 	$(STRIP) zerotier-selftest
 
+mkworld: $(OBJS)
+	rm -f mkworld
+	$(CXX) $(CXXFLAGS) -o mkworld mkworld.cpp $(OBJS) $(LIBS)
+
 # Requires Packages: http://s.sudre.free.fr/Software/Packages/about.html
 mac-dist-pkg: FORCE
 	packagesbuild "ext/installfiles/mac/ZeroTier One.pkgproj"
@@ -93,7 +97,7 @@ official: FORCE
 	make ZT_OFFICIAL_RELEASE=1 mac-dist-pkg
 
 clean:
-	rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-*
+	rm -rf *.dSYM build-* *.pkg *.dmg *.o node/*.o controller/*.o service/*.o osdep/*.o ext/http-parser/*.o ext/lz4/*.o ext/json-parser/*.o zerotier-one zerotier-idtool zerotier-selftest zerotier-cli ZeroTierOneInstaller-* mkworld
 
 # For those building from source -- installs signed binary tap driver in system ZT home
 install-mac-tap: FORCE

+ 153 - 0
mkworld.cpp

@@ -0,0 +1,153 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015  ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+/*
+ * This utility makes the World from the configuration specified below.
+ * It probably won't be much use to anyone outside ZeroTier, Inc. except
+ * for testing and experimentation purposes.
+ *
+ * If you want to make your own World you must edit this file.
+ *
+ * When run, it expects two files in the current directory:
+ *
+ * previous.c25519 - key pair to sign this world (key from previous world)
+ * current.c25519 - key pair whose public key should be embedded in this world
+ *
+ * If these files do not exist, they are both created with the same key pair
+ * and a self-signed initial World is born.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+#include <algorithm>
+
+#include "node/Constants.hpp"
+#include "node/World.hpp"
+#include "node/C25519.hpp"
+#include "node/Identity.hpp"
+#include "node/InetAddress.hpp"
+#include "osdep/OSUtils.hpp"
+
+using namespace ZeroTier;
+
+class WorldMaker : public World
+{
+public:
+	static inline World make(uint64_t id,uint64_t ts,const C25519::Public &sk,const std::vector<World::Root> &roots,const C25519::Pair &signWith)
+	{
+		WorldMaker w;
+		w._id = id;
+		w._ts = ts;
+		w._updateSigningKey = sk;
+		w._roots = roots;
+
+		Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+		w.serialize(tmp,true);
+		w._signature = C25519::sign(signWith,tmp.data(),tmp.size());
+
+		return w;
+	}
+};
+
+int main(int argc,char **argv)
+{
+	std::string previous,current;
+	if ((!OSUtils::readFile("previous.c25519",previous))||(!OSUtils::readFile("current.c25519",current))) {
+		C25519::Pair np(C25519::generate());
+		previous = std::string();
+		previous.append((const char *)np.pub.data,ZT_C25519_PUBLIC_KEY_LEN);
+		previous.append((const char *)np.priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+		current = previous;
+		OSUtils::writeFile("previous.c25519",previous);
+		OSUtils::writeFile("current.c25519",current);
+		fprintf(stderr,"INFO: created initial world keys: previous.c25519, current.c25519"ZT_EOL_S);
+	}
+
+	if ((previous.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))||(current.length() != (ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_PRIVATE_KEY_LEN))) {
+		fprintf(stderr,"FATAL: previous.c25519 or current.c25519 empty or invalid"ZT_EOL_S);
+		return 1;
+	}
+	C25519::Pair previousKP;
+	memcpy(previousKP.pub.data,previous.data(),ZT_C25519_PUBLIC_KEY_LEN);
+	memcpy(previousKP.priv.data,previous.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN);
+	C25519::Pair currentKP;
+	memcpy(currentKP.pub.data,current.data(),ZT_C25519_PUBLIC_KEY_LEN);
+	memcpy(currentKP.priv.data,current.data() + ZT_C25519_PUBLIC_KEY_LEN,ZT_C25519_PRIVATE_KEY_LEN);
+
+	// EDIT BELOW HERE ---------------------------------------------------------
+
+	std::vector<World::Root> roots;
+
+	// old US-SFO
+	roots.push_back(World::Root());
+	roots.back().identity = Identity("7e19876aba:0:2a6e2b2318930f60eb097f70d0f4b028b2cd6d3d0c63c014b9039ff35390e41181f216fb2e6fa8d95c1ee9667156411905c3dccfea78d8c6dfafba688170b3fa");
+	roots.back().stableEndpoints.push_back(InetAddress("198.199.97.220/9993"));
+	std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
+
+	// old EU-PARIS
+	roots.push_back(World::Root());
+	roots.back().identity = Identity("8841408a2e:0:bb1d31f2c323e264e9e64172c1a74f77899555ed10751cd56e86405cde118d02dffe555d462ccf6a85b5631c12350c8d5dc409ba10b9025d0f445cf449d92b1c");
+	roots.back().stableEndpoints.push_back(InetAddress("107.191.46.210/9993"));
+	std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
+
+	// old US-NYC
+	roots.push_back(World::Root());
+	roots.back().identity = Identity("8acf059fe3:0:482f6ee5dfe902319b419de5bdc765209c0ecda38c4d6e4fcf0d33658398b4527dcd22f93112fb9befd02fd78bf7261b333fc105d192a623ca9e50fc60b374a5");
+	roots.back().stableEndpoints.push_back(InetAddress("162.243.77.111/9993"));
+	std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
+
+	// old AP-SNG
+	roots.push_back(World::Root());
+	roots.back().identity = Identity("9d219039f3:0:01f0922a98e3b34ebcbff333269dc265d7a020aab69d72be4d4acc9c8c9294785771256cd1d942a90d1bd1d2dca3ea84ef7d85afe6611fb43ff0b74126d90a6e");
+	roots.back().stableEndpoints.push_back(InetAddress("128.199.197.217/9993"));
+	std::sort(roots.back().stableEndpoints.begin(),roots.back().stableEndpoints.end());
+
+	std::sort(roots.begin(),roots.end());
+
+	const uint64_t id = ZT_WORLD_ID_EARTH;
+	const uint64_t ts = OSUtils::now();
+
+	// END WORLD SETUP ---------------------------------------------------------
+
+	fprintf(stderr,"INFO: generating and signing id==%llu ts==%llu"ZT_EOL_S,(unsigned long long)id,(unsigned long long)ts);
+
+	World nw = WorldMaker::make(id,ts,currentKP.pub,roots,previousKP);
+
+	Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> outtmp;
+	nw.serialize(outtmp,false);
+	fwrite(outtmp.data(),outtmp.size(),1,stdout);
+	fflush(stdout);
+
+	fprintf(stderr,"INFO: wrote %u bytes to stdout"ZT_EOL_S,outtmp.size());
+
+	return 0;
+}

+ 0 - 1
node/IncomingPacket.cpp

@@ -33,7 +33,6 @@
 #include "../include/ZeroTierOne.h"
 
 #include "Constants.hpp"
-#include "Defaults.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "IncomingPacket.hpp"
 #include "Topology.hpp"

+ 3 - 21
node/Node.cpp

@@ -46,7 +46,6 @@
 #include "Address.hpp"
 #include "Identity.hpp"
 #include "SelfAwareness.hpp"
-#include "Defaults.hpp"
 
 const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
 
@@ -64,8 +63,7 @@ Node::Node(
 	ZT_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
-	ZT_EventCallback eventCallback,
-	const char *overrideRootTopology) :
+	ZT_EventCallback eventCallback) :
 	_RR(this),
 	RR(&_RR),
 	_uPtr(uptr),
@@ -125,21 +123,6 @@ Node::Node(
 		throw;
 	}
 
-	Dictionary rt;
-	if (overrideRootTopology) {
-		rt.fromString(std::string(overrideRootTopology));
-	} else {
-		std::string rttmp(dataStoreGet("root-topology"));
-		if (rttmp.length() > 0) {
-			rt.fromString(rttmp);
-			if (!Topology::authenticateRootTopology(rt))
-				rt.clear();
-		}
-		if ((!rt.size())||(!rt.contains("rootservers")))
-			rt.fromString(ZT_DEFAULTS.defaultRootTopology);
-	}
-	RR->topology->setRootServers(Dictionary(rt.get("rootservers","")));
-
 	postEvent(ZT_EVENT_UP);
 }
 
@@ -609,12 +592,11 @@ enum ZT_ResultCode ZT_Node_new(
 	ZT_WirePacketSendFunction wirePacketSendFunction,
 	ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 	ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
-	ZT_EventCallback eventCallback,
-	const char *overrideRootTopology)
+	ZT_EventCallback eventCallback)
 {
 	*node = (ZT_Node *)0;
 	try {
-		*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback,overrideRootTopology));
+		*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback));
 		return ZT_RESULT_OK;
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;

+ 1 - 2
node/Node.hpp

@@ -71,8 +71,7 @@ public:
 		ZT_WirePacketSendFunction wirePacketSendFunction,
 		ZT_VirtualNetworkFrameFunction virtualNetworkFrameFunction,
 		ZT_VirtualNetworkConfigFunction virtualNetworkConfigFunction,
-		ZT_EventCallback eventCallback,
-		const char *overrideRootTopology);
+		ZT_EventCallback eventCallback);
 
 	~Node();
 

+ 4 - 2
node/Packet.hpp

@@ -540,6 +540,8 @@ public:
 		 *   <[...] binary serialized identity (see Identity)>
 		 *   <[1] destination address type>
 		 *   [<[...] destination address>]
+		 *   <[8] 64-bit world ID of current world>
+		 *   <[8] 64-bit timestamp of current world>
 		 *
 		 * This is the only message that ever must be sent in the clear, since it
 		 * is used to push an identity to a new peer.
@@ -564,8 +566,8 @@ public:
 		 *   <[2] software revision (of responder)>
 		 *   <[1] destination address type (for this OK, not copied from HELLO)>
 		 *   [<[...] destination address>]
-		 *   <[8] 64-bit world ID of current world>
-		 *   <[8] 64-bit timestamp of current world>
+		 *   <[8] 64-bit world ID of current world (of responder)>
+		 *   <[8] 64-bit timestamp of current world (of responder)>
 		 *
 		 * ERROR has no payload.
 		 */

+ 49 - 29
node/Topology.cpp

@@ -42,23 +42,6 @@ Topology::Topology(const RuntimeEnvironment *renv) :
 	RR(renv),
 	_amRoot(false)
 {
-	try {
-		std::string dsWorld(RR->node->dataStoreGet("world"));
-		Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),dsWorld.length());
-		_world.deserialize(dswtmp,0);
-	} catch ( ... ) {
-		_world = World(); // set to null if cached world is invalid
-	}
-	{
-		World defaultWorld;
-		Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
-		defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
-		if (_world.verifyUpdate(defaultWorld)) {
-			_world = defaultWorld;
-			RR->node->dataStorePut("world",ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH,false);
-		}
-	}
-
 	std::string alls(RR->node->dataStoreGet("peers.save"));
 	const uint8_t *all = reinterpret_cast<const uint8_t *>(alls.data());
 	RR->node->dataStoreDelete("peers.save");
@@ -97,19 +80,24 @@ Topology::Topology(const RuntimeEnvironment *renv) :
 
 	clean(RR->node->now());
 
-	for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
-		if (r->identity == RR->identity)
-			_amRoot = true;
-		_rootAddresses.push_back(r->identity.address());
-		SharedPtr<Peer> *rp = _peers.get(r->identity.address());
-		if (rp) {
-			_rootPeers.push_back(*rp);
-		} else if (r->identity.address() != RR->identity.address()) {
-			SharedPtr<Peer> newrp(new Peer(RR->identity,r->identity));
-			_peers.set(r->identity.address(),newrp);
-			_rootPeers.push_back(newrp);
-		}
+	std::string dsWorld(RR->node->dataStoreGet("world"));
+	World cachedWorld;
+	try {
+		Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(dsWorld.data(),dsWorld.length());
+		cachedWorld.deserialize(dswtmp,0);
+	} catch ( ... ) {
+		cachedWorld = World(); // clear if cached world is invalid
 	}
+	World defaultWorld;
+	{
+		Buffer<ZT_DEFAULT_WORLD_LENGTH> wtmp(ZT_DEFAULT_WORLD,ZT_DEFAULT_WORLD_LENGTH);
+		defaultWorld.deserialize(wtmp,0); // throws on error, which would indicate a bad static variable up top
+	}
+	if (cachedWorld.shouldBeReplacedBy(defaultWorld,false)) {
+		_setWorld(defaultWorld);
+		if (dsWorld.length() > 0)
+			RR->node->dataStoreDelete("world");
+	} else _setWorld(cachedWorld);
 }
 
 Topology::~Topology()
@@ -283,6 +271,16 @@ keep_searching_for_roots:
 	return bestRoot;
 }
 
+bool Topology::worldUpdateIfValid(const World &newWorld)
+{
+	Mutex::Lock _l(_lock);
+	if (_world.shouldBeReplacedBy(newWorld,true)) {
+		_setWorld(newWorld);
+		return true;
+	}
+	return false;
+}
+
 void Topology::clean(uint64_t now)
 {
 	Mutex::Lock _l(_lock);
@@ -320,4 +318,26 @@ void Topology::_saveIdentity(const Identity &id)
 	}
 }
 
+void Topology::_setWorld(const World &newWorld)
+{
+	// assumed _lock is locked (or in constructor)
+	_world = newWorld;
+	_amRoot = false;
+	_rootAddresses.clear();
+	_rootPeers.clear();
+	for(std::vector<World::Root>::const_iterator r(_world.roots().begin());r!=_world.roots().end();++r) {
+		if (r->identity == RR->identity)
+			_amRoot = true;
+		_rootAddresses.push_back(r->identity.address());
+		SharedPtr<Peer> *rp = _peers.get(r->identity.address());
+		if (rp) {
+			_rootPeers.push_back(*rp);
+		} else if (r->identity.address() != RR->identity.address()) {
+			SharedPtr<Peer> newrp(new Peer(RR->identity,r->identity));
+			_peers.set(r->identity.address(),newrp);
+			_rootPeers.push_back(newrp);
+		}
+	}
+}
+
 } // namespace ZeroTier

+ 20 - 2
node/Topology.hpp

@@ -31,10 +31,10 @@
 #include <stdio.h>
 #include <string.h>
 
-#include <map>
 #include <vector>
 #include <stdexcept>
 #include <algorithm>
+#include <utility>
 
 #include "Constants.hpp"
 
@@ -146,6 +146,23 @@ public:
 		return _world;
 	}
 
+	/**
+	 * @return Pair containing world ID and world timestamp (faster than world().id() etc.)
+	 */
+	inline std::pair<uint64_t,uint64_t> worldIdentification() const
+	{
+		Mutex::Lock _l(_lock);
+		return std::pair<uint64_t,uint64_t>(_world.id(),_world.timestamp());
+	}
+
+	/**
+	 * Validate new world and update if newer and signature is okay
+	 *
+	 * @param newWorld Potential new world definition revision
+	 * @return True if an update actually occurred
+	 */
+	bool worldUpdateIfValid(const World &newWorld);
+
 	/**
 	 * Clean and flush database
 	 */
@@ -176,7 +193,7 @@ public:
 	}
 
 	/**
-	 * @return All currently active peers by address
+	 * @return All currently active peers by address (unsorted)
 	 */
 	inline std::vector< std::pair< Address,SharedPtr<Peer> > > allPeers() const
 	{
@@ -187,6 +204,7 @@ public:
 private:
 	Identity _getIdentity(const Address &zta);
 	void _saveIdentity(const Identity &id);
+	void _setWorld(const World &newWorld);
 
 	const RuntimeEnvironment *RR;
 

+ 40 - 20
node/World.hpp

@@ -55,7 +55,7 @@
 /**
  * The (more than) maximum length of a serialized World
  */
-#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 64)
+#define ZT_WORLD_MAX_SERIALIZED_LENGTH (((1024 + (32 * ZT_WORLD_MAX_STABLE_ENDPOINTS_PER_ROOT)) * ZT_WORLD_MAX_ROOTS) + ZT_C25519_PUBLIC_KEY_LEN + ZT_C25519_SIGNATURE_LEN + 128)
 
 /**
  * World ID indicating null / empty World object
@@ -68,15 +68,11 @@
 #define ZT_WORLD_ID_TESTNET 1
 
 /**
- * World ID for Earth -- its approximate distance from the sun in kilometers
+ * World ID for Earth
  *
  * This is the ID for the ZeroTier World used on planet Earth. It is unrelated
- * to the public network 8056c2e21c000001 of the same name.
- *
- * It's advisable to create a new World for network regions spaced more than
- * 2-3 light seconds, since RTT times in excess of 5s are problematic for some
- * protocols. Earth could therefore include its low and high orbits, the Moon,
- * and nearby Lagrange points.
+ * to the public network 8056c2e21c000001 of the same name. It was chosen
+ * from Earth's approximate distance from the sun in kilometers.
  */
 #define ZT_WORLD_ID_EARTH 149604618
 
@@ -90,9 +86,24 @@ namespace ZeroTier {
 /**
  * A world definition (formerly known as a root topology)
  *
- * A world consists of a set of root servers and a signature scheme enabling
- * it to be updated going forward. It defines a single ZeroTier VL1 network
- * area within which any device can reach any other.
+ * Think of a World as a single data center. Within this data center a set
+ * of distributed fault tolerant root servers provide stable anchor points
+ * for a peer to peer network that provides VLAN service. Updates to a world
+ * definition can be published by signing them with the previous revision's
+ * signing key, and should be very infrequent.
+ *
+ * The maximum data center size is approximately 2.5 cubic light seconds,
+ * since many protocols have issues with >5s RTT latencies.
+ *
+ * ZeroTier operates a World for Earth capable of encompassing the planet, its
+ * orbits, the Moon (about 1.3 light seconds), and nearby Lagrange points. A
+ * world ID for Mars and nearby space is defined but not yet used, and a test
+ * world ID is provided for testing purposes.
+ *
+ * If you absolutely must run your own "unofficial" ZeroTier network, please
+ * define your world IDs above 0xffffffff (4294967295). Code to make a World
+ * is in mkworld.cpp in the parent directory and must be edited to change
+ * settings.
  */
 class World
 {
@@ -130,24 +141,28 @@ public:
 	inline uint64_t timestamp() const throw() { return _ts; }
 
 	/**
-	 * Verify a world update
+	 * Check whether a world update should replace this one
 	 *
 	 * A new world update is valid if it is for the same world ID, is newer,
 	 * and is signed by the current world's signing key. If this world object
 	 * is null, it can always be updated.
 	 *
 	 * @param update Candidate update
+	 * @param fullSignatureCheck Perform full cryptographic signature check (true == yes, false == skip)
 	 * @return True if update is newer than current and is properly signed
 	 */
-	inline bool verifyUpdate(const World &update)
+	inline bool shouldBeReplacedBy(const World &update,bool fullSignatureCheck)
 	{
 		if (_id == ZT_WORLD_ID_NULL)
 			return true;
-		if ((update._id != _id)||(update._ts <= _ts))
-			return false;
-		Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
-		update.serialize(tmp);
-		return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
+		if ((_id == update._id)&&(_ts < update._ts)) {
+			if (fullSignatureCheck) {
+				Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> tmp;
+				update.serialize(tmp,true);
+				return C25519::verify(_updateSigningKey,tmp.data(),tmp.size(),update._signature);
+			} else return true;
+		}
+		return false;
 	}
 
 	/**
@@ -156,13 +171,16 @@ public:
 	inline operator bool() const throw() { return (_id != ZT_WORLD_ID_NULL); }
 
 	template<unsigned int C>
-	inline void serialize(Buffer<C> &b) const
+	inline void serialize(Buffer<C> &b,bool forSign = false) const
 	{
+		if (forSign)
+			b.append((uint64_t)0x7f7f7f7f7f7f7f7fULL);
 		b.append((uint8_t)0x01); // version -- only one valid value for now
 		b.append((uint64_t)_id);
 		b.append((uint64_t)_ts);
 		b.append(_updateSigningKey.data,ZT_C25519_PUBLIC_KEY_LEN);
-		b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
+		if (!forSign)
+			b.append(_signature.data,ZT_C25519_SIGNATURE_LEN);
 		b.append((uint8_t)_roots.size());
 		for(std::vector<Root>::const_iterator r(_roots.begin());r!=_roots.end();++r) {
 			r->identity.serialize(b);
@@ -170,6 +188,8 @@ public:
 			for(std::vector<InetAddress>::const_iterator ep(r->stableEndpoints.begin());ep!=r->stableEndpoints.end();++ep)
 				ep->serialize(b);
 		}
+		if (forSign)
+			b.append((uint64_t)0xf7f7f7f7f7f7f7f7ULL);
 	}
 
 	template<unsigned int C>

+ 1 - 15
one.cpp

@@ -911,7 +911,6 @@ static void printHelp(const char *cn,FILE *out)
 	fprintf(out,"  -v                - Show version"ZT_EOL_S);
 	fprintf(out,"  -U                - Run as unprivileged user (skip privilege check)"ZT_EOL_S);
 	fprintf(out,"  -p<port>          - Port for UDP and TCP/HTTP (default: 9993, 0 for random)"ZT_EOL_S);
-	//fprintf(out,"  -T<path>          - Override root topology, do not authenticate or update"ZT_EOL_S);
 
 #ifdef __UNIX_LIKE__
 	fprintf(out,"  -d                - Fork and run as daemon (Unix-ish OSes)"ZT_EOL_S);
@@ -974,7 +973,6 @@ int main(int argc,char **argv)
 	if ((strstr(argv[0],"zerotier-cli"))||(strstr(argv[0],"ZEROTIER-CLI")))
 		return cli(argc,argv);
 
-	std::string overrideRootTopology;
 	std::string homeDir;
 	unsigned int port = ZT_DEFAULT_PORT;
 	bool skipRootCheck = false;
@@ -1001,18 +999,6 @@ int main(int argc,char **argv)
 					skipRootCheck = true;
 					break;
 
-				case 'T': // Override root topology
-					if (argv[i][2]) {
-						if (!OSUtils::readFile(argv[i] + 2,overrideRootTopology)) {
-							fprintf(stderr,"%s: cannot read root topology from %s"ZT_EOL_S,argv[0],argv[i] + 2);
-							return 1;
-						}
-					} else {
-						printHelp(argv[0],stdout);
-						return 1;
-					}
-					break;
-
 				case 'v': // Display version
 					printf("%d.%d.%d"ZT_EOL_S,ZEROTIER_ONE_VERSION_MAJOR,ZEROTIER_ONE_VERSION_MINOR,ZEROTIER_ONE_VERSION_REVISION);
 					return 0;
@@ -1169,7 +1155,7 @@ int main(int argc,char **argv)
 
 	try {
 		for(;;) {
-			zt1Service = OneService::newInstance(homeDir.c_str(),port,(overrideRootTopology.length() > 0) ? overrideRootTopology.c_str() : (const char *)0);
+			zt1Service = OneService::newInstance(homeDir.c_str(),port);
 			switch(zt1Service->run()) {
 				case OneService::ONE_STILL_RUNNING: // shouldn't happen, run() won't return until done
 				case OneService::ONE_NORMAL_TERMINATION:

+ 3 - 6
service/OneService.cpp

@@ -418,14 +418,13 @@ struct TcpConnection
 class OneServiceImpl : public OneService
 {
 public:
-	OneServiceImpl(const char *hp,unsigned int port,const char *overrideRootTopology) :
+	OneServiceImpl(const char *hp,unsigned int port) :
 		_homePath((hp) ? hp : "."),
 		_tcpFallbackResolver(ZT_TCP_FALLBACK_RELAY),
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 		_controller((SqliteNetworkController *)0),
 #endif
 		_phy(this,false,true),
-		_overrideRootTopology((overrideRootTopology) ? overrideRootTopology : ""),
 		_node((Node *)0),
 		_controlPlane((ControlPlane *)0),
 		_lastDirectReceiveFromGlobal(0),
@@ -550,8 +549,7 @@ public:
 				SnodeWirePacketSendFunction,
 				SnodeVirtualNetworkFrameFunction,
 				SnodeVirtualNetworkConfigFunction,
-				SnodeEventCallback,
-				((_overrideRootTopology.length() > 0) ? _overrideRootTopology.c_str() : (const char *)0));
+				SnodeEventCallback);
 
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 			_controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
@@ -1329,7 +1327,6 @@ private:
 	SqliteNetworkController *_controller;
 #endif
 	Phy<OneServiceImpl *> _phy;
-	std::string _overrideRootTopology;
 	Node *_node;
 	InetAddress _v4LocalAddress,_v6LocalAddress;
 	PhySocket *_v4UdpSocket;
@@ -1526,7 +1523,7 @@ std::string OneService::autoUpdateUrl()
 	return std::string();
 }
 
-OneService *OneService::newInstance(const char *hp,unsigned int port,const char *overrideRootTopology) { return new OneServiceImpl(hp,port,overrideRootTopology); }
+OneService *OneService::newInstance(const char *hp,unsigned int port) { return new OneServiceImpl(hp,port); }
 OneService::~OneService() {}
 
 } // namespace ZeroTier

+ 1 - 3
service/OneService.hpp

@@ -95,12 +95,10 @@ public:
 	 *
 	 * @param hp Home path
 	 * @param port TCP and UDP port for packets and HTTP control (if 0, pick random port)
-	 * @param overrideRootTopology String-serialized root topology (for testing, default: NULL)
 	 */
 	static OneService *newInstance(
 		const char *hp,
-		unsigned int port,
-		const char *overrideRootTopology = (const char *)0);
+		unsigned int port);
 
 	virtual ~OneService();