diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index 80091f623..f69ab54cd 100644
--- a/include/ZeroTierOne.h
+++ b/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
diff --git a/make-mac.mk b/make-mac.mk
index 6daa6aa0b..9fb613d88 100644
--- a/make-mac.mk
+++ b/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
diff --git a/mkworld.cpp b/mkworld.cpp
new file mode 100644
index 000000000..2b41d735f
--- /dev/null
+++ b/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 .
+ *
+ * --
+ *
+ * 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
+#include
+#include
+#include
+
+#include
+#include
+#include
+
+#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 &roots,const C25519::Pair &signWith)
+ {
+ WorldMaker w;
+ w._id = id;
+ w._ts = ts;
+ w._updateSigningKey = sk;
+ w._roots = roots;
+
+ Buffer 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 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 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;
+}
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 9fcc2e494..39abe720b 100644
--- a/node/IncomingPacket.cpp
+++ b/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"
diff --git a/node/Node.cpp b/node/Node.cpp
index 1eb219145..7496b045b 100644
--- a/node/Node.cpp
+++ b/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(new ZeroTier::Node(now,uptr,dataStoreGetFunction,dataStorePutFunction,wirePacketSendFunction,virtualNetworkFrameFunction,virtualNetworkConfigFunction,eventCallback,overrideRootTopology));
+ *node = reinterpret_cast(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;
diff --git a/node/Node.hpp b/node/Node.hpp
index 0ae176c0d..c7038ed40 100644
--- a/node/Node.hpp
+++ b/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();
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 958d0f3e7..939d84a51 100644
--- a/node/Packet.hpp
+++ b/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.
*/
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 5aedae868..0cf4cfe84 100644
--- a/node/Topology.cpp
+++ b/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 dswtmp(dsWorld.data(),dsWorld.length());
- _world.deserialize(dswtmp,0);
- } catch ( ... ) {
- _world = World(); // set to null if cached world is invalid
- }
- {
- World defaultWorld;
- Buffer 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(alls.data());
RR->node->dataStoreDelete("peers.save");
@@ -97,19 +80,24 @@ Topology::Topology(const RuntimeEnvironment *renv) :
clean(RR->node->now());
- for(std::vector::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 *rp = _peers.get(r->identity.address());
- if (rp) {
- _rootPeers.push_back(*rp);
- } else if (r->identity.address() != RR->identity.address()) {
- SharedPtr 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 dswtmp(dsWorld.data(),dsWorld.length());
+ cachedWorld.deserialize(dswtmp,0);
+ } catch ( ... ) {
+ cachedWorld = World(); // clear if cached world is invalid
}
+ World defaultWorld;
+ {
+ Buffer 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::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 *rp = _peers.get(r->identity.address());
+ if (rp) {
+ _rootPeers.push_back(*rp);
+ } else if (r->identity.address() != RR->identity.address()) {
+ SharedPtr newrp(new Peer(RR->identity,r->identity));
+ _peers.set(r->identity.address(),newrp);
+ _rootPeers.push_back(newrp);
+ }
+ }
+}
+
} // namespace ZeroTier
diff --git a/node/Topology.hpp b/node/Topology.hpp
index ed8f3d865..3abc27e4a 100644
--- a/node/Topology.hpp
+++ b/node/Topology.hpp
@@ -31,10 +31,10 @@
#include
#include
-#include