summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--attic/DBM.cpp243
-rw-r--r--attic/DBM.hpp168
-rw-r--r--attic/kubernetes_docs/.zerotierCliSettings (renamed from doc/ext/kubernetes/.zerotierCliSettings)0
-rw-r--r--attic/kubernetes_docs/Dockerfile (renamed from doc/ext/kubernetes/Dockerfile)0
-rw-r--r--attic/kubernetes_docs/README.md (renamed from doc/ext/kubernetes/README.md)0
-rw-r--r--attic/kubernetes_docs/entrypoint.sh (renamed from doc/ext/kubernetes/entrypoint.sh)0
-rw-r--r--attic/kubernetes_docs/server.js (renamed from doc/ext/kubernetes/server.js)0
-rw-r--r--attic/root-watcher/README.md (renamed from root-watcher/README.md)0
-rw-r--r--attic/root-watcher/config.json.example (renamed from root-watcher/config.json.example)0
-rw-r--r--attic/root-watcher/package.json (renamed from root-watcher/package.json)0
-rw-r--r--attic/root-watcher/schema.sql (renamed from root-watcher/schema.sql)0
-rw-r--r--attic/root-watcher/zerotier-root-watcher.js (renamed from root-watcher/zerotier-root-watcher.js)0
-rw-r--r--attic/tcp-proxy/Makefile (renamed from tcp-proxy/Makefile)0
-rw-r--r--attic/tcp-proxy/README.md (renamed from tcp-proxy/README.md)0
-rw-r--r--attic/tcp-proxy/tcp-proxy.cpp (renamed from tcp-proxy/tcp-proxy.cpp)0
-rw-r--r--include/ZeroTierOne.h419
-rw-r--r--make-mac.mk2
-rw-r--r--node/Buffer.hpp53
-rw-r--r--node/Constants.hpp6
-rw-r--r--node/Hashtable.hpp7
-rw-r--r--node/Network.cpp54
-rw-r--r--node/Network.hpp3
-rw-r--r--node/Node.cpp292
-rw-r--r--node/Node.hpp64
-rw-r--r--node/Topology.cpp70
-rw-r--r--osdep/Binder.hpp205
-rw-r--r--osdep/OSUtils.cpp2
-rw-r--r--service/OneService.cpp1300
-rw-r--r--service/OneService.hpp45
29 files changed, 1798 insertions, 1135 deletions
diff --git a/attic/DBM.cpp b/attic/DBM.cpp
new file mode 100644
index 00000000..54f017e0
--- /dev/null
+++ b/attic/DBM.cpp
@@ -0,0 +1,243 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2017 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.
+ */
+
+#include "DBM.hpp"
+
+#include "../version.h"
+
+#include "../node/Salsa20.hpp"
+#include "../node/Poly1305.hpp"
+#include "../node/SHA512.hpp"
+
+#include "../osdep/OSUtils.hpp"
+
+#define ZT_STORED_OBJECT_TYPE__CLUSTER_NODE_STATUS (ZT_STORED_OBJECT__MAX_TYPE_ID + 1)
+#define ZT_STORED_OBJECT_TYPE__CLUSTER_DEFINITION (ZT_STORED_OBJECT__MAX_TYPE_ID + 2)
+
+namespace ZeroTier {
+
+// We generate the cluster ID from our address and version info since this is
+// not at all designed to allow interoperation between versions (or endians)
+// in the same cluster.
+static inline uint64_t _mkClusterId(const Address &myAddress)
+{
+ uint64_t x = ZEROTIER_ONE_VERSION_MAJOR;
+ x <<= 8;
+ x += ZEROTIER_ONE_VERSION_MINOR;
+ x <<= 8;
+ x += ZEROTIER_ONE_VERSION_REVISION;
+ x <<= 40;
+ x ^= myAddress.toInt();
+#if __BYTE_ORDER == __BIG_ENDIAN
+ ++x;
+#endif;
+ return x;
+}
+
+void DBM::onUpdate(uint64_t from,const _MapKey &k,const _MapValue &v,uint64_t rev)
+{
+ char p[4096];
+ char tmp[ZT_DBM_MAX_VALUE_SIZE];
+ if (_persistentPath((ZT_StoredObjectType)k.type,k.key,p,sizeof(p))) {
+ // Reduce unnecessary disk writes
+ FILE *f = fopen(p,"r");
+ if (f) {
+ long n = (long)fread(tmp,1,sizeof(tmp),f);
+ fclose(f);
+ if ((n == (long)v.len)&&(!memcmp(v.data,tmp,n)))
+ return;
+ }
+
+ // Write to disk if file has changed or was not already present
+ f = fopen(p,"w");
+ if (f) {
+ if (fwrite(data,len,1,f) != 1)
+ fprintf(stderr,"WARNING: error writing to %s (I/O error)" ZT_EOL_S,p);
+ fclose(f);
+ if (type == ZT_STORED_OBJECT_IDENTITY_SECRET)
+ OSUtils::lockDownFile(p,false);
+ } else {
+ fprintf(stderr,"WARNING: error writing to %s (cannot open)" ZT_EOL_S,p);
+ }
+ }
+}
+
+void DBM::onDelete(uint64_t from,const _MapKey &k)
+{
+ char p[4096];
+ if (_persistentPath((ZT_StoredObjectType)k.type,k.key,p,sizeof(p)))
+ OSUtils::rm(p);
+}
+
+DBM::_vsdm_cryptor::_vsdm_cryptor(const Identity &secretIdentity)
+{
+ uint8_t s512[64];
+ SHA512::hash(h512,secretIdentity.privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+ memcpy(_key,s512,sizeof(_key));
+}
+
+void DBM::_vsdm_cryptor::encrypt(void *d,unsigned long l)
+{
+ if (l >= 24) { // sanity check
+ uint8_t key[32];
+ uint8_t authKey[32];
+ uint8_t auth[16];
+
+ uint8_t *const iv = reinterpret_cast<uint8_t *>(d) + (l - 16);
+ Utils::getSecureRandom(iv,16);
+ memcpy(key,_key,32);
+ for(unsigned long i=0;i<8;++i)
+ _key[i] ^= iv[i];
+
+ Salsa20 s20(key,iv + 8);
+ memset(authKey,0,32);
+ s20.crypt12(authKey,authKey,32);
+ s20.crypt12(d,d,l - 24);
+
+ Poly1305::compute(auth,d,l - 24,authKey);
+ memcpy(reinterpret_cast<uint8_t *>(d) + (l - 24),auth,8);
+ }
+}
+
+bool DBM::_vsdm_cryptor::decrypt(void *d,unsigned long l)
+{
+ if (l >= 24) { // sanity check
+ uint8_t key[32];
+ uint8_t authKey[32];
+ uint8_t auth[16];
+
+ uint8_t *const iv = reinterpret_cast<uint8_t *>(d) + (l - 16);
+ memcpy(key,_key,32);
+ for(unsigned long i=0;i<8;++i)
+ _key[i] ^= iv[i];
+
+ Salsa20 s20(key,iv + 8);
+ memset(authKey,0,32);
+ s20.crypt12(authKey,authKey,32);
+
+ Poly1305::compute(auth,d,l - 24,authKey);
+ if (!Utils::secureEq(reinterpret_cast<uint8_t *>(d) + (l - 24),auth,8))
+ return false;
+
+ s20.crypt12(d,d,l - 24);
+
+ return true;
+ }
+ return false;
+}
+
+DBM::DBM(const Identity &secretIdentity,uint64_t clusterMemberId,const std::string &basePath,Node *node) :
+ _basePath(basePath),
+ _node(node),
+ _startTime(OSUtils::now()),
+ _m(_mkClusterId(secretIdentity.address()),clusterMemberId,false,_vsdm_cryptor(secretIdentity),_vsdm_watcher(this))
+{
+}
+
+DBM::~DBM()
+{
+}
+
+void DBM::put(const ZT_StoredObjectType type,const uint64_t key,const void *data,unsigned int len)
+{
+ char p[4096];
+ if (_m.put(_MapKey(key,(uint16_t)type),Value(OSUtils::now(),(uint16_t)len,data))) {
+ if (_persistentPath(type,key,p,sizeof(p))) {
+ FILE *f = fopen(p,"w");
+ if (f) {
+ if (fwrite(data,len,1,f) != 1)
+ fprintf(stderr,"WARNING: error writing to %s (I/O error)" ZT_EOL_S,p);
+ fclose(f);
+ if (type == ZT_STORED_OBJECT_IDENTITY_SECRET)
+ OSUtils::lockDownFile(p,false);
+ } else {
+ fprintf(stderr,"WARNING: error writing to %s (cannot open)" ZT_EOL_S,p);
+ }
+ }
+ }
+}
+
+bool DBM::get(const ZT_StoredObjectType type,const uint64_t key,Value &value)
+{
+ char p[4096];
+ if (_m.get(_MapKey(key,(uint16_t)type),value))
+ return true;
+ if (_persistentPath(type,key,p,sizeof(p))) {
+ FILE *f = fopen(p,"r");
+ if (f) {
+ long n = (long)fread(value.data,1,sizeof(value.data),f);
+ value.len = (n > 0) ? (uint16_t)n : (uint16_t)0;
+ fclose(f);
+ value.ts = OSUtils::getLastModified(p);
+ _m.put(_MapKey(key,(uint16_t)type),value);
+ return true;
+ }
+ }
+ return false;
+}
+
+void DBM::del(const ZT_StoredObjectType type,const uint64_t key)
+{
+ char p[4096];
+ _m.del(_MapKey(key,(uint16_t)type));
+ if (_persistentPath(type,key,p,sizeof(p)))
+ OSUtils::rm(p);
+}
+
+void DBM::clean()
+{
+}
+
+bool DBM::_persistentPath(const ZT_StoredObjectType type,const uint64_t key,char *p,unsigned int maxlen)
+{
+ switch(type) {
+ case ZT_STORED_OBJECT_IDENTITY_PUBLIC:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "identity.public",_basePath.c_str());
+ return true;
+ case ZT_STORED_OBJECT_IDENTITY_SECRET:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "identity.secret",_basePath.c_str());
+ return true;
+ case ZT_STORED_OBJECT_IDENTITY:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "iddb.d" ZT_PATH_SEPARATOR_S "%.10llx",_basePath.c_str(),key);
+ return true;
+ case ZT_STORED_OBJECT_NETWORK_CONFIG:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.conf",_basePath.c_str(),key);
+ return true;
+ case ZT_STORED_OBJECT_PLANET:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "planet",_basePath.c_str());
+ return true;
+ case ZT_STORED_OBJECT_MOON:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "moons.d" ZT_PATH_SEPARATOR_S "%.16llx.moon",_basePath.c_str(),key);
+ return true;
+ case (ZT_StoredObjectType)ZT_STORED_OBJECT_TYPE__CLUSTER_DEFINITION:
+ Utils::snprintf(p,maxlen,"%s" ZT_PATH_SEPARATOR_S "cluster",_basePath.c_str());
+ return true;
+ default:
+ return false;
+ }
+}
+
+} // namespace ZeroTier
diff --git a/attic/DBM.hpp b/attic/DBM.hpp
new file mode 100644
index 00000000..c6d5b8c0
--- /dev/null
+++ b/attic/DBM.hpp
@@ -0,0 +1,168 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2017 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_DBM_HPP___
+#define ZT_DBM_HPP___
+
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <stdexcept>
+
+#include "../node/Constants.hpp"
+#include "../node/Mutex.hpp"
+#include "../node/Utils.hpp"
+#include "../node/Identity.hpp"
+#include "../node/Peer.hpp"
+
+#include "../ext/vsdm/vsdm.hpp"
+
+// The Peer is the largest structure we persist here
+#define ZT_DBM_MAX_VALUE_SIZE sizeof(Peer)
+
+namespace ZeroTier {
+
+class Node;
+class DBM;
+
+class DBM
+{
+public:
+ ZT_PACKED_STRUCT(struct Value
+ {
+ Value(const uint64_t t,const uint16_t l,const void *d) :
+ ts(t),
+ l(l)
+ {
+ memcpy(data,d,l);
+ }
+ uint64_t ts;
+ uint16_t len;
+ uint8_t data[ZT_DBM_MAX_VALUE_SIZE];
+ });
+
+private:
+ ZT_PACKED_STRUCT(struct _MapKey
+ {
+ _MapKey() : obj(0),type(0) {}
+ _MapKey(const uint16_t t,const uint64_t o) : obj(o),type(t) {}
+ uint64_t obj;
+ uint16_t type;
+ inline bool operator==(const _MapKey &k) const { return ((obj == k.obj)&&(type == k.type)); }
+ });
+ struct _MapHasher
+ {
+ inline std::size_t operator()(const _MapKey &k) const { return (std::size_t)((k.obj ^ (k.obj >> 32)) + (uint64_t)k.type); }
+ };
+
+ void onUpdate(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev);
+ void onDelete(uint64_t from,const _MapKey &k);
+
+ class _vsdm_watcher
+ {
+ public:
+ _vsdm_watcher(DBM *p) : _parent(p) {}
+ inline void add(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev) { _parent->onUpdate(from,k,v,rev); }
+ inline void update(uint64_t from,const _MapKey &k,const Value &v,uint64_t rev) { _parent->onUpdate(from,k,v,rev); }
+ inline void del(uint64_t from,const _MapKey &k) { _parent->onDelete(from,k); }
+ private:
+ DBM *_parent;
+ };
+ class _vsdm_serializer
+ {
+ public:
+ static inline unsigned long objectSize(const _MapKey &k) { return 10; }
+ static inline unsigned long objectSize(const Value &v) { return (10 + v.len); }
+ static inline const char *objectData(const _MapKey &k) { return reinterpret_cast<const char *>(&k); }
+ static inline const char *objectData(const Value &v) { return reinterpret_cast<const char *>(&v); }
+ static inline bool objectDeserialize(const char *d,unsigned long l,_MapKey &k)
+ {
+ if (l == 10) {
+ memcpy(&k,d,10);
+ return true;
+ }
+ return false;
+ }
+ static inline bool objectDeserialize(const char *d,unsigned long l,Value &v)
+ {
+ if ((l >= 10)&&(l <= (10 + ZT_DBM_MAX_VALUE_SIZE))) {
+ memcpy(&v,d,l);
+ return true;
+ }
+ return false;
+ }
+ };
+ class _vsdm_cryptor
+ {
+ public:
+ _vsdm_cryptor(const Identity &secretIdentity);
+ static inline unsigned long overhead() { return 24; }
+ void encrypt(void *d,unsigned long l);
+ bool decrypt(void *d,unsigned long l);
+ uint8_t _key[32];
+ };
+
+ typedef vsdm< _MapKey,Value,16384,_vsdm_watcher,_vsdm_serializer,_vsdm_cryptor,_MapHasher > _Map;
+
+ friend class _Map;
+
+public:
+ ZT_PACKED_STRUCT(struct ClusterPeerStatus
+ {
+ uint64_t startTime;
+ uint64_t currentTime;
+ uint64_t clusterPeersConnected;
+ uint64_t ztPeersConnected;
+ uint16_t platform;
+ uint16_t arch;
+ });
+
+ DBM(const Identity &secretIdentity,uint64_t clusterMemberId,const std::string &basePath,Node *node);
+
+ ~DBM();
+
+ void put(const ZT_StoredObjectType type,const uint64_t key,const void *data,unsigned int len);
+
+ bool get(const ZT_StoredObjectType type,const uint64_t key,Value &value);
+
+ void del(const ZT_StoredObjectType type,const uint64_t key);
+
+ void clean();
+
+private:
+ bool DBM::_persistentPath(const ZT_StoredObjectType type,const uint64_t key,char *p,unsigned int maxlen);
+
+ const std::string _basePath;
+ Node *const _node;
+ uint64_t _startTime;
+ _Map _m;
+};
+
+} // namespace ZeroTier
+
+#endif
diff --git a/doc/ext/kubernetes/.zerotierCliSettings b/attic/kubernetes_docs/.zerotierCliSettings
index 0e7df9b6..0e7df9b6 100644
--- a/doc/ext/kubernetes/.zerotierCliSettings
+++ b/attic/kubernetes_docs/.zerotierCliSettings
diff --git a/doc/ext/kubernetes/Dockerfile b/attic/kubernetes_docs/Dockerfile
index 6437a2bb..6437a2bb 100644
--- a/doc/ext/kubernetes/Dockerfile
+++ b/attic/kubernetes_docs/Dockerfile
diff --git a/doc/ext/kubernetes/README.md b/attic/kubernetes_docs/README.md
index 482e77e5..482e77e5 100644
--- a/doc/ext/kubernetes/README.md
+++ b/attic/kubernetes_docs/README.md
diff --git a/doc/ext/kubernetes/entrypoint.sh b/attic/kubernetes_docs/entrypoint.sh
index 80cd278e..80cd278e 100644
--- a/doc/ext/kubernetes/entrypoint.sh
+++ b/attic/kubernetes_docs/entrypoint.sh
diff --git a/doc/ext/kubernetes/server.js b/attic/kubernetes_docs/server.js
index a4b08bb8..a4b08bb8 100644
--- a/doc/ext/kubernetes/server.js
+++ b/attic/kubernetes_docs/server.js
diff --git a/root-watcher/README.md b/attic/root-watcher/README.md
index ded6a63f..ded6a63f 100644
--- a/root-watcher/README.md
+++ b/attic/root-watcher/README.md
diff --git a/root-watcher/config.json.example b/attic/root-watcher/config.json.example
index 0ad1bbe1..0ad1bbe1 100644
--- a/root-watcher/config.json.example
+++ b/attic/root-watcher/config.json.example
diff --git a/root-watcher/package.json b/attic/root-watcher/package.json
index d6e86d78..d6e86d78 100644
--- a/root-watcher/package.json
+++ b/attic/root-watcher/package.json
diff --git a/root-watcher/schema.sql b/attic/root-watcher/schema.sql
index ade0fa3e..ade0fa3e 100644
--- a/root-watcher/schema.sql
+++ b/attic/root-watcher/schema.sql
diff --git a/root-watcher/zerotier-root-watcher.js b/attic/root-watcher/zerotier-root-watcher.js
index d4607fc2..d4607fc2 100644
--- a/root-watcher/zerotier-root-watcher.js
+++ b/attic/root-watcher/zerotier-root-watcher.js
diff --git a/tcp-proxy/Makefile b/attic/tcp-proxy/Makefile
index af4e71e3..af4e71e3 100644
--- a/tcp-proxy/Makefile
+++ b/attic/tcp-proxy/Makefile
diff --git a/tcp-proxy/README.md b/attic/tcp-proxy/README.md
index 6f347d64..6f347d64 100644
--- a/tcp-proxy/README.md
+++ b/attic/tcp-proxy/README.md
diff --git a/tcp-proxy/tcp-proxy.cpp b/attic/tcp-proxy/tcp-proxy.cpp
index a7906aae..a7906aae 100644
--- a/tcp-proxy/tcp-proxy.cpp
+++ b/attic/tcp-proxy/tcp-proxy.cpp
diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index 5126c5a2..9c295cee 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -140,39 +140,6 @@ extern "C" {
#define ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH 7
/**
- * Maximum number of hops in a ZeroTier circuit test
- *
- * This is more or less the max that can be fit in a given packet (with
- * fragmentation) and only one address per hop.
- */
-#define ZT_CIRCUIT_TEST_MAX_HOPS 256
-
-/**
- * Maximum number of addresses per hop in a circuit test
- */
-#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 8
-
-/**
- * Circuit test report flag: upstream peer authorized in path (e.g. by network COM)
- */
-#define ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH 0x0000000000000001ULL
-
-/**
- * Maximum number of cluster members (and max member ID plus one)
- */
-#define ZT_CLUSTER_MAX_MEMBERS 128
-
-/**
- * Maximum number of physical ZeroTier addresses a cluster member can report
- */
-#define ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES 16
-
-/**
- * Maximum allowed cluster message length in bytes
- */
-#define ZT_CLUSTER_MAX_MESSAGE_LENGTH (1500 - 48)
-
-/**
* Maximum value for link quality (min is 0)
*/
#define ZT_PATH_LINK_QUALITY_MAX 0xff
@@ -286,22 +253,27 @@ enum ZT_ResultCode
*/
ZT_RESULT_OK = 0,
- // Fatal errors (>0, <1000)
+ /**
+ * Call produced no error but no action was taken
+ */
+ ZT_RESULT_OK_IGNORED = 1,
+
+ // Fatal errors (>100, <1000)
/**
* Ran out of memory
*/
- ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 1,
+ ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY = 100,
/**
* Data store is not writable or has failed
*/
- ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 2,
+ ZT_RESULT_FATAL_ERROR_DATA_STORE_FAILED = 101,
/**
* Internal error (e.g. unexpected exception indicating bug or build problem)
*/
- ZT_RESULT_FATAL_ERROR_INTERNAL = 3,
+ ZT_RESULT_FATAL_ERROR_INTERNAL = 102,
// Non-fatal errors (>1000)
@@ -325,7 +297,7 @@ enum ZT_ResultCode
* @param x Result code
* @return True if result code indicates a fatal error
*/
-#define ZT_ResultCode_isFatal(x) ((((int)(x)) > 0)&&(((int)(x)) < 1000))
+#define ZT_ResultCode_isFatal(x) ((((int)(x)) >= 100)&&(((int)(x)) < 1000))
/**
* Status codes sent to status update callback when things happen
@@ -421,6 +393,13 @@ enum ZT_Event
/**
* User message used with ZT_EVENT_USER_MESSAGE
+ *
+ * These are direct VL1 P2P messages for application use. Encryption and
+ * authentication in the ZeroTier protocol will guarantee the origin
+ * address and message content, but you are responsible for any other
+ * levels of authentication or access control that are required. Any node
+ * in the world can send you a user message! (Unless your network is air
+ * gapped.)
*/
typedef struct
{
@@ -748,24 +727,6 @@ typedef struct
} v;
} ZT_VirtualNetworkRule;
-typedef struct
-{
- /**
- * 128-bit ID (GUID) of this capability
- */
- uint64_t id[2];
-
- /**
- * Expiration time (measured vs. network config timestamp issued by controller)
- */
- uint64_t expiration;
-
- struct {
- uint64_t from;
- uint64_t to;
- } custody[ZT_MAX_CAPABILITY_CUSTODY_CHAIN_LENGTH];
-} ZT_VirtualNetworkCapability;
-
/**
* A route to be pushed on a virtual network
*/
@@ -1105,76 +1066,98 @@ typedef struct
} ZT_PeerList;
/**
- * A cluster member's status
+ * ZeroTier core state objects
+ *
+ * All of these objects can be persisted if desired. To preserve the
+ * identity of a node and its address, the identity (public and secret)
+ * must be saved at a minimum.
+ *
+ * The reference service implementation currently persists identity,
+ * peer identities (for a period of time), planet, moons, and network
+ * configurations. Other state is treated as ephemeral.
+ *
+ * All state objects should be replicated in cluster mode. The reference
+ * clustering implementation uses a rumor mill algorithm in which state
+ * updates that are accepted with RESULT_OK (but not RESULT_OK_IGNORED)
+ * are flooded to all connected cluster peers. This results in updates
+ * being flooded across the cluster until all cluster members have the
+ * latest.
*/
-typedef struct {
- /**
- * This cluster member's ID (from 0 to 1-ZT_CLUSTER_MAX_MEMBERS)
- */
- unsigned int id;
-
- /**
- * Number of milliseconds since last 'alive' heartbeat message received via cluster backplane address
- */
- unsigned int msSinceLastHeartbeat;
-
+enum ZT_StateObjectType
+{
/**
- * Non-zero if cluster member is alive
+ * Null object -- ignored
*/
- int alive;
+ ZT_STATE_OBJECT_NULL = 0,
/**
- * X, Y, and Z coordinates of this member (if specified, otherwise zero)
+ * Public address and public key
*
- * What these mean depends on the location scheme being used for
- * location-aware clustering. At present this is GeoIP and these
- * will be the X, Y, and Z coordinates of the location on a spherical
- * approximation of Earth where Earth's core is the origin (in km).
- * They don't have to be perfect and need only be comparable with others
- * to find shortest path via the standard vector distance formula.
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.public
+ * Persistence: required
*/
- int x,y,z;
+ ZT_STATE_OBJECT_IDENTITY_PUBLIC = 1,
/**
- * Cluster member's last reported load
+ * Full identity with secret key
+ *
+ * Object ID: this node's address if known, or 0 if unknown (first query)
+ * Canonical path: <HOME>/identity.secret
+ * Persistence: required, should be stored with restricted permissions e.g. mode 0600 on *nix
*/
- uint64_t load;
+ ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
/**
- * Number of peers
+ * A peer to which this node is communicating
+ *
+ * Object ID: peer address
+ * Canonical path: <HOME>/peers.d/<ADDRESS> (10-digit hex address)
+ * Persistence: optional, can be purged at any time
*/
- uint64_t peers;
+ ZT_STATE_OBJECT_PEER = 3,
/**
- * Physical ZeroTier endpoints for this member (where peers are sent when directed here)
+ * The identity of a known peer
+ *
+ * Object ID: peer address
+ * Canonical path: <HOME>/iddb.d/<ADDRESS> (10-digit hex address)
+ * Persistence: optional, can be purged at any time, recommended ttl 30-60 days
*/
- struct sockaddr_storage zeroTierPhysicalEndpoints[ZT_CLUSTER_MAX_ZT_PHYSICAL_ADDRESSES];
+ ZT_STATE_OBJECT_PEER_IDENTITY = 4,
/**
- * Number of physical ZeroTier endpoints this member is announcing
+ * Network configuration
+ *
+ * Object ID: peer address
+ * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID)
+ * Persistence: required if network memberships should persist
*/
- unsigned int numZeroTierPhysicalEndpoints;
-} ZT_ClusterMemberStatus;
+ ZT_STATE_OBJECT_NETWORK_CONFIG = 5,
-/**
- * ZeroTier cluster status
- */
-typedef struct {
/**
- * My cluster member ID (a record for 'self' is included in member[])
+ * The planet (there is only one per... well... planet!)
+ *
+ * Object ID: world ID of planet, or 0 if unknown (first query)
+ * Canonical path: <HOME>/planet
+ * Persistence: recommended
*/
- unsigned int myId;
+ ZT_STATE_OBJECT_PLANET = 6,
/**
- * Number of cluster members
+ * A moon (federated root set)
+ *
+ * Object ID: world ID of moon
+ * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
+ * Persistence: required if moon memberships should persist
*/
- unsigned int clusterSize;
+ ZT_STATE_OBJECT_MOON = 7,
/**
- * Cluster member statuses
+ * IDs above this value will not be used by the core (and could be used as implementation-specific IDs)
*/
- ZT_ClusterMemberStatus members[ZT_CLUSTER_MAX_MEMBERS];
-} ZT_ClusterStatus;
+ ZT_STATE_OBJECT__MAX_ID = 255
+};
/**
* An instance of a ZeroTier One node (opaque)
@@ -1252,62 +1235,41 @@ typedef void (*ZT_EventCallback)(
const void *); /* Event payload (if applicable) */
/**
- * Function to get an object from the data store
- *
- * Parameters: (1) object name, (2) buffer to fill, (3) size of buffer, (4)
- * index in object to start reading, (5) result parameter that must be set
- * to the actual size of the object if it exists.
+ * Callback for storing and/or publishing state information
*
- * Object names can contain forward slash (/) path separators. They will
- * never contain .. or backslash (\), so this is safe to map as a Unix-style
- * path if the underlying storage permits. For security reasons we recommend
- * returning errors if .. or \ are used.
+ * See ZT_StateObjectType docs for information about each state object type
+ * and when and if it needs to be persisted.
*
- * The function must return the actual number of bytes read. If the object
- * doesn't exist, it should return -1. -2 should be returned on other errors
- * such as errors accessing underlying storage.
- *
- * If the read doesn't fit in the buffer, the max number of bytes should be
- * read. The caller may call the function multiple times to read the whole
- * object.
+ * An object of length -1 is sent to indicate that an object should be
+ * deleted.
*/
-typedef long (*ZT_DataStoreGetFunction)(
+typedef void (*ZT_StatePutFunction)(
ZT_Node *, /* Node */
void *, /* User ptr */
void *, /* Thread ptr */
- const char *,
- void *,
- unsigned long,
- unsigned long,
- unsigned long *);
+ enum ZT_StateObjectType, /* State object type */
+ uint64_t, /* State object ID (if applicable) */
+ const void *, /* State object data */
+ int); /* Length of data or -1 to delete */
/**
- * Function to store an object in the data store
- *
- * Parameters: (1) node, (2) user ptr, (3) object name, (4) object data,
- * (5) object size, (6) secure? (bool).
- *
- * If secure is true, the file should be set readable and writable only
- * to the user running ZeroTier One. What this means is platform-specific.
+ * Callback for retrieving stored state information
*
- * Name semantics are the same as the get function. This must return zero on
- * success. You can return any OS-specific error code on failure, as these
- * may be visible in logs or error messages and might aid in debugging.
- *
- * If the data pointer is null, this must be interpreted as a delete
- * operation.
+ * This function should return the number of bytes actually stored to the
+ * buffer or -1 if the state object was not found or the buffer was too
+ * small to store it.
*/
-typedef int (*ZT_DataStorePutFunction)(
- ZT_Node *,
- void *,
+typedef int (*ZT_StateGetFunction)(
+ ZT_Node *, /* Node */
+ void *, /* User ptr */
void *, /* Thread ptr */
- const char *,
- const void *,
- unsigned long,
- int);
+ enum ZT_StateObjectType, /* State object type */
+ uint64_t, /* State object ID (if applicable) */
+ void *, /* Buffer to store state object data */
+ unsigned int); /* Length of data buffer in bytes */
/**
- * Function to send a ZeroTier packet out over the wire
+ * Function to send a ZeroTier packet out over the physical wire (L2/L3)
*
* Parameters:
* (1) Node
@@ -1362,9 +1324,6 @@ typedef int (*ZT_WirePacketSendFunction)(
* all configured ZeroTier interfaces and check to ensure that the supplied
* addresses will not result in ZeroTier traffic being sent over a ZeroTier
* interface (recursion).
- *
- * Obviously this is not required in configurations where this can't happen,
- * such as network containers or embedded.
*/
typedef int (*ZT_PathCheckFunction)(
ZT_Node *, /* Node */
@@ -1412,14 +1371,14 @@ struct ZT_Node_Callbacks
long version;
/**
- * REQUIRED: Function to get objects from persistent storage
+ * REQUIRED: Function to store and/or replicate state objects
*/
- ZT_DataStoreGetFunction dataStoreGetFunction;
+ ZT_StatePutFunction statePutFunction;
/**
- * REQUIRED: Function to store objects in persistent storage
+ * REQUIRED: Function to retrieve state objects from an object store
*/
- ZT_DataStorePutFunction dataStorePutFunction;
+ ZT_StateGetFunction stateGetFunction;
/**
* REQUIRED: Function to send packets over the physical wire
@@ -1453,13 +1412,12 @@ struct ZT_Node_Callbacks
};
/**
- * Create a new ZeroTier One node
+ * Create a new ZeroTier node
*
- * Note that this can take a few seconds the first time it's called, as it
- * will generate an identity.
- *
- * TODO: should consolidate function pointers into versioned structure for
- * better API stability.
+ * This will attempt to load its identity via the state get function in the
+ * callback struct. If that fails it will generate a new identity and store
+ * it. Identity generation can take anywhere from a few hundred milliseconds
+ * to a few seconds depending on your CPU speed.
*
* @param node Result: pointer is set to new node instance on success
* @param uptr User pointer to pass to functions/callbacks
@@ -1481,6 +1439,49 @@ enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct
void ZT_Node_delete(ZT_Node *node);
/**
+ * Notify node of an update to a state object
+ *
+ * This can be called after node startup to restore cached state objects such
+ * as network configurations for joined networks, planet, moons, etc. See
+ * the documentation of ZT_StateObjectType for more information. It's okay
+ * to call this for everything in the object store, but note that the node
+ * will automatically query for some core objects like identities so supplying
+ * these via this function is not necessary.
+ *
+ * Unless clustering is being implemented this function doesn't need to be
+ * used after startup. It could be called in response to filesystem changes
+ * to allow some degree of live configurability by filesystem observation.
+ *
+ * The return value of this function indicates whether the update was accepted
+ * as new. A return value of ZT_RESULT_OK indicates that the node gleaned new
+ * information from this update and that therefore (in cluster rumor mill mode)
+ * this update should be distributed to other members of a cluster. A return
+ * value of ZT_RESULT_OK_IGNORED indicates that the object did not provide any
+ * new information and therefore should not be propagated in a cluster.
+ *
+ * If clustering isn't being implemented the return value of this function can
+ * generally be ignored.
+ *
+ * ZT_RESULT_ERROR_BAD_PARAMETER can be returned if the parameter was invalid
+ * or not applicable. Object stores may delete the object in this case.
+ *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
+ * @param type State object type
+ * @param id State object ID
+ * @param data State object data
+ * @param len Length of state object data in bytes
+ * @return ZT_RESULT_OK if object was accepted or ZT_RESULT_OK_IGNORED if non-informative, error if object was invalid
+ */
+enum ZT_ResultCode ZT_Node_processStateUpdate(
+ ZT_Node *node,
+ void *tptr,
+ ZT_StateObjectType type,
+ uint64_t id,
+ const void *data,
+ unsigned int len);
+
+/**
* Process a packet received from the physical wire
*
* @param node Node instance
@@ -1766,116 +1767,6 @@ int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t type
void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
/**
- * Initialize cluster operation
- *
- * This initializes the internal structures and state for cluster operation.
- * It takes two function pointers. The first is to a function that can be
- * used to send data to cluster peers (mechanism is not defined by Node),
- * and the second is to a function that can be used to get the location of
- * a physical address in X,Y,Z coordinate space (e.g. as cartesian coordinates
- * projected from the center of the Earth).
- *
- * Send function takes an arbitrary pointer followed by the cluster member ID
- * to send data to, a pointer to the data, and the length of the data. The
- * maximum message length is ZT_CLUSTER_MAX_MESSAGE_LENGTH (65535). Messages
- * must be delivered whole and may be dropped or transposed, though high
- * failure rates are undesirable and can cause problems. Validity checking or
- * CRC is also not required since the Node validates the authenticity of
- * cluster messages using cryptogrphic methods and will silently drop invalid
- * messages.
- *
- * Address to location function is optional and if NULL geo-handoff is not
- * enabled (in this case x, y, and z in clusterInit are also unused). It
- * takes an arbitrary pointer followed by a physical address and three result
- * parameters for x, y, and z. It returns zero on failure or nonzero if these
- * three coordinates have been set. Coordinate space is arbitrary and can be
- * e.g. coordinates on Earth relative to Earth's center. These can be obtained
- * from latitutde and longitude with versions of the Haversine formula.
- *
- * See: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
- *
- * Neither the send nor the address to location function should block. If the
- * address to location function does not have a location for an address, it
- * should return zero and then look up the address for future use since it
- * will be called again in (typically) 1-3 minutes.
- *
- * Note that both functions can be called from any thread from which the
- * various Node functions are called, and so must be thread safe if multiple
- * threads are being used.
- *
- * @param node Node instance
- * @param myId My cluster member ID (less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @param zeroTierPhysicalEndpoints Preferred physical address(es) for ZeroTier clients to contact this cluster member (for peer redirect)
- * @param numZeroTierPhysicalEndpoints Number of physical endpoints in zeroTierPhysicalEndpoints[] (max allowed: 255)
- * @param x My cluster member's X location
- * @param y My cluster member's Y location
- * @param z My cluster member's Z location
- * @param sendFunction Function to be called to send data to other cluster members
- * @param sendFunctionArg First argument to sendFunction()
- * @param addressToLocationFunction Function to be called to get the location of a physical address or NULL to disable geo-handoff
- * @param addressToLocationFunctionArg First argument to addressToLocationFunction()
- * @return OK or UNSUPPORTED_OPERATION if this Node was not built with cluster support
- */
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
-
-/**
- * Add a member to this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID (must be less than or equal to ZT_CLUSTER_MAX_MEMBERS)
- * @return OK or error if clustering is disabled, ID invalid, etc.
- */
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Remove a member from this cluster
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param memberId Member ID to remove (nothing happens if not present)
- */
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId);
-
-/**
- * Handle an incoming cluster state message
- *
- * The message itself contains cluster member IDs, and invalid or badly
- * addressed messages will be silently discarded.
- *
- * Calling this without having called clusterInit() will do nothing.
- *
- * @param node Node instance
- * @param msg Cluster message
- * @param len Length of cluster message
- */
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len);
-
-/**
- * Get the current status of the cluster from this node's point of view
- *
- * Calling this without clusterInit() or without cluster support will just
- * zero out the structure and show a cluster size of zero.
- *
- * @param node Node instance
- * @param cs Cluster status structure to fill with data
- */
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
-
-/**
* Set trusted paths
*
* A trusted path is a physical network (network/bits) over which both
diff --git a/make-mac.mk b/make-mac.mk
index de66f49c..5622a41b 100644
--- a/make-mac.mk
+++ b/make-mac.mk
@@ -54,7 +54,7 @@ ifeq ($(ZT_DEBUG),1)
# C25519 in particular is almost UNUSABLE in heavy testing without it.
node/Salsa20.o node/SHA512.o node/C25519.o node/Poly1305.o: CFLAGS = -Wall -O2 -g $(INCLUDES) $(DEFS)
else
- CFLAGS?=-Ofast -fstack-protector-strong
+ CFLAGS?=-Os -fstack-protector-strong
CFLAGS+=$(ARCH_FLAGS) -Wall -Werror -flto -fPIE -mmacosx-version-min=10.7 -DNDEBUG -Wno-unused-private-field $(INCLUDES) $(DEFS)
STRIP=strip
endif
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 8e6b78fd..69ee1758 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -93,7 +93,6 @@ public:
}
Buffer(unsigned int l)
- throw(std::out_of_range)
{
if (l > C)
throw std::out_of_range("Buffer: construct with size larger than capacity");
@@ -102,51 +101,42 @@ public:
template<unsigned int C2>
Buffer(const Buffer<C2> &b)
- throw(std::out_of_range)
{
*this = b;
}
Buffer(const void *b,unsigned int l)
- throw(std::out_of_range)
{
copyFrom(b,l);
}
Buffer(const std::string &s)
- throw(std::out_of_range)
{
copyFrom(s.data(),s.length());
}
template<unsigned int C2>
inline Buffer &operator=(const Buffer<C2> &b)
- throw(std::out_of_range)
{
if (unlikely(b._l > C))
throw std::out_of_range("Buffer: assignment from buffer larger than capacity");
- memcpy(_b,b._b,_l = b._l);
- return *this;
- }
-
- inline Buffer &operator=(const std::string &s)
- throw(std::out_of_range)
- {
- copyFrom(s.data(),s.length());
+ if (C2 == C) {
+ memcpy(this,&b,sizeof(Buffer<C>));
+ } else {
+ memcpy(_b,b._b,_l = b._l);
+ }
return *this;
}
inline void copyFrom(const void *b,unsigned int l)
- throw(std::out_of_range)
{
if (unlikely(l > C))
throw std::out_of_range("Buffer: set from C array larger than capacity");
- _l = l;
memcpy(_b,b,l);
+ _l = l;
}
unsigned char operator[](const unsigned int i) const
- throw(std::out_of_range)
{
if (unlikely(i >= _l))
throw std::out_of_range("Buffer: [] beyond end of data");
@@ -154,7 +144,6 @@ public:
}
unsigned char &operator[](const unsigned int i)
- throw(std::out_of_range)
{
if (unlikely(i >= _l))
throw std::out_of_range("Buffer: [] beyond end of data");
@@ -175,14 +164,12 @@ public:
* @throws std::out_of_range Field extends beyond data size
*/
unsigned char *field(unsigned int i,unsigned int l)
- throw(std::out_of_range)
{
if (unlikely((i + l) > _l))
throw std::out_of_range("Buffer: field() beyond end of data");
return (unsigned char *)(_b + i);
}
const unsigned char *field(unsigned int i,unsigned int l) const
- throw(std::out_of_range)
{
if (unlikely((i + l) > _l))
throw std::out_of_range("Buffer: field() beyond end of data");
@@ -198,7 +185,6 @@ public:
*/
template<typename T>
inline void setAt(unsigned int i,const T v)
- throw(std::out_of_range)
{
if (unlikely((i + sizeof(T)) > _l))
throw std::out_of_range("Buffer: setAt() beyond end of data");
@@ -221,7 +207,6 @@ public:
*/
template<typename T>
inline T at(unsigned int i) const
- throw(std::out_of_range)
{
if (unlikely((i + sizeof(T)) > _l))
throw std::out_of_range("Buffer: at() beyond end of data");
@@ -248,7 +233,6 @@ public:
*/
template<typename T>
inline void append(const T v)
- throw(std::out_of_range)
{
if (unlikely((_l + sizeof(T)) > C))
throw std::out_of_range("Buffer: append beyond capacity");
@@ -271,7 +255,6 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(unsigned char c,unsigned int n)
- throw(std::out_of_range)
{
if (unlikely((_l + n) > C))
throw std::out_of_range("Buffer: append beyond capacity");
@@ -280,6 +263,19 @@ public:
}
/**
+ * Append secure random bytes
+ *
+ * @param n Number of random bytes to append
+ */
+ inline void appendRandom(unsigned int n)
+ {
+ if (unlikely((_l + n) > C))
+ throw std::out_of_range("Buffer: append beyond capacity");
+ Utils::getSecureRandom(_b + _l,n);
+ _l += n;
+ }
+
+ /**
* Append a C-array of bytes
*
* @param b Data
@@ -287,7 +283,6 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(const void *b,unsigned int l)
- throw(std::out_of_range)
{
if (unlikely((_l + l) > C))
throw std::out_of_range("Buffer: append beyond capacity");
@@ -302,7 +297,6 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void append(const std::string &s)
- throw(std::out_of_range)
{
append(s.data(),(unsigned int)s.length());
}
@@ -314,7 +308,6 @@ public:
* @throws std::out_of_range Attempt to append beyond capacity
*/
inline void appendCString(const char *s)
- throw(std::out_of_range)
{
for(;;) {
if (unlikely(_l >= C))
@@ -333,7 +326,6 @@ public:
*/
template<unsigned int C2>
inline void append(const Buffer<C2> &b)
- throw(std::out_of_range)
{
append(b._b,b._l);
}
@@ -349,7 +341,6 @@ public:
* @return Pointer to beginning of appended field of length 'l'
*/
inline char *appendField(unsigned int l)
- throw(std::out_of_range)
{
if (unlikely((_l + l) > C))
throw std::out_of_range("Buffer: append beyond capacity");
@@ -367,7 +358,6 @@ public:
* @throws std::out_of_range Capacity exceeded
*/
inline void addSize(unsigned int i)
- throw(std::out_of_range)
{
if (unlikely((i + _l) > C))
throw std::out_of_range("Buffer: setSize to larger than capacity");
@@ -383,7 +373,6 @@ public:
* @throws std::out_of_range Size larger than capacity
*/
inline void setSize(const unsigned int i)
- throw(std::out_of_range)
{
if (unlikely(i > C))
throw std::out_of_range("Buffer: setSize to larger than capacity");
@@ -397,7 +386,6 @@ public:
* @throw std::out_of_range Position is beyond size of buffer
*/
inline void behead(const unsigned int at)
- throw(std::out_of_range)
{
if (!at)
return;
@@ -414,7 +402,6 @@ public:
* @throw std::out_of_range Position plus length is beyond size of buffer
*/
inline void erase(const unsigned int at,const unsigned int length)
- throw(std::out_of_range)
{
const unsigned int endr = at + length;
if (unlikely(endr > _l))
@@ -495,8 +482,8 @@ public:
}
private:
- unsigned int _l;
char ZT_VAR_MAY_ALIAS _b[C];
+ unsigned int _l;
};
} // namespace ZeroTier
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 3974f0ec..fbbba76e 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -150,6 +150,12 @@
#endif
#endif
+#ifdef __WINDOWS__
+#define ZT_PACKED_STRUCT(D) __pragma(pack(push,1)) D __pragma(pack(pop))
+#else
+#define ZT_PACKED_STRUCT(D) D __attribute__((packed))
+#endif
+
/**
* Length of a ZeroTier address in bytes
*/
diff --git a/node/Hashtable.hpp b/node/Hashtable.hpp
index c46ed68f..b702f608 100644
--- a/node/Hashtable.hpp
+++ b/node/Hashtable.hpp
@@ -374,12 +374,7 @@ private:
}
static inline unsigned long _hc(const uint64_t i)
{
- /* NOTE: this assumes that 'i' is evenly distributed, which is the case for
- * packet IDs and network IDs -- the two use cases in ZT for uint64_t keys.
- * These values are also greater than 0xffffffff so they'll map onto a full
- * bucket count just fine no matter what happens. Normally you'd want to
- * hash an integer key index in a hash table. */
- return (unsigned long)i;
+ return (unsigned long)(i ^ (i >> 32)); // good for network IDs and addresses
}
static inline unsigned long _hc(const uint32_t i)
{
diff --git a/node/Network.cpp b/node/Network.cpp
index de2ea7d7..74d81941 100644
--- a/node/Network.cpp
+++ b/node/Network.cpp
@@ -682,7 +682,7 @@ static _doZtFilterResult _doZtFilter(
const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
-Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr) :
+Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf) :
RR(renv),
_uPtr(uptr),
_id(nwid),
@@ -697,29 +697,30 @@ Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *u
for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
_incomingConfigChunks[i].ts = 0;
- char confn[128];
- Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
-
- bool gotConf = false;
- Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
- NetworkConfig *nconf = new NetworkConfig();
- try {
- std::string conf(RR->node->dataStoreGet(tPtr,confn));
- if (conf.length()) {
- dconf->load(conf.c_str());
- if (nconf->fromDictionary(*dconf)) {
- this->setConfiguration(tPtr,*nconf,false);
- _lastConfigUpdate = 0; // we still want to re-request a new config from the network
- gotConf = true;
+ if (nconf) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request since it's likely outdated
+ } else {
+ bool got = false;
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
+ try {
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,dict->unsafeData(),ZT_NETWORKCONFIG_DICT_CAPACITY - 1);
+ if (n > 1) {
+ NetworkConfig *nconf = new NetworkConfig();
+ try {
+ if (nconf->fromDictionary(*dict)) {
+ this->setConfiguration(tPtr,*nconf,false);
+ _lastConfigUpdate = 0; // still want to re-request an update since it's likely outdated
+ got = true;
+ }
+ } catch ( ... ) {}
+ delete nconf;
}
- }
- } catch ( ... ) {} // ignore invalids, we'll re-request
- delete nconf;
- delete dconf;
+ } catch ( ... ) {}
+ delete dict;
- if (!gotConf) {
- // Save a one-byte CR to persist membership while we request a real netconf
- RR->node->dataStorePut(tPtr,confn,"\n",1,false);
+ if (!got)
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,"\n",1);
}
if (!_portInitialized) {
@@ -735,12 +736,9 @@ Network::~Network()
ZT_VirtualNetworkConfig ctmp;
_externalConfig(&ctmp);
- char n[128];
if (_destroyed) {
- // This is done in Node::leave() so we can pass tPtr
+ // This is done in Node::leave() so we can pass tPtr properly
//RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
- RR->node->dataStoreDelete((void *)0,n);
} else {
RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
}
@@ -1188,10 +1186,8 @@ int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToD
if (saveToDisk) {
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
- char n[64];
- Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
if (nconf.toDictionary(*d,false))
- RR->node->dataStorePut(tPtr,n,(const void *)d->data(),d->sizeBytes(),true);
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_NETWORK_CONFIG,_id,d->data(),d->sizeBytes());
} catch ( ... ) {}
delete d;
}
diff --git a/node/Network.hpp b/node/Network.hpp
index cce6c41f..454a3f20 100644
--- a/node/Network.hpp
+++ b/node/Network.hpp
@@ -88,8 +88,9 @@ public:
* @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
* @param nwid Network ID
* @param uptr Arbitrary pointer used by externally-facing API (for user use)
+ * @param nconf Network config, if known
*/
- Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr);
+ Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr,const NetworkConfig *nconf);
~Network();
diff --git a/node/Node.cpp b/node/Node.cpp
index 911c9c4b..37586834 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -33,6 +33,7 @@
#include "../version.h"
#include "Constants.hpp"
+#include "SharedPtr.hpp"
#include "Node.hpp"
#include "RuntimeEnvironment.hpp"
#include "NetworkController.hpp"
@@ -45,6 +46,7 @@
#include "Identity.hpp"
#include "SelfAwareness.hpp"
#include "Cluster.hpp"
+#include "Network.hpp"
const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
@@ -58,6 +60,7 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6
_RR(this),
RR(&_RR),
_uPtr(uptr),
+ _networks(8),
_now(now),
_lastPingCheck(0),
_lastHousekeepingRun(0)
@@ -74,20 +77,31 @@ Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint6
memset(_expectingRepliesTo,0,sizeof(_expectingRepliesTo));
memset(_lastIdentityVerification,0,sizeof(_lastIdentityVerification));
- std::string idtmp(dataStoreGet(tptr,"identity.secret"));
- if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
- TRACE("identity.secret not found, generating...");
- RR->identity.generate();
- idtmp = RR->identity.toString(true);
- if (!dataStorePut(tptr,"identity.secret",idtmp,true))
- throw std::runtime_error("unable to write identity.secret");
+ char tmp[512];
+ std::string tmp2;
+ int n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,0,tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
+ if (!RR->identity.fromString(tmp))
+ n = -1;
}
- RR->publicIdentityStr = RR->identity.toString(false);
- RR->secretIdentityStr = RR->identity.toString(true);
- idtmp = dataStoreGet(tptr,"identity.public");
- if (idtmp != RR->publicIdentityStr) {
- if (!dataStorePut(tptr,"identity.public",RR->publicIdentityStr,false))
- throw std::runtime_error("unable to write identity.public");
+ if (n <= 0) {
+ RR->identity.generate();
+ tmp2 = RR->identity.toString(true);
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_SECRET,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length());
+ tmp2 = RR->identity.toString(false);
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length());
+ } else {
+ n = stateObjectGet(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
+ if (RR->identity.toString(false) != tmp)
+ n = -1;
+ }
+ if (n <= 0) {
+ tmp2 = RR->identity.toString(false);
+ stateObjectPut(tptr,ZT_STATE_OBJECT_IDENTITY_PUBLIC,RR->identity.address().toInt(),tmp2.data(),(unsigned int)tmp2.length());
+ }
}
try {
@@ -110,7 +124,7 @@ Node::~Node()
{
Mutex::Lock _l(_networks_m);
- _networks.clear(); // ensure that networks are destroyed before shutdow
+ _networks.clear(); // destroy all networks before shutdown
delete RR->sa;
delete RR->topology;
@@ -122,6 +136,97 @@ Node::~Node()
#endif
}
+ZT_ResultCode Node::processStateUpdate(
+ void *tptr,
+ ZT_StateObjectType type,
+ uint64_t id,
+ const void *data,
+ unsigned int len)
+{
+ ZT_ResultCode r = ZT_RESULT_OK_IGNORED;
+ switch(type) {
+
+ case ZT_STATE_OBJECT_PEER:
+ if (len) {
+ }
+ break;
+
+ case ZT_STATE_OBJECT_PEER_IDENTITY:
+ if (len) {
+ }
+ break;
+
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ if (len <= (ZT_NETWORKCONFIG_DICT_CAPACITY - 1)) {
+ if (len < 2) {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> &nw = _networks[id];
+ if (!nw) {
+ nw = SharedPtr<Network>(new Network(RR,tptr,id,(void *)0,(const NetworkConfig *)0));
+ r = ZT_RESULT_OK;
+ }
+ } else {
+ Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(reinterpret_cast<const char *>(data),len);
+ try {
+ NetworkConfig *nconf = new NetworkConfig();
+ try {
+ if (nconf->fromDictionary(*dict)) {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> &nw = _networks[id];
+ if (nw) {
+ switch (nw->setConfiguration(tptr,*nconf,false)) {
+ default:
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ break;
+ case 1:
+ r = ZT_RESULT_OK_IGNORED;
+ break;
+ case 2:
+ r = ZT_RESULT_OK;
+ break;
+ }
+ } else {
+ nw = SharedPtr<Network>(new Network(RR,tptr,id,(void *)0,nconf));
+ }
+ } else {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ } catch ( ... ) {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ delete nconf;
+ } catch ( ... ) {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ delete dict;
+ }
+ } else {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ break;
+
+ case ZT_STATE_OBJECT_PLANET:
+ case ZT_STATE_OBJECT_MOON:
+ if (len <= ZT_WORLD_MAX_SERIALIZED_LENGTH) {
+ World w;
+ try {
+ w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(data,len));
+ if (( (w.type() == World::TYPE_MOON)&&(type == ZT_STATE_OBJECT_MOON) )||( (w.type() == World::TYPE_PLANET)&&(type == ZT_STATE_OBJECT_PLANET) )) {
+ r = (RR->topology->addWorld(tptr,w,false)) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED;
+ }
+ } catch ( ... ) {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ } else {
+ r = ZT_RESULT_ERROR_BAD_PARAMETER;
+ }
+ break;
+
+ default: break;
+ }
+ return r;
+}
+
ZT_ResultCode Node::processWirePacket(
void *tptr,
uint64_t now,
@@ -238,10 +343,13 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint
std::vector< SharedPtr<Network> > needConfig;
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
- needConfig.push_back(n->second);
- n->second->sendUpdatesToMembers(tptr);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v)) {
+ if (((now - (*v)->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!(*v)->hasConfig()))
+ needConfig.push_back(*v);
+ (*v)->sendUpdatesToMembers(tptr);
}
}
for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
@@ -306,37 +414,38 @@ ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint
ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(!nw) {
- const std::pair< uint64_t,SharedPtr<Network> > nn(nwid,SharedPtr<Network>(new Network(RR,tptr,nwid,uptr)));
- _networks.insert(std::upper_bound(_networks.begin(),_networks.end(),nn),nn);
- }
+ SharedPtr<Network> &nw = _networks[nwid];
+ if (!nw)
+ nw = SharedPtr<Network>(new Network(RR,tptr,nwid,uptr,(const NetworkConfig *)0));
return ZT_RESULT_OK;
}
ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
{
ZT_VirtualNetworkConfig ctmp;
- std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
void **nUserPtr = (void **)0;
- Mutex::Lock _l(_networks_m);
-
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
- if (n->first != nwid) {
- newn.push_back(*n);
- } else {
- if (uptr)
- *uptr = *n->second->userPtr();
- n->second->externalConfig(&ctmp);
- n->second->destroy();
- nUserPtr = n->second->userPtr();
- }
+ {
+ Mutex::Lock _l(_networks_m);
+ SharedPtr<Network> *nw = _networks.get(nwid);
+ if (!nw)
+ return ZT_RESULT_OK;
+ if (uptr)
+ *uptr = (*nw)->userPtr();
+ (*nw)->externalConfig(&ctmp);
+ (*nw)->destroy();
+ nUserPtr = (*nw)->userPtr();
}
- _networks.swap(newn);
-
+
if (nUserPtr)
RR->node->configureVirtualNetworkPort(tptr,nwid,nUserPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
+ {
+ Mutex::Lock _l(_networks_m);
+ _networks.erase(nwid);
+ }
+
+ RR->node->stateObjectDelete(tptr,ZT_STATE_OBJECT_NETWORK_CONFIG,nwid);
+
return ZT_RESULT_OK;
}
@@ -431,10 +540,10 @@ ZT_PeerList *Node::peers() const
ZT_VirtualNetworkConfig *Node::networkConfig(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- SharedPtr<Network> nw = _network(nwid);
- if(nw) {
+ const SharedPtr<Network> *nw = _networks.get(nwid);
+ if (nw) {
ZT_VirtualNetworkConfig *nc = (ZT_VirtualNetworkConfig *)::malloc(sizeof(ZT_VirtualNetworkConfig));
- nw->externalConfig(nc);
+ (*nw)->externalConfig(nc);
return nc;
}
return (ZT_VirtualNetworkConfig *)0;
@@ -451,8 +560,11 @@ ZT_VirtualNetworkList *Node::networks() const
nl->networks = (ZT_VirtualNetworkConfig *)(buf + sizeof(ZT_VirtualNetworkList));
nl->networkCount = 0;
- for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n)
- n->second->externalConfig(&(nl->networks[nl->networkCount++]));
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > *>(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ (*v)->externalConfig(&(nl->networks[nl->networkCount++]));
return nl;
}
@@ -503,6 +615,7 @@ void Node::setNetconfMaster(void *networkControllerInstance)
RR->localNetworkController->init(RR->identity,this);
}
+/*
ZT_ResultCode Node::clusterInit(
unsigned int myId,
const struct sockaddr_storage *zeroTierPhysicalEndpoints,
@@ -570,25 +683,12 @@ void Node::clusterStatus(ZT_ClusterStatus *cs)
#endif
memset(cs,0,sizeof(ZT_ClusterStatus));
}
+*/
/****************************************************************************/
/* Node methods used only within node/ */
/****************************************************************************/
-std::string Node::dataStoreGet(void *tPtr,const char *name)
-{
- char buf[1024];
- std::string r;
- unsigned long olen = 0;
- do {
- long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
- if (n <= 0)
- return std::string();
- r.append(buf,n);
- } while (r.length() < olen);
- return r;
-}
-
bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
{
if (!Path::isAddressValidForPath(remoteAddress))
@@ -599,10 +699,13 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->second->hasConfig()) {
- for(unsigned int k=0;k<i->second->config().staticIpCount;++k) {
- if (i->second->config().staticIps[k].containsAddress(remoteAddress))
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(_networks);
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v)) {
+ if ((*v)->hasConfig()) {
+ for(unsigned int k=0;k<(*v)->config().staticIpCount;++k) {
+ if ((*v)->config().staticIps[k].containsAddress(remoteAddress))
return false;
}
}
@@ -806,6 +909,23 @@ void ZT_Node_delete(ZT_Node *node)
} catch ( ... ) {}
}
+enum ZT_ResultCode ZT_Node_processStateUpdate(
+ ZT_Node *node,
+ void *tptr,
+ ZT_StateObjectType type,
+ uint64_t id,
+ const void *data,
+ unsigned int len)
+{
+ try {
+ return reinterpret_cast<ZeroTier::Node *>(node)->processStateUpdate(tptr,type,id,data,len);
+ } catch (std::bad_alloc &exc) {
+ return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
+ } catch ( ... ) {
+ return ZT_RESULT_FATAL_ERROR_INTERNAL;
+ }
+}
+
enum ZT_ResultCode ZT_Node_processWirePacket(
ZT_Node *node,
void *tptr,
@@ -998,56 +1118,6 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
} catch ( ... ) {}
}
-enum ZT_ResultCode ZT_Node_clusterInit(
- ZT_Node *node,
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterInit(myId,zeroTierPhysicalEndpoints,numZeroTierPhysicalEndpoints,x,y,z,sendFunction,sendFunctionArg,addressToLocationFunction,addressToLocationFunctionArg);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-enum ZT_ResultCode ZT_Node_clusterAddMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- return reinterpret_cast<ZeroTier::Node *>(node)->clusterAddMember(memberId);
- } catch ( ... ) {
- return ZT_RESULT_FATAL_ERROR_INTERNAL;
- }
-}
-
-void ZT_Node_clusterRemoveMember(ZT_Node *node,unsigned int memberId)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterRemoveMember(memberId);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned int len)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterHandleIncomingMessage(msg,len);
- } catch ( ... ) {}
-}
-
-void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
-{
- try {
- reinterpret_cast<ZeroTier::Node *>(node)->clusterStatus(cs);
- } catch ( ... ) {}
-}
-
void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
{
try {
diff --git a/node/Node.hpp b/node/Node.hpp
index 57b5489e..f407c60c 100644
--- a/node/Node.hpp
+++ b/node/Node.hpp
@@ -46,6 +46,7 @@
#include "Path.hpp"
#include "Salsa20.hpp"
#include "NetworkController.hpp"
+#include "Hashtable.hpp"
#undef TRACE
#ifdef ZT_TRACE
@@ -81,6 +82,12 @@ public:
// Public API Functions ----------------------------------------------------
+ ZT_ResultCode processStateUpdate(
+ void *tptr,
+ ZT_StateObjectType type,
+ uint64_t id,
+ const void *data,
+ unsigned int len);
ZT_ResultCode processWirePacket(
void *tptr,
uint64_t now,
@@ -117,21 +124,6 @@ public:
void clearLocalInterfaceAddresses();
int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
void setNetconfMaster(void *networkControllerInstance);
- ZT_ResultCode clusterInit(
- unsigned int myId,
- const struct sockaddr_storage *zeroTierPhysicalEndpoints,
- unsigned int numZeroTierPhysicalEndpoints,
- int x,
- int y,
- int z,
- void (*sendFunction)(void *,unsigned int,const void *,unsigned int),
- void *sendFunctionArg,
- int (*addressToLocationFunction)(void *,const struct sockaddr_storage *,int *,int *,int *),
- void *addressToLocationFunctionArg);
- ZT_ResultCode clusterAddMember(unsigned int memberId);
- void clusterRemoveMember(unsigned int memberId);
- void clusterHandleIncomingMessage(const void *msg,unsigned int len);
- void clusterStatus(ZT_ClusterStatus *cs);
// Internal functions ------------------------------------------------------
@@ -169,26 +161,27 @@ public:
inline SharedPtr<Network> network(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- return _network(nwid);
+ const SharedPtr<Network> *n = _networks.get(nwid);
+ if (n)
+ return *n;
+ return SharedPtr<Network>();
}
inline bool belongsToNetwork(uint64_t nwid) const
{
Mutex::Lock _l(_networks_m);
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return true;
- }
- return false;
+ return _networks.contains(nwid);
}
inline std::vector< SharedPtr<Network> > allNetworks() const
{
std::vector< SharedPtr<Network> > nw;
Mutex::Lock _l(_networks_m);
- nw.reserve(_networks.size());
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i)
- nw.push_back(i->second);
+ Hashtable< uint64_t,SharedPtr<Network> >::Iterator i(*const_cast< Hashtable< uint64_t,SharedPtr<Network> > * >(&_networks));
+ uint64_t *k = (uint64_t *)0;
+ SharedPtr<Network> *v = (SharedPtr<Network> *)0;
+ while (i.next(k,v))
+ nw.push_back(*v);
return nw;
}
@@ -198,17 +191,16 @@ public:
return _directPaths;
}
- inline bool dataStorePut(void *tPtr,const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,data,len,(int)secure) == 0); }
- inline bool dataStorePut(void *tPtr,const char *name,const std::string &data,bool secure) { return dataStorePut(tPtr,name,(const void *)data.data(),(unsigned int)data.length(),secure); }
- inline void dataStoreDelete(void *tPtr,const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,(const void *)0,0,0); }
- std::string dataStoreGet(void *tPtr,const char *name);
-
inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
inline bool online() const throw() { return _online; }
+ inline int stateObjectGet(void *const tPtr,ZT_StateObjectType type,const uint64_t id,void *const data,const unsigned int maxlen) { return _cb.stateGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,maxlen); }
+ inline void stateObjectPut(void *const tPtr,ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,data,(int)len); }
+ inline void stateObjectDelete(void *const tPtr,ZT_StateObjectType type,const uint64_t id) { _cb.statePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,type,id,(const void *)0,-1); }
+
#ifdef ZT_TRACE
void postTrace(const char *module,unsigned int line,const char *fmt,...);
#endif
@@ -222,6 +214,8 @@ public:
World planet() const;
std::vector<World> moons() const;
+ inline const Identity &identity() const { return _RR.identity; }
+
/**
* Register that we are expecting a reply to a packet ID
*
@@ -281,16 +275,6 @@ public:
virtual void ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &destination,NetworkController::ErrorCode errorCode);
private:
- inline SharedPtr<Network> _network(uint64_t nwid) const
- {
- // assumes _networks_m is locked
- for(std::vector< std::pair< uint64_t, SharedPtr<Network> > >::const_iterator i=_networks.begin();i!=_networks.end();++i) {
- if (i->first == nwid)
- return i->second;
- }
- return SharedPtr<Network>();
- }
-
RuntimeEnvironment _RR;
RuntimeEnvironment *RR;
void *_uPtr; // _uptr (lower case) is reserved in Visual Studio :P
@@ -303,7 +287,7 @@ private:
// Time of last identity verification indexed by InetAddress.rateGateHash() -- used in IncomingPacket::_doHELLO() via rateGateIdentityVerification()
uint64_t _lastIdentityVerification[16384];
- std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
+ Hashtable< uint64_t,SharedPtr<Network> > _networks;
Mutex _networks_m;
std::vector<InetAddress> _directPaths;
diff --git a/node/Topology.cpp b/node/Topology.cpp
index 80f4ed4e..d4b424ff 100644
--- a/node/Topology.cpp
+++ b/node/Topology.cpp
@@ -68,15 +68,15 @@ Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
_trustedPathCount(0),
_amRoot(false)
{
- try {
- World cachedPlanet;
- std::string buf(RR->node->dataStoreGet(tPtr,"planet"));
- if (buf.length() > 0) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp(buf.data(),(unsigned int)buf.length());
- cachedPlanet.deserialize(dswtmp,0);
- }
- addWorld(tPtr,cachedPlanet,false);
- } catch ( ... ) {}
+ uint8_t tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PLANET,0,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
+ World cachedPlanet;
+ cachedPlanet.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n),0);
+ addWorld(tPtr,cachedPlanet,false);
+ } catch ( ... ) {} // ignore invalid cached planets
+ }
World defaultPlanet;
{
@@ -158,9 +158,8 @@ Identity Topology::getIdentity(void *tPtr,const Address &zta)
void Topology::saveIdentity(void *tPtr,const Identity &id)
{
if (id) {
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)id.address().toInt());
- RR->node->dataStorePut(tPtr,p,id.toString(false),false);
+ const std::string tmp(id.toString(false));
+ RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER_IDENTITY,id.address().toInt(),tmp.data(),(unsigned int)tmp.length());
}
}
@@ -327,19 +326,11 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
return false;
}
- char savePath[64];
- if (existing->type() == World::TYPE_MOON) {
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",existing->id());
- } else {
- Utils::scopy(savePath,sizeof(savePath),"planet");
- }
try {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> dswtmp;
- existing->serialize(dswtmp,false);
- RR->node->dataStorePut(tPtr,savePath,dswtmp.data(),dswtmp.size(),false);
- } catch ( ... ) {
- RR->node->dataStoreDelete(tPtr,savePath);
- }
+ Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> sbuf;
+ existing->serialize(sbuf,false);
+ RR->node->stateObjectPut(tPtr,(existing->type() == World::TYPE_PLANET) ? ZT_STATE_OBJECT_PLANET : ZT_STATE_OBJECT_MOON,existing->id(),sbuf.data(),sbuf.size());
+ } catch ( ... ) {}
_memoizeUpstreams(tPtr);
@@ -348,21 +339,18 @@ bool Topology::addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew)
void Topology::addMoon(void *tPtr,const uint64_t id,const Address &seed)
{
- char savePath[64];
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
-
- try {
- std::string moonBin(RR->node->dataStoreGet(tPtr,savePath));
- if (moonBin.length() > 1) {
- Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH> wtmp(moonBin.data(),(unsigned int)moonBin.length());
+ char tmp[ZT_WORLD_MAX_SERIALIZED_LENGTH];
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_MOON,id,tmp,sizeof(tmp));
+ if (n > 0) {
+ try {
World w;
- w.deserialize(wtmp);
+ w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(tmp,(unsigned int)n));
if ((w.type() == World::TYPE_MOON)&&(w.id() == id)) {
addWorld(tPtr,w,true);
return;
}
- }
- } catch ( ... ) {}
+ } catch ( ... ) {}
+ }
if (seed) {
Mutex::Lock _l(_upstreams_m);
@@ -381,9 +369,7 @@ void Topology::removeMoon(void *tPtr,const uint64_t id)
if (m->id() != id) {
nm.push_back(*m);
} else {
- char savePath[64];
- Utils::snprintf(savePath,sizeof(savePath),"moons.d/%.16llx.moon",id);
- RR->node->dataStoreDelete(tPtr,savePath);
+ RR->node->stateObjectDelete(tPtr,ZT_STATE_OBJECT_MOON,id);
}
}
_moons.swap(nm);
@@ -425,12 +411,12 @@ void Topology::clean(uint64_t now)
Identity Topology::_getIdentity(void *tPtr,const Address &zta)
{
- char p[128];
- Utils::snprintf(p,sizeof(p),"iddb.d/%.10llx",(unsigned long long)zta.toInt());
- std::string ids(RR->node->dataStoreGet(tPtr,p));
- if (ids.length() > 0) {
+ char tmp[512];
+ int n = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER_IDENTITY,zta.toInt(),tmp,sizeof(tmp) - 1);
+ if (n > 0) {
+ tmp[n] = (char)0;
try {
- return Identity(ids);
+ return Identity(tmp);
} catch ( ... ) {} // ignore invalid IDs
}
return Identity();
diff --git a/osdep/Binder.hpp b/osdep/Binder.hpp
index ee832825..1839ecc2 100644
--- a/osdep/Binder.hpp
+++ b/osdep/Binder.hpp
@@ -66,11 +66,7 @@
#include "Phy.hpp"
#include "OSUtils.hpp"
-/**
- * Period between binder rescans/refreshes
- *
- * OneService also does this on detected restarts.
- */
+// Period between refreshes of bindings
#define ZT_BINDER_REFRESH_PERIOD 30000
namespace ZeroTier {
@@ -105,10 +101,7 @@ public:
Binder() {}
/**
- * Close all bound ports
- *
- * This should be called on shutdown. It closes listen sockets and UDP ports
- * but not TCP connections from any TCP listen sockets.
+ * Close all bound ports, should be called on shutdown
*
* @param phy Physical interface
*/
@@ -116,9 +109,9 @@ public:
void closeAll(Phy<PHY_HANDLER_TYPE> &phy)
{
Mutex::Lock _l(_lock);
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- phy.close(i->udpSock,false);
- phy.close(i->tcpListenSock,false);
+ for(std::vector<_Binding>::iterator b(_bindings.begin());b!=_bindings.end();++b) {
+ phy.close(b->udpSock,false);
+ phy.close(b->tcpListenSock,false);
}
}
@@ -129,7 +122,7 @@ public:
* changes, on startup, or periodically (e.g. every 30-60s).
*
* @param phy Physical interface
- * @param port Port to bind to on all interfaces (TCP and UDP)
+ * @param ports Ports to bind on all interfaces
* @param ignoreInterfacesByName Ignore these interfaces by name
* @param ignoreInterfacesByNamePrefix Ignore these interfaces by name-prefix (starts-with, e.g. zt ignores zt*)
* @param ignoreInterfacesByAddress Ignore these interfaces by address
@@ -137,11 +130,10 @@ public:
* @tparam INTERFACE_CHECKER Type for class containing shouldBindInterface() method
*/
template<typename PHY_HANDLER_TYPE,typename INTERFACE_CHECKER>
- void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int port,INTERFACE_CHECKER &ifChecker)
+ void refresh(Phy<PHY_HANDLER_TYPE> &phy,unsigned int *ports,unsigned int portCount,INTERFACE_CHECKER &ifChecker)
{
std::map<InetAddress,std::string> localIfAddrs;
- PhySocket *udps;
- //PhySocket *tcps;
+ PhySocket *udps,*tcps;
Mutex::Lock _l(_lock);
#ifdef __WINDOWS__
@@ -161,8 +153,10 @@ public:
case InetAddress::IP_SCOPE_GLOBAL:
case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
+ for(int x=0;x<portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string()));
+ }
break;
}
}
@@ -231,8 +225,10 @@ public:
case InetAddress::IP_SCOPE_GLOBAL:
case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
+ for(int x=0;x<portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(devname)));
+ }
break;
}
}
@@ -249,11 +245,8 @@ public:
configuration.ifc_buf = nullptr;
if (controlfd < 0) goto ip4_address_error;
-
if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
-
configuration.ifc_buf = (char*)malloc(configuration.ifc_len);
-
if (ioctl(controlfd, SIOCGIFCONF, &configuration) < 0) goto ip4_address_error;
for (int i=0; i < (int)(configuration.ifc_len / sizeof(ifreq)); i ++) {
@@ -262,9 +255,8 @@ public:
if (addr->sa_family != AF_INET) continue;
std::string ifname = request.ifr_ifrn.ifrn_name;
// name can either be just interface name or interface name followed by ':' and arbitrary label
- if (ifname.find(':') != std::string::npos) {
+ if (ifname.find(':') != std::string::npos)
ifname = ifname.substr(0, ifname.find(':'));
- }
InetAddress ip(&(((struct sockaddr_in *)addr)->sin_addr),4,0);
if (ifChecker.shouldBindInterface(ifname.c_str(), ip)) {
@@ -274,8 +266,10 @@ public:
case InetAddress::IP_SCOPE_GLOBAL:
case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip, ifname));
+ for(int x=0;x<portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,ifname));
+ }
break;
}
}
@@ -306,8 +300,10 @@ public:
case InetAddress::IP_SCOPE_GLOBAL:
case InetAddress::IP_SCOPE_SHARED:
case InetAddress::IP_SCOPE_PRIVATE:
- ip.setPort(port);
- localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
+ for(int x=0;x<portCount;++x) {
+ ip.setPort(ports[x]);
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(ip,std::string(ifa->ifa_name)));
+ }
break;
}
}
@@ -322,59 +318,57 @@ public:
// Default to binding to wildcard if we can't enumerate addresses
if (localIfAddrs.empty()) {
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,port),std::string()));
- localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,port),std::string()));
+ for(int x=0;x<portCount;++x) {
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((uint32_t)0,ports[x]),std::string()));
+ localIfAddrs.insert(std::pair<InetAddress,std::string>(InetAddress((const void *)"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0",16,ports[x]),std::string()));
+ }
}
- // Close any old bindings to anything that doesn't exist anymore
- for(typename std::vector<_Binding>::const_iterator bi(_bindings.begin());bi!=_bindings.end();++bi) {
- if (localIfAddrs.find(bi->address) == localIfAddrs.end()) {
- phy.close(bi->udpSock,false);
- phy.close(bi->tcpListenSock,false);
+ std::vector<_Binding> newBindings;
+
+ // Save bindings that are still valid, close those that are not
+ for(std::vector<_Binding>::iterator b(_bindings.begin());b!=_bindings.end();++b) {
+ if (localIfAddrs.find(b->address) != localIfAddrs.end()) {
+ newBindings.push_back(*b);
+ } else {
+ phy.close(b->udpSock,false);
+ phy.close(b->tcpListenSock,false);
}
}
- std::vector<_Binding> newBindings;
+ // Create new bindings for those not already bound
for(std::map<InetAddress,std::string>::const_iterator ii(localIfAddrs.begin());ii!=localIfAddrs.end();++ii) {
- typename std::vector<_Binding>::const_iterator bi(_bindings.begin());
- while (bi != _bindings.end()) {
- if (bi->address == ii->first) {
- newBindings.push_back(*bi);
+ typename std::vector<_Binding>::const_iterator bi(newBindings.begin());
+ while (bi != newBindings.end()) {
+ if (bi->address == ii->first)
break;
- }
++bi;
}
-
- if (bi == _bindings.end()) {
+ if (bi == newBindings.end()) {
udps = phy.udpBind(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0,ZT_UDP_DESIRED_BUF_SIZE);
- if (udps) {
- //tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&ii),(void *)0);
- //if (tcps) {
+ tcps = phy.tcpListen(reinterpret_cast<const struct sockaddr *>(&(ii->first)),(void *)0);
+ if ((udps)&&(tcps)) {
#ifdef __LINUX__
- // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
- if (ii->second.length() > 0) {
- int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
- char tmp[256];
- Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
- if (fd >= 0) {
- if (setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp)) != 0) {
- fprintf(stderr,"WARNING: unable to set SO_BINDTODEVICE to bind %s to %s\n",ii->first.toIpString().c_str(),ii->second.c_str());
- }
- }
- }
+ // Bind Linux sockets to their device so routes tha we manage do not override physical routes (wish all platforms had this!)
+ if (ii->second.length() > 0) {
+ char tmp[256];
+ Utils::scopy(tmp,sizeof(tmp),ii->second.c_str());
+ int fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(udps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ fd = (int)Phy<PHY_HANDLER_TYPE>::getDescriptor(tcps);
+ if (fd >= 0)
+ setsockopt(fd,SOL_SOCKET,SO_BINDTODEVICE,tmp,strlen(tmp));
+ }
#endif // __LINUX__
- newBindings.push_back(_Binding());
- newBindings.back().udpSock = udps;
- //newBindings.back().tcpListenSock = tcps;
- newBindings.back().address = ii->first;
- //} else {
- // phy.close(udps,false);
- //}
+ newBindings.push_back(_Binding());
+ newBindings.back().udpSock = udps;
+ newBindings.back().tcpListenSock = tcps;
+ newBindings.back().address = ii->first;
}
}
}
- // Swapping pointers and then letting the old one fall out of scope is faster than copying again
_bindings.swap(newBindings);
}
@@ -402,47 +396,78 @@ public:
* @param data Data to send
* @param len Length of data
* @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
+ * @return -1 == local doesn't match any bound address, 0 == send failure, 1 == send successful
*/
template<typename PHY_HANDLER_TYPE>
- inline bool udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
+ inline int udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
{
+ PhySocket *s;
+ typename std::vector<_Binding>::const_iterator i;
+ int result;
Mutex::Lock _l(_lock);
- if (local) {
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address == local) {
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- const bool result = phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(local.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
- return result;
+
+ if (remote.ss_family == AF_INET) {
+ if (local) {
+ for(i=_bindings.begin();i!=_bindings.end();++i) {
+ if (
+ (i->address.ss_family == AF_INET) &&
+ (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_port == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_port) &&
+ (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_addr.s_addr)
+ )
+ {
+ s = i->udpSock;
+ goto Binder_send_packet;
+ }
+ }
+ } else {
+ for(i=_bindings.begin();i!=_bindings.end();++i) {
+ if (i->address.ss_family == AF_INET) {
+ s = i->udpSock;
+ goto Binder_send_packet;
+ }
}
}
- return false;
} else {
- bool result = false;
- for(typename std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i) {
- if (i->address.ss_family == remote.ss_family) {
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,v4ttl);
- result |= phy.udpSend(i->udpSock,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
- if ((v4ttl)&&(remote.ss_family == AF_INET))
- phy.setIp4UdpTtl(i->udpSock,255);
+ if (local) {
+ for(i=_bindings.begin();i!=_bindings.end();++i) {
+ if (
+ (i->address.ss_family == AF_INET6) &&
+ (reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_port) &&
+ (!memcmp(reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_addr.s6_addr,16))
+ )
+ {
+ s = i->udpSock;
+ goto Binder_send_packet;
+ }
+ }
+ } else {
+ for(i=_bindings.begin();i!=_bindings.end();++i) {
+ if (i->address.ss_family == AF_INET6) {
+ s = i->udpSock;
+ goto Binder_send_packet;
+ }
}
}
- return result;
}
+
+ return -1;
+
+Binder_send_packet:
+ if (v4ttl) phy.setIp4UdpTtl(s,v4ttl);
+ result = (int)phy.udpSend(s,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
+ if (v4ttl) phy.setIp4UdpTtl(s,255);
+ return result;
}
/**
* @return All currently bound local interface addresses
*/
- inline std::vector<InetAddress> allBoundLocalInterfaceAddresses()
+ inline std::vector<InetAddress> allBoundLocalInterfaceAddresses() const
{
- Mutex::Lock _l(_lock);
std::vector<InetAddress> aa;
- for(std::vector<_Binding>::const_iterator i(_bindings.begin());i!=_bindings.end();++i)
- aa.push_back(i->address);
+ Mutex::Lock _l(_lock);
+ for(std::vector<_Binding>::const_iterator b(_bindings.begin());b!=_bindings.end();++b)
+ aa.push_back(b->address);
return aa;
}
diff --git a/osdep/OSUtils.cpp b/osdep/OSUtils.cpp
index b7fce982..53e8bb97 100644
--- a/osdep/OSUtils.cpp
+++ b/osdep/OSUtils.cpp
@@ -287,7 +287,7 @@ int64_t OSUtils::getFileSize(const char *path)
bool OSUtils::readFile(const char *path,std::string &buf)
{
- char tmp[1024];
+ char tmp[16384];
FILE *f = fopen(path,"rb");
if (f) {
for(;;) {
diff --git a/service/OneService.cpp b/service/OneService.cpp
index b96f3aed..0fadc191 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -31,7 +31,6 @@
#include <string>
#include <map>
-#include <set>
#include <vector>
#include <algorithm>
#include <list>
@@ -47,6 +46,9 @@
#include "../node/MAC.hpp"
#include "../node/Identity.hpp"
#include "../node/World.hpp"
+#include "../node/Salsa20.hpp"
+#include "../node/Poly1305.hpp"
+#include "../node/SHA512.hpp"
#include "../osdep/Phy.hpp"
#include "../osdep/Thread.hpp"
@@ -85,16 +87,6 @@
using json = nlohmann::json;
-/**
- * Uncomment to enable UDP breakage switch
- *
- * If this is defined, the presence of a file called /tmp/ZT_BREAK_UDP
- * will cause direct UDP TX/RX to stop working. This can be used to
- * test TCP tunneling fallback and other robustness features. Deleting
- * this file will cause it to start working again.
- */
-//#define ZT_BREAK_UDP
-
#include "../controller/EmbeddedNetworkController.hpp"
#ifdef ZT_USE_TEST_TAP
@@ -105,11 +97,13 @@ namespace ZeroTier { typedef TestEthernetTap EthernetTap; }
#else
#ifdef ZT_SDK
- #include "../controller/EmbeddedNetworkController.hpp"
- #include "../node/Node.hpp"
- // Use the virtual netcon endpoint instead of a tun/tap port driver
- #include "../src/SocketTap.hpp"
- namespace ZeroTier { typedef SocketTap EthernetTap; }
+
+#include "../controller/EmbeddedNetworkController.hpp"
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../src/SocketTap.hpp"
+namespace ZeroTier { typedef SocketTap EthernetTap; }
+
#else
#ifdef __APPLE__
@@ -148,9 +142,6 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
// How often to check for new multicast subscriptions on a tap device
#define ZT_TAP_CHECK_MULTICAST_INTERVAL 5000
-// Path under ZT1 home for controller database if controller is enabled
-#define ZT_CONTROLLER_DB_PATH "controller.d"
-
// TCP fallback relay (run by ZeroTier, Inc. -- this will eventually go away)
#define ZT_TCP_FALLBACK_RELAY "204.80.128.1/443"
@@ -166,10 +157,22 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
// Clean files from iddb.d that are older than this (60 days)
#define ZT_IDDB_CLEANUP_AGE 5184000000ULL
+// Maximum write buffer size for outgoing TCP connections (sanity limit)
+#define ZT_TCP_MAX_WRITEQ_SIZE 33554432
+
+// How often to check TCP connections and cluster links and send status to cluster peers
+#define ZT_TCP_CHECK_PERIOD 15000
+
+// TCP activity timeout
+#define ZT_TCP_ACTIVITY_TIMEOUT 60000
+
namespace ZeroTier {
namespace {
+// Fake TLS hello for TCP tunnel outgoing connections (TUNNELED mode)
+static const char ZT_TCP_TUNNEL_HELLO[9] = { 0x17,0x03,0x03,0x00,0x04,(char)ZEROTIER_ONE_VERSION_MAJOR,(char)ZEROTIER_ONE_VERSION_MINOR,(char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff),(char)(ZEROTIER_ONE_VERSION_REVISION & 0xff) };
+
static std::string _trimString(const std::string &s)
{
unsigned long end = (unsigned long)s.length();
@@ -309,18 +312,12 @@ class OneServiceImpl;
static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize);
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure);
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,const void *data,int len);
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen);
static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z);
-#endif
-
static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
static int ShttpOnMessageBegin(http_parser *parser);
@@ -359,36 +356,55 @@ static const struct http_parser_settings HTTP_PARSER_SETTINGS = {
};
#endif
+/**
+ * A TCP connection and related state and buffers
+ */
struct TcpConnection
{
enum {
+ TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection
TCP_HTTP_INCOMING,
- TCP_HTTP_OUTGOING, // not currently used
- TCP_TUNNEL_OUTGOING // fale-SSL outgoing tunnel -- HTTP-related fields are not used
+ TCP_HTTP_OUTGOING,
+ TCP_TUNNEL_OUTGOING, // TUNNELED mode proxy outbound connection
+ TCP_CLUSTER_BACKPLANE
} type;
- bool shouldKeepAlive;
OneServiceImpl *parent;
PhySocket *sock;
- InetAddress from;
+ InetAddress remoteAddr;
+ unsigned long lastReceive;
+
+ // Used for inbound HTTP connections
http_parser parser;
unsigned long messageSize;
- uint64_t lastActivity;
-
std::string currentHeaderField;
std::string currentHeaderValue;
-
std::string url;
std::string status;
std::map< std::string,std::string > headers;
- std::string body;
- std::string writeBuf;
- Mutex writeBuf_m;
+ // Used for cluster backplane connections
+ uint64_t clusterMemberId;
+ unsigned int clusterMemberVersionMajor;
+ unsigned int clusterMemberVersionMinor;
+ unsigned int clusterMemberVersionRev;
+ std::vector< InetAddress > clusterMemberLocalAddresses;
+ Mutex clusterMemberLocalAddresses_m;
+
+ std::string readq;
+ std::string writeq;
+ Mutex writeq_m;
};
-// Used to pseudo-randomize local source port picking
-static volatile unsigned int _udpPortPickerCounter = 0;
+/**
+ * Message types for cluster backplane communication
+ */
+enum ClusterMessageType
+{
+ CLUSTER_MESSAGE_STATUS = 0,
+ CLUSTER_MESSAGE_STATE_OBJECT = 1,
+ CLUSTER_MESSAGE_PROXY_SEND = 2
+};
class OneServiceImpl : public OneService
{
@@ -398,14 +414,21 @@ public:
const std::string _homePath;
std::string _authToken;
std::string _controllerDbPath;
+ const std::string _iddbPath;
+ const std::string _networksPath;
+ const std::string _moonsPath;
+
EmbeddedNetworkController *_controller;
Phy<OneServiceImpl *> _phy;
Node *_node;
SoftwareUpdater *_updater;
bool _updateAutoApply;
unsigned int _primaryPort;
+ volatile unsigned int _udpPortPickerCounter;
+ uint64_t _clusterMemberId;
+ uint8_t _clusterKey[32]; // secret key for cluster backplane config
- // Local configuration and memo-ized static path definitions
+ // Local configuration and memo-ized information from it
json _localConfig;
Hashtable< uint64_t,std::vector<InetAddress> > _v4Hints;
Hashtable< uint64_t,std::vector<InetAddress> > _v6Hints;
@@ -415,6 +438,7 @@ public:
std::vector< InetAddress > _globalV6Blacklist;
std::vector< InetAddress > _allowManagementFrom;
std::vector< std::string > _interfacePrefixBlacklist;
+ std::vector< InetAddress > _clusterBackplaneAddresses;
Mutex _localConfig_m;
/*
@@ -428,13 +452,8 @@ public:
* destructively with uPnP port mapping behavior in very weird buggy ways.
* It's only used if uPnP/NAT-PMP is enabled in this build.
*/
- Binder _bindings[3];
unsigned int _ports[3];
- uint16_t _portsBE[3]; // ports in big-endian network byte order as in sockaddr
-
- // Sockets for JSON API -- bound only to V4 and V6 localhost
- PhySocket *_v4TcpControlSocket;
- PhySocket *_v6TcpControlSocket;
+ Binder _binder;
// Time we last received a packet from a global address
uint64_t _lastDirectReceiveFromGlobal;
@@ -470,7 +489,8 @@ public:
Mutex _nets_m;
// Active TCP/IP connections
- std::set< TcpConnection * > _tcpConnections; // no mutex for this since it's done in the main loop thread only
+ std::vector< TcpConnection * > _tcpConnections;
+ Mutex _tcpConnections_m;
TcpConnection *_tcpFallbackTunnel;
// Termination status information
@@ -484,13 +504,6 @@ public:
PortMapper *_portMapper;
#endif
- // Cluster management instance if enabled
-#ifdef ZT_ENABLE_CLUSTER
- PhySocket *_clusterMessageSocket;
- ClusterDefinition *_clusterDefinition;
- unsigned int _clusterMemberId;
-#endif
-
// Set to false to force service to stop
volatile bool _run;
Mutex _run_m;
@@ -499,15 +512,18 @@ public:
OneServiceImpl(const char *hp,unsigned int port) :
_homePath((hp) ? hp : ".")
- ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S ZT_CONTROLLER_DB_PATH)
+ ,_controllerDbPath(_homePath + ZT_PATH_SEPARATOR_S "controller.d")
+ ,_iddbPath(_homePath + ZT_PATH_SEPARATOR_S "iddb.d")
+ ,_networksPath(_homePath + ZT_PATH_SEPARATOR_S "networks.d")
+ ,_moonsPath(_homePath + ZT_PATH_SEPARATOR_S "moons.d")
,_controller((EmbeddedNetworkController *)0)
,_phy(this,false,true)
,_node((Node *)0)
,_updater((SoftwareUpdater *)0)
,_updateAutoApply(false)
,_primaryPort(port)
- ,_v4TcpControlSocket((PhySocket *)0)
- ,_v6TcpControlSocket((PhySocket *)0)
+ ,_udpPortPickerCounter(0)
+ ,_clusterMemberId(0)
,_lastDirectReceiveFromGlobal(0)
#ifdef ZT_TCP_FALLBACK_RELAY
,_lastSendToGlobalV4(0)
@@ -520,11 +536,6 @@ public:
#ifdef ZT_USE_MINIUPNPC
,_portMapper((PortMapper *)0)
#endif
-#ifdef ZT_ENABLE_CLUSTER
- ,_clusterMessageSocket((PhySocket *)0)
- ,_clusterDefinition((ClusterDefinition *)0)
- ,_clusterMemberId(0)
-#endif
,_run(true)
{
_ports[0] = 0;
@@ -534,23 +545,11 @@ public:
virtual ~OneServiceImpl()
{
- for(int i=0;i<3;++i)
- _bindings[i].closeAll(_phy);
-
- _phy.close(_v4TcpControlSocket);
- _phy.close(_v6TcpControlSocket);
-
-#ifdef ZT_ENABLE_CLUSTER
- _phy.close(_clusterMessageSocket);
-#endif
-
+ _binder.closeAll(_phy);
#ifdef ZT_USE_MINIUPNPC
delete _portMapper;
#endif
delete _controller;
-#ifdef ZT_ENABLE_CLUSTER
- delete _clusterDefinition;
-#endif
}
virtual ReasonForTermination run()
@@ -576,15 +575,11 @@ public:
_authToken = _trimString(_authToken);
}
- // Clean up any legacy files if present
- OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "peers.save").c_str());
- OSUtils::rm((_homePath + ZT_PATH_SEPARATOR_S "world").c_str());
-
{
struct ZT_Node_Callbacks cb;
cb.version = 0;
- cb.dataStoreGetFunction = SnodeDataStoreGetFunction;
- cb.dataStorePutFunction = SnodeDataStorePutFunction;
+ cb.stateGetFunction = SnodeStateGetFunction;
+ cb.statePutFunction = SnodeStatePutFunction;
cb.wirePacketSendFunction = SnodeWirePacketSendFunction;
cb.virtualNetworkFrameFunction = SnodeVirtualNetworkFrameFunction;
cb.virtualNetworkConfigFunction = SnodeVirtualNetworkConfigFunction;
@@ -600,9 +595,10 @@ public:
InetAddress trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
unsigned int trustedPathCount = 0;
- // Old style "trustedpaths" flat file -- will eventually go away
+ // LEGACY: support old "trustedpaths" flat file
FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S "trustedpaths").c_str(),"r");
if (trustpaths) {
+ fprintf(stderr,"WARNING: 'trustedpaths' flat file format is deprecated in favor of path definitions in local.conf" ZT_EOL_S);
char buf[1024];
while ((fgets(buf,sizeof(buf),trustpaths))&&(trustedPathCount < ZT_MAX_TRUSTED_PATHS)) {
int fno = 0;
@@ -664,9 +660,11 @@ public:
if (trustedPathCount)
_node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(trustedPathNetworks),trustedPathIds,trustedPathCount);
}
+
+ // Apply other runtime configuration from local.conf
applyLocalConfig();
- // Bind TCP control socket
+ // Make sure we can use the primary port, and hunt for one if configured to do so
const int portTrials = (_primaryPort == 0) ? 256 : 1; // if port is 0, pick random
for(int k=0;k<portTrials;++k) {
if (_primaryPort == 0) {
@@ -674,35 +672,8 @@ public:
Utils::getSecureRandom(&randp,sizeof(randp));
_primaryPort = 20000 + (randp % 45500);
}
-
if (_trialBind(_primaryPort)) {
- struct sockaddr_in in4;
- memset(&in4,0,sizeof(in4));
- in4.sin_family = AF_INET;
- in4.sin_addr.s_addr = Utils::hton((uint32_t)((_allowManagementFrom.size() > 0) ? 0 : 0x7f000001)); // right now we just listen for TCP @127.0.0.1
- in4.sin_port = Utils::hton((uint16_t)_primaryPort);
- _v4TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in4,this);
-
- struct sockaddr_in6 in6;
- memset((void *)&in6,0,sizeof(in6));
- in6.sin6_family = AF_INET6;
- in6.sin6_port = in4.sin_port;
- if (_allowManagementFrom.size() == 0)
- in6.sin6_addr.s6_addr[15] = 1; // IPv6 localhost == ::1
- _v6TcpControlSocket = _phy.tcpListen((const struct sockaddr *)&in6,this);
-
- // We must bind one of IPv4 or IPv6 -- support either failing to support hosts that
- // have only IPv4 or only IPv6 stacks.
- if ((_v4TcpControlSocket)||(_v6TcpControlSocket)) {
- _ports[0] = _primaryPort;
- break;
- } else {
- if (_v4TcpControlSocket)
- _phy.close(_v4TcpControlSocket,false);
- if (_v6TcpControlSocket)
- _phy.close(_v6TcpControlSocket,false);
- _primaryPort = 0;
- }
+ _ports[0] = _primaryPort;
} else {
_primaryPort = 0;
}
@@ -714,7 +685,7 @@ public:
return _termReason;
}
- // Write file containing primary port to be read by CLIs, etc.
+ // Save primary port to a file so CLIs and GUIs can learn it easily
char portstr[64];
Utils::snprintf(portstr,sizeof(portstr),"%u",_ports[0]);
OSUtils::writeFile((_homePath + ZT_PATH_SEPARATOR_S "zerotier-one.port").c_str(),std::string(portstr));
@@ -722,7 +693,7 @@ public:
// Attempt to bind to a secondary port chosen from our ZeroTier address.
// This exists because there are buggy NATs out there that fail if more
// than one device behind the same NAT tries to use the same internal
- // private address port number.
+ // private address port number. Buggy NATs are a running theme.
_ports[1] = 20000 + ((unsigned int)_node->address() % 45500);
for(int i=0;;++i) {
if (i > 1000) {
@@ -761,13 +732,11 @@ public:
}
#endif
- // Populate ports in big-endian format for quick compare
- for(int i=0;i<3;++i)
- _portsBE[i] = Utils::hton((uint16_t)_ports[i]);
-
+ // Network controller is now enabled by default for desktop and server
_controller = new EmbeddedNetworkController(_node,_controllerDbPath.c_str());
_node->setNetconfMaster((void *)_controller);
+/*
#ifdef ZT_ENABLE_CLUSTER
if (OSUtils::fileExists((_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str())) {
_clusterDefinition = new ClusterDefinition(_node->address(),(_homePath + ZT_PATH_SEPARATOR_S "cluster").c_str());
@@ -816,8 +785,10 @@ public:
}
}
#endif
+*/
- { // Load existing networks
+ // Join existing networks in networks.d
+ {
std::vector<std::string> networksDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "networks.d").c_str()));
for(std::vector<std::string>::iterator f(networksDotD.begin());f!=networksDotD.end();++f) {
std::size_t dot = f->find_last_of('.');
@@ -825,7 +796,9 @@ public:
_node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0);
}
}
- { // Load existing moons
+
+ // Orbit existing moons in moons.d
+ {
std::vector<std::string> moonsDotD(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S "moons.d").c_str()));
for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) {
std::size_t dot = f->find_last_of('.');
@@ -834,6 +807,16 @@ public:
}
}
+ // Derive the cluster's shared secret backplane encryption key by hashing its shared secret identity
+ {
+ uint8_t tmp[64];
+ SHA512::hash(tmp,_node->identity().privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN);
+ memcpy(_clusterKey,tmp,32);
+ }
+ _clusterMemberId = _node->prng();
+ if (!_clusterMemberId) _clusterMemberId = 1;
+
+ // Main I/O loop
_nextBackgroundTaskDeadline = 0;
uint64_t clockShouldBe = OSUtils::now();
_lastRestart = clockShouldBe;
@@ -842,6 +825,7 @@ public:
uint64_t lastUpdateCheck = clockShouldBe;
uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
uint64_t lastCleanedIddb = 0;
+ uint64_t lastTcpCheck = 0;
for(;;) {
_run_m.lock();
if (!_run) {
@@ -859,7 +843,7 @@ public:
// Clean iddb.d on start and every 24 hours
if ((now - lastCleanedIddb) > 86400000) {
lastCleanedIddb = now;
- OSUtils::cleanDirectory((_homePath + ZT_PATH_SEPARATOR_S "iddb.d").c_str(),now - ZT_IDDB_CLEANUP_AGE);
+ OSUtils::cleanDirectory(_iddbPath.c_str(),now - ZT_IDDB_CLEANUP_AGE);
}
// Attempt to detect sleep/wake events by detecting delay overruns
@@ -879,11 +863,13 @@ public:
// Refresh bindings in case device's interfaces have changed, and also sync routes to update any shadow routes (e.g. shadow default)
if (((now - lastBindRefresh) >= ZT_BINDER_REFRESH_PERIOD)||(restarted)) {
lastBindRefresh = now;
+ unsigned int p[3];
+ unsigned int pc = 0;
for(int i=0;i<3;++i) {
- if (_ports[i]) {
- _bindings[i].refresh(_phy,_ports[i],*this);
- }
+ if (_ports[i])
+ p[pc++] = _ports[i];
}
+ _binder.refresh(_phy,p,pc,*this);
{
Mutex::Lock _l(_nets_m);
for(std::map<uint64_t,NetworkState>::iterator n(_nets.begin());n!=_nets.end();++n) {
@@ -893,15 +879,18 @@ public:
}
}
+ // Run background task processor in core if it's time to do so
uint64_t dl = _nextBackgroundTaskDeadline;
if (dl <= now) {
_node->processBackgroundTasks((void *)0,now,&_nextBackgroundTaskDeadline);
dl = _nextBackgroundTaskDeadline;
}
+ // Close TCP fallback tunnel if we have direct UDP
if ((_tcpFallbackTunnel)&&((now - _lastDirectReceiveFromGlobal) < (ZT_TCP_FALLBACK_AFTER / 2)))
_phy.close(_tcpFallbackTunnel->sock);
+ // Sync multicast group memberships
if ((now - lastTapMulticastGroupCheck) >= ZT_TAP_CHECK_MULTICAST_INTERVAL) {
lastTapMulticastGroupCheck = now;
Mutex::Lock _l(_nets_m);
@@ -917,6 +906,7 @@ public:
}
}
+ // Sync information about physical network interfaces
if ((now - lastLocalInterfaceAddressCheck) >= ZT_LOCAL_INTERFACE_CHECK_INTERVAL) {
lastLocalInterfaceAddressCheck = now;
@@ -930,19 +920,65 @@ public:
}
#endif
- std::vector<InetAddress> boundAddrs(_bindings[0].allBoundLocalInterfaceAddresses());
+ std::vector<InetAddress> boundAddrs(_binder.allBoundLocalInterfaceAddresses());
for(std::vector<InetAddress>::const_iterator i(boundAddrs.begin());i!=boundAddrs.end();++i)
_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i)));
}
+ // Check TCP connections and cluster links
+ if ((now - lastTcpCheck) >= ZT_TCP_CHECK_PERIOD) {
+ lastTcpCheck = now;
+
+ std::vector<PhySocket *> toClose;
+ std::vector<InetAddress> clusterLinksUp;
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) {
+ TcpConnection *const tc = *c;
+ tc->writeq_m.lock();
+ const unsigned long wql = (unsigned long)tc->writeq.length();
+ tc->writeq_m.unlock();
+ if ((tc->sock)&&((wql > ZT_TCP_MAX_WRITEQ_SIZE)||((now - tc->lastReceive) > ZT_TCP_ACTIVITY_TIMEOUT))) {
+ toClose.push_back(tc->sock);
+ } else if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) {
+ clusterLinksUp.push_back(tc->remoteAddr);
+ sendMyCurrentClusterState(tc);
+ }
+ }
+ }
+ for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s)
+ _phy.close(*s,true);
+
+ {
+ Mutex::Lock _l(_localConfig_m);
+ for(std::vector<InetAddress>::const_iterator ca(_clusterBackplaneAddresses.begin());ca!=_clusterBackplaneAddresses.end();++ca) {
+ if (std::find(clusterLinksUp.begin(),clusterLinksUp.end(),*ca) == clusterLinksUp.end()) {
+ TcpConnection *tc = new TcpConnection();
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+
+ tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE;
+ tc->remoteAddr = *ca;
+ tc->lastReceive = OSUtils::now();
+ tc->parent = this;
+ tc->sock = (PhySocket *)0; // set in connect handler
+ tc->messageSize = 0;
+
+ tc->clusterMemberId = 0; // not known yet
+
+ bool connected = false;
+ _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&(*ca)),connected,(void *)tc,true);
+ }
+ }
+ }
+ }
+
const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
clockShouldBe = now + (uint64_t)delay;
_phy.poll(delay);
}
- } catch (std::exception &exc) {
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = exc.what();
} catch ( ... ) {
Mutex::Lock _l(_termReason_m);
_termReason = ONE_UNRECOVERABLE_ERROR;
@@ -950,6 +986,7 @@ public:
}
try {
+ Mutex::Lock _l(_tcpConnections_m);
while (!_tcpConnections.empty())
_phy.close((*_tcpConnections.begin())->sock);
} catch ( ... ) {}
@@ -991,35 +1028,35 @@ public:
}
#ifdef ZT_SDK
- virtual void leave(const char *hp)
- {
- _node->leave(Utils::hexStrToU64(hp),NULL,NULL);
- }
+ virtual void leave(const char *hp)
+ {
+ _node->leave(Utils::hexStrToU64(hp),NULL,NULL);
+ }
virtual void join(const char *hp)
{
_node->join(Utils::hexStrToU64(hp),NULL,NULL);
}
- virtual std::string givenHomePath()
- {
- return _homePath;
- }
+ virtual std::string givenHomePath()
+ {
+ return _homePath;
+ }
- virtual EthernetTap * getTap(uint64_t nwid)
- {
+ virtual EthernetTap * getTap(uint64_t nwid)
+ {
Mutex::Lock _l(_nets_m);
std::map<uint64_t,NetworkState>::const_iterator n(_nets.find(nwid));
if (n == _nets.end())
- return NULL;
+ return NULL;
return n->second.tap;
- }
+ }
- virtual EthernetTap *getTap(InetAddress &addr)
- {
- Mutex::Lock _l(_nets_m);
+ virtual EthernetTap *getTap(InetAddress &addr)
+ {
+ Mutex::Lock _l(_nets_m);
std::map<uint64_t,NetworkState>::iterator it;
- for(it = _nets.begin(); it != _nets.end(); it++) {
+ for(it = _nets.begin(); it != _nets.end(); it++) {
if(it->second.tap) {
for(int j=0; j<it->second.tap->_ips.size(); j++) {
if(it->second.tap->_ips[j].isEqualPrefix(addr) || it->second.tap->_ips[j].ipsEqual(addr) || it->second.tap->_ips[j].containsAddress(addr)) {
@@ -1027,9 +1064,9 @@ public:
}
}
}
- }
- return NULL;
- }
+ }
+ return NULL;
+ }
virtual Node * getNode()
{
@@ -1040,9 +1077,8 @@ public:
{
Mutex::Lock _l(_nets_m);
std::map<uint64_t,NetworkState>::iterator i;
- for(i = _nets.begin(); i != _nets.end(); i++) {
- delete i->second.tap;
- }
+ for(i = _nets.begin(); i != _nets.end(); i++)
+ delete i->second.tap;
}
#endif // ZT_SDK
@@ -1073,8 +1109,8 @@ public:
return false;
n->second.settings = settings;
- char nlcpath[256];
- Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "networks.d" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_homePath.c_str(),nwid);
+ char nlcpath[4096];
+ Utils::snprintf(nlcpath,sizeof(nlcpath),"%s" ZT_PATH_SEPARATOR_S "%.16llx.local.conf",_networksPath.c_str(),nwid);
FILE *out = fopen(nlcpath,"w");
if (out) {
fprintf(out,"allowManaged=%d\n",(int)n->second.settings.allowManaged);
@@ -1089,7 +1125,9 @@ public:
return true;
}
- // Internal implementation methods -----------------------------------------
+ // =========================================================================
+ // Internal implementation methods for control plane, route setup, etc.
+ // =========================================================================
inline unsigned int handleControlPlaneHttpRequest(
const InetAddress &fromAddress,
@@ -1216,7 +1254,6 @@ public:
settings["portMappingEnabled"] = false; // not supported in build
#endif
#ifndef ZT_SDK
-
settings["softwareUpdate"] = OSUtils::jsonString(settings["softwareUpdate"],ZT_SOFTWARE_UPDATE_DEFAULT);
settings["softwareUpdateChannel"] = OSUtils::jsonString(settings["softwareUpdateChannel"],ZT_SOFTWARE_UPDATE_DEFAULT_CHANNEL);
#endif
@@ -1224,6 +1261,7 @@ public:
res["planetWorldId"] = planet.id();
res["planetWorldTimestamp"] = planet.timestamp();
+/*
#ifdef ZT_ENABLE_CLUSTER
json cj;
ZT_ClusterStatus cs;
@@ -1250,6 +1288,7 @@ public:
#else
res["cluster"] = json();
#endif
+*/
scode = 200;
} else if (ps[0] == "moon") {
@@ -1770,24 +1809,237 @@ public:
}
// =========================================================================
- // Handlers for Node and Phy<> callbacks
+ // Cluster messaging functions
// =========================================================================
- inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
+ // mlen must be at least 24
+ void encryptClusterMessage(char *data,unsigned int mlen)
{
-#ifdef ZT_ENABLE_CLUSTER
- if (sock == _clusterMessageSocket) {
- _lastDirectReceiveFromGlobal = OSUtils::now();
- _node->clusterHandleIncomingMessage(data,len);
- return;
+ uint8_t key[32];
+ memcpy(key,_clusterKey,32);
+ for(int i=0;i<8;++i) key[i] ^= data[i];
+ Salsa20 s20(key,data + 8);
+
+ uint8_t macKey[32];
+ uint8_t mac[16];
+ memset(macKey,0,32);
+ s20.crypt12(macKey,macKey,32);
+ s20.crypt12(data + 24,data + 24,mlen - 24);
+ Poly1305::compute(mac,data + 24,mlen - 24,macKey);
+ memcpy(data + 16,mac,8);
+ }
+
+ void announceStatusToClusterMember(TcpConnection *tc)
+ {
+ try {
+ Buffer<8194> buf;
+
+ buf.appendRandom(16);
+ buf.addSize(8); // space for MAC
+ buf.append((uint8_t)CLUSTER_MESSAGE_STATUS);
+ buf.append(_clusterMemberId);
+ buf.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR);
+ buf.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR);
+ buf.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
+
+ std::vector<InetAddress> lif(_binder.allBoundLocalInterfaceAddresses());
+ buf.append((uint16_t)lif.size());
+ for(std::vector<InetAddress>::const_iterator i(lif.begin());i!=lif.end();++i)
+ i->serialize(buf);
+
+ Mutex::Lock _l(tc->writeq_m);
+
+ if (tc->writeq.length() == 0)
+ _phy.setNotifyWritable(tc->sock,true);
+
+ const unsigned int mlen = buf.size();
+ tc->writeq.push_back((char)((mlen >> 16) & 0xff));
+ tc->writeq.push_back((char)((mlen >> 8) & 0xff));
+ tc->writeq.push_back((char)(mlen & 0xff));
+
+ char *const data = reinterpret_cast<char *>(buf.unsafeData());
+ encryptClusterMessage(data,mlen);
+ tc->writeq.append(data,mlen);
+ } catch ( ... ) {
+ fprintf(stderr,"WARNING: unexpected exception announcing status to cluster members" ZT_EOL_S);
}
-#endif
+ }
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return;
-#endif
+ bool proxySendViaCluster(const InetAddress &fromAddress,const InetAddress &dest,const void *data,unsigned int len,unsigned int ttl)
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) {
+ TcpConnection *const tc = *c;
+ if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) {
+ Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m);
+ for(std::vector<InetAddress>::const_iterator i(tc->clusterMemberLocalAddresses.begin());i!=tc->clusterMemberLocalAddresses.end();++i) {
+ if (*i == fromAddress) {
+ Buffer<1024> buf;
+
+ buf.appendRandom(16);
+ buf.addSize(8); // space for MAC
+ buf.append((uint8_t)CLUSTER_MESSAGE_PROXY_SEND);
+ buf.append((uint8_t)ttl);
+ dest.serialize(buf);
+ fromAddress.serialize(buf);
+
+ Mutex::Lock _l3(tc->writeq_m);
+
+ if (tc->writeq.length() == 0)
+ _phy.setNotifyWritable(tc->sock,true);
+
+ const unsigned int mlen = buf.size() + len;
+ tc->writeq.push_back((char)((mlen >> 16) & 0xff));
+ tc->writeq.push_back((char)((mlen >> 8) & 0xff));
+ tc->writeq.push_back((char)(mlen & 0xff));
+
+ const unsigned long startpos = (unsigned long)tc->writeq.length();
+ tc->writeq.append(reinterpret_cast<const char *>(buf.data()),buf.size());
+ tc->writeq.append(reinterpret_cast<const char *>(data),len);
+
+ char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos;
+ encryptClusterMessage(outdata,mlen);
+
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ void replicateStateObject(const ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len,TcpConnection *tc)
+ {
+ char buf[34];
+ Mutex::Lock _l2(tc->writeq_m);
+
+ if (tc->writeq.length() == 0)
+ _phy.setNotifyWritable(tc->sock,true);
+
+ const unsigned int mlen = len + 34;
+
+ tc->writeq.push_back((char)((mlen >> 16) & 0xff));
+ tc->writeq.push_back((char)((mlen >> 8) & 0xff));
+ tc->writeq.push_back((char)(mlen & 0xff));
+
+ Utils::getSecureRandom(buf,16);
+ buf[24] = (char)CLUSTER_MESSAGE_STATE_OBJECT;
+ buf[25] = (char)type;
+ buf[26] = (char)((id >> 56) & 0xff);
+ buf[27] = (char)((id >> 48) & 0xff);
+ buf[28] = (char)((id >> 40) & 0xff);
+ buf[29] = (char)((id >> 32) & 0xff);
+ buf[30] = (char)((id >> 24) & 0xff);
+ buf[31] = (char)((id >> 16) & 0xff);
+ buf[32] = (char)((id >> 8) & 0xff);
+ buf[33] = (char)(id & 0xff);
+
+ const unsigned long startpos = (unsigned long)tc->writeq.length();
+ tc->writeq.append(buf,34);
+ tc->writeq.append(reinterpret_cast<const char *>(data),len);
+
+ char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos;
+ encryptClusterMessage(outdata,mlen);
+ }
+
+ void replicateStateObjectToCluster(const ZT_StateObjectType type,const uint64_t id,const void *const data,const unsigned int len,const uint64_t everyoneBut)
+ {
+ std::vector<uint64_t> sentTo;
+ if (everyoneBut)
+ sentTo.push_back(everyoneBut);
+ Mutex::Lock _l(_tcpConnections_m);
+ for(std::vector<TcpConnection *>::const_iterator ci(_tcpConnections.begin());ci!=_tcpConnections.end();++ci) {
+ TcpConnection *const c = *ci;
+ if ((c->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(c->clusterMemberId != 0)&&(std::find(sentTo.begin(),sentTo.end(),c->clusterMemberId) == sentTo.end())) {
+ sentTo.push_back(c->clusterMemberId);
+ replicateStateObject(type,id,data,len,c);
+ }
+ }
+ }
+
+ void writeStateObject(enum ZT_StateObjectType type,uint64_t id,const void *data,int len)
+ {
+ char p[4096];
+ bool secure = false;
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PEER_IDENTITY:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "iddb.d/%.10llx",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id);
+ secure = true;
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id);
+ break;
+ default:
+ p[0] = (char)0;
+ break;
+ }
+ if (p[0]) {
+ if (len >= 0) {
+ FILE *f = fopen(p,"w");
+ if (f) {
+ if (fwrite(data,len,1,f) != 1)
+ fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p);
+ fclose(f);
+ if (secure)
+ OSUtils::lockDownFile(p,false);
+ } else {
+ fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p);
+ }
+ } else {
+ OSUtils::rm(p);
+ }
+ }
+ }
+
+ void sendMyCurrentClusterState(TcpConnection *tc)
+ {
+ // We currently don't need to dump everything. Networks and moons are most important.
+ // The rest will get caught up rapidly due to constant peer updates, etc.
+ std::string buf;
+ std::vector<std::string> l(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str(),false));
+ for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) {
+ buf.clear();
+ if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) {
+ if (f->length() == 21) {
+ const uint64_t nwid = Utils::hexStrToU64(f->substr(0,16).c_str());
+ if (nwid)
+ replicateStateObject(ZT_STATE_OBJECT_NETWORK_CONFIG,nwid,buf.data(),(int)buf.length(),tc);
+ }
+ }
+ }
+ l = OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "moons.d").c_str(),false);
+ for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) {
+ buf.clear();
+ if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) {
+ if (f->length() == 21) {
+ const uint64_t moonId = Utils::hexStrToU64(f->substr(0,16).c_str());
+ if (moonId)
+ replicateStateObject(ZT_STATE_OBJECT_MOON,moonId,buf.data(),(int)buf.length(),tc);
+ }
+ }
+ }
+ }
+
+ // =========================================================================
+ // Handlers for Node and Phy<> callbacks
+ // =========================================================================
+
+ inline void phyOnDatagram(PhySocket *sock,void **uptr,const struct sockaddr *localAddr,const struct sockaddr *from,void *data,unsigned long len)
+ {
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(from)->ipScope() == InetAddress::IP_SCOPE_GLOBAL))
_lastDirectReceiveFromGlobal = OSUtils::now();
@@ -1811,38 +2063,33 @@ public:
inline void phyOnTcpConnect(PhySocket *sock,void **uptr,bool success)
{
- if (!success)
+ if (!success) {
+ phyOnTcpClose(sock,uptr);
return;
+ }
- // Outgoing TCP connections are always TCP fallback tunnel connections.
-
- TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
-
- tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
- tc->shouldKeepAlive = true;
- tc->parent = this;
+ TcpConnection *const tc = reinterpret_cast<TcpConnection *>(*uptr);
+ if (!tc) { // sanity check
+ _phy.close(sock,true);
+ return;
+ }
tc->sock = sock;
- // from and parser are not used
- tc->messageSize = 0; // unused
- tc->lastActivity = OSUtils::now();
- // HTTP stuff is not used
- tc->writeBuf = "";
- *uptr = (void *)tc;
-
- // Send "hello" message
- tc->writeBuf.push_back((char)0x17);
- tc->writeBuf.push_back((char)0x03);
- tc->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- tc->writeBuf.push_back((char)0x00);
- tc->writeBuf.push_back((char)0x04); // mlen == 4
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MAJOR);
- tc->writeBuf.push_back((char)ZEROTIER_ONE_VERSION_MINOR);
- tc->writeBuf.push_back((char)((ZEROTIER_ONE_VERSION_REVISION >> 8) & 0xff));
- tc->writeBuf.push_back((char)(ZEROTIER_ONE_VERSION_REVISION & 0xff));
- _phy.setNotifyWritable(sock,true);
-
- _tcpFallbackTunnel = tc;
+
+ if (tc->type == TcpConnection::TCP_TUNNEL_OUTGOING) {
+ if (_tcpFallbackTunnel)
+ _phy.close(_tcpFallbackTunnel->sock);
+ _tcpFallbackTunnel = tc;
+ _phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO));
+ } else if (tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE) {
+ {
+ Mutex::Lock _l(tc->writeq_m);
+ tc->writeq.push_back((char)0x93); // identifies type of connection as cluster backplane
+ }
+ announceStatusToClusterMember(tc);
+ _phy.setNotifyWritable(sock,true);
+ } else {
+ _phy.close(sock,true);
+ }
}
inline void phyOnTcpAccept(PhySocket *sockL,PhySocket *sockN,void **uptrL,void **uptrN,const struct sockaddr *from)
@@ -1852,149 +2099,321 @@ public:
return;
} else {
TcpConnection *tc = new TcpConnection();
- _tcpConnections.insert(tc);
- tc->type = TcpConnection::TCP_HTTP_INCOMING;
- tc->shouldKeepAlive = true;
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+
+ tc->type = TcpConnection::TCP_UNCATEGORIZED_INCOMING;
tc->parent = this;
tc->sock = sockN;
- tc->from = from;
+ tc->remoteAddr = from;
+ tc->lastReceive = OSUtils::now();
http_parser_init(&(tc->parser),HTTP_REQUEST);
tc->parser.data = (void *)tc;
tc->messageSize = 0;
- tc->lastActivity = OSUtils::now();
- tc->currentHeaderField = "";
- tc->currentHeaderValue = "";
- tc->url = "";
- tc->status = "";
- tc->headers.clear();
- tc->body = "";
- tc->writeBuf = "";
+
*uptrN = (void *)tc;
}
}
- inline void phyOnTcpClose(PhySocket *sock,void **uptr)
+ void phyOnTcpClose(PhySocket *sock,void **uptr)
{
TcpConnection *tc = (TcpConnection *)*uptr;
if (tc) {
- if (tc == _tcpFallbackTunnel)
+ if (tc == _tcpFallbackTunnel) {
_tcpFallbackTunnel = (TcpConnection *)0;
- _tcpConnections.erase(tc);
+ }
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.erase(std::remove(_tcpConnections.begin(),_tcpConnections.end(),tc),_tcpConnections.end());
+ }
delete tc;
}
}
- inline void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
+ void phyOnTcpData(PhySocket *sock,void **uptr,void *data,unsigned long len)
{
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- switch(tc->type) {
+ try {
+ if (!len) return; // sanity check, should never happen
+ TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
+ tc->lastReceive = OSUtils::now();
+ switch(tc->type) {
+
+ case TcpConnection::TCP_UNCATEGORIZED_INCOMING:
+ switch(reinterpret_cast<uint8_t *>(data)[0]) {
+ // 0x93 is first byte of cluster backplane connections
+ case 0x93: {
+ // We only allow this from cluster backplane IPs. We also authenticate
+ // each packet cryptographically, so this is just a first line of defense.
+ bool allow = false;
+ {
+ Mutex::Lock _l(_localConfig_m);
+ for(std::vector< InetAddress >::const_iterator i(_clusterBackplaneAddresses.begin());i!=_clusterBackplaneAddresses.end();++i) {
+ if (tc->remoteAddr.ipsEqual(*i)) {
+ allow = true;
+ break;
+ }
+ }
+ }
+ if (allow) {
+ tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE;
+ tc->clusterMemberId = 0; // unknown, waiting for first status message
+ announceStatusToClusterMember(tc);
+ if (len > 1)
+ phyOnTcpData(sock,uptr,reinterpret_cast<uint8_t *>(data) + 1,len - 1);
+ } else {
+ _phy.close(sock);
+ }
+ } break;
+
+ // HTTP: GET, PUT, POST, HEAD
+ case 'G':
+ case 'P':
+ case 'H': {
+ // This is only allowed from IPs permitted to access the management
+ // backplane, which is just 127.0.0.1/::1 unless otherwise configured.
+ bool allow;
+ {
+ Mutex::Lock _l(_localConfig_m);
+ if (_allowManagementFrom.size() == 0) {
+ allow = (tc->remoteAddr.ipScope() == InetAddress::IP_SCOPE_LOOPBACK);
+ } else {
+ allow = false;
+ for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) {
+ if (i->containsAddress(tc->remoteAddr)) {
+ allow = true;
+ break;
+ }
+ }
+ }
+ }
+ if (allow) {
+ tc->type = TcpConnection::TCP_HTTP_INCOMING;
+ phyOnTcpData(sock,uptr,data,len);
+ } else {
+ _phy.close(sock);
+ }
+ } break;
- case TcpConnection::TCP_HTTP_INCOMING:
- case TcpConnection::TCP_HTTP_OUTGOING:
- http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
- if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK)) {
- _phy.close(sock);
+ // Drop unknown protocols
+ default:
+ _phy.close(sock);
+ break;
+ }
+ return;
+
+ case TcpConnection::TCP_HTTP_INCOMING:
+ case TcpConnection::TCP_HTTP_OUTGOING:
+ http_parser_execute(&(tc->parser),&HTTP_PARSER_SETTINGS,(const char *)data,len);
+ if ((tc->parser.upgrade)||(tc->parser.http_errno != HPE_OK))
+ _phy.close(sock);
return;
- }
- break;
- case TcpConnection::TCP_TUNNEL_OUTGOING:
- tc->body.append((const char *)data,len);
- while (tc->body.length() >= 5) {
- const char *data = tc->body.data();
- const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
- if (tc->body.length() >= (mlen + 5)) {
- InetAddress from;
+ case TcpConnection::TCP_TUNNEL_OUTGOING:
+ tc->readq.append((const char *)data,len);
+ while (tc->readq.length() >= 5) {
+ const char *data = tc->readq.data();
+ const unsigned long mlen = ( ((((unsigned long)data[3]) & 0xff) << 8) | (((unsigned long)data[4]) & 0xff) );
+ if (tc->readq.length() >= (mlen + 5)) {
+ InetAddress from;
unsigned long plen = mlen; // payload length, modified if there's an IP header
- data += 5; // skip forward past pseudo-TLS junk and mlen
- if (plen == 4) {
- // Hello message, which isn't sent by proxy and would be ignored by client
- } else if (plen) {
- // Messages should contain IPv4 or IPv6 source IP address data
- switch(data[0]) {
- case 4: // IPv4
- if (plen >= 7) {
- from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
- data += 7; // type + 4 byte IP + 2 byte port
- plen -= 7;
- } else {
+ data += 5; // skip forward past pseudo-TLS junk and mlen
+ if (plen == 4) {
+ // Hello message, which isn't sent by proxy and would be ignored by client
+ } else if (plen) {
+ // Messages should contain IPv4 or IPv6 source IP address data
+ switch(data[0]) {
+ case 4: // IPv4
+ if (plen >= 7) {
+ from.set((const void *)(data + 1),4,((((unsigned int)data[5]) & 0xff) << 8) | (((unsigned int)data[6]) & 0xff));
+ data += 7; // type + 4 byte IP + 2 byte port
+ plen -= 7;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 6: // IPv6
+ if (plen >= 19) {
+ from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
+ data += 19; // type + 16 byte IP + 2 byte port
+ plen -= 19;
+ } else {
+ _phy.close(sock);
+ return;
+ }
+ break;
+ case 0: // none/omitted
+ ++data;
+ --plen;
+ break;
+ default: // invalid address type
_phy.close(sock);
return;
- }
- break;
- case 6: // IPv6
- if (plen >= 19) {
- from.set((const void *)(data + 1),16,((((unsigned int)data[17]) & 0xff) << 8) | (((unsigned int)data[18]) & 0xff));
- data += 19; // type + 16 byte IP + 2 byte port
- plen -= 19;
- } else {
+ }
+
+ if (from) {
+ InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
+ const ZT_ResultCode rc = _node->processWirePacket(
+ (void *)0,
+ OSUtils::now(),
+ reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
+ reinterpret_cast<struct sockaddr_storage *>(&from),
+ data,
+ plen,
+ &_nextBackgroundTaskDeadline);
+ if (ZT_ResultCode_isFatal(rc)) {
+ char tmp[256];
+ Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
+ Mutex::Lock _l(_termReason_m);
+ _termReason = ONE_UNRECOVERABLE_ERROR;
+ _fatalErrorMessage = tmp;
+ this->terminate();
_phy.close(sock);
return;
}
+ }
+ }
+
+ if (tc->readq.length() > (mlen + 5))
+ tc->readq.erase(tc->readq.begin(),tc->readq.begin() + (mlen + 5));
+ else tc->readq.clear();
+ } else break;
+ }
+ return;
+
+ case TcpConnection::TCP_CLUSTER_BACKPLANE:
+ tc->readq.append((const char *)data,len);
+ if (tc->readq.length() >= 28) { // got 3-byte message size + 16-byte IV + 8-byte MAC + 1-byte type (encrypted)
+ uint8_t *data = reinterpret_cast<uint8_t *>(const_cast<char *>(tc->readq.data()));
+ unsigned long mlen = ( ((unsigned long)data[0] << 16) | ((unsigned long)data[1] << 8) | (unsigned long)data[2] );
+ if ((mlen < 25)||(mlen > ZT_TCP_MAX_WRITEQ_SIZE)) {
+ _phy.close(sock);
+ return;
+ } else if (tc->readq.length() >= (mlen + 3)) { // got entire message
+ data += 3;
+
+ uint8_t key[32];
+ memcpy(key,_clusterKey,32);
+ for(int i=0;i<8;++i) key[i] ^= data[i]; // first 8 bytes of IV get XORed with key
+ Salsa20 s20(key,data + 8); // last 8 bytes of IV are fed into Salsa20 directly as its 64-bit IV
+
+ uint8_t macKey[32];
+ uint8_t mac[16];
+ memset(macKey,0,32);
+ s20.crypt12(macKey,macKey,32);
+ Poly1305::compute(mac,data + 24,mlen - 24,macKey);
+ if (!Utils::secureEq(mac,data + 16,8)) {
+ _phy.close(sock);
+ return;
+ }
+ s20.crypt12(data + 24,data + 24,mlen - 24);
+
+ switch((ClusterMessageType)data[24]) {
+ case CLUSTER_MESSAGE_STATUS:
+ if (mlen > (25 + 16)) {
+ Buffer<4096> tmp(data + 25,mlen - 25);
+ try {
+ const uint64_t cmid = tmp.at<uint64_t>(0);
+ if (cmid == _clusterMemberId) { // shouldn't happen, but don't allow self-to-self
+ _phy.close(sock);
+ return;
+ }
+ if (!tc->clusterMemberId) {
+ tc->clusterMemberId = cmid;
+ sendMyCurrentClusterState(tc);
+ }
+ tc->clusterMemberVersionMajor = tmp.at<uint16_t>(8);
+ tc->clusterMemberVersionMinor = tmp.at<uint16_t>(10);
+ tc->clusterMemberVersionRev = tmp.at<uint16_t>(12);
+ const unsigned int clusterMemberLocalAddressCount = tmp.at<uint16_t>(14);
+ std::vector<InetAddress> la;
+ unsigned int ptr = 16;
+ for(unsigned int k=0;k<clusterMemberLocalAddressCount;++k) {
+ la.push_back(InetAddress());
+ ptr += la.back().deserialize(tmp,ptr);
+ }
+ {
+ Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m);
+ tc->clusterMemberLocalAddresses.swap(la);
+ }
+ } catch ( ... ) {}
+ }
break;
- case 0: // none/omitted
- ++data;
- --plen;
+
+ case CLUSTER_MESSAGE_STATE_OBJECT:
+ if (mlen >= (25 + 9)) { // type + object ID + [data]
+ const uint64_t objId = (
+ ((uint64_t)data[26] << 56) |
+ ((uint64_t)data[27] << 48) |
+ ((uint64_t)data[28] << 40) |
+ ((uint64_t)data[29] << 32) |
+ ((uint64_t)data[30] << 24) |
+ ((uint64_t)data[31] << 16) |
+ ((uint64_t)data[32] << 8) |
+ (uint64_t)data[33]
+ );
+ if (_node->processStateUpdate((void *)0,(ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34)) == ZT_RESULT_OK) {
+ writeStateObject((ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34));
+ replicateStateObjectToCluster((ZT_StateObjectType)data[25],objId,data + 34,(unsigned int)(mlen - 34),tc->clusterMemberId);
+ }
+ }
break;
- default: // invalid address type
- _phy.close(sock);
- return;
- }
- if (from) {
- InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
- const ZT_ResultCode rc = _node->processWirePacket(
- (void *)0,
- OSUtils::now(),
- reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
- reinterpret_cast<struct sockaddr_storage *>(&from),
- data,
- plen,
- &_nextBackgroundTaskDeadline);
- if (ZT_ResultCode_isFatal(rc)) {
- char tmp[256];
- Utils::snprintf(tmp,sizeof(tmp),"fatal error code from processWirePacket: %d",(int)rc);
- Mutex::Lock _l(_termReason_m);
- _termReason = ONE_UNRECOVERABLE_ERROR;
- _fatalErrorMessage = tmp;
- this->terminate();
- _phy.close(sock);
- return;
- }
+ case CLUSTER_MESSAGE_PROXY_SEND:
+ if (mlen > 25) {
+ Buffer<4096> tmp(data + 25,mlen - 25);
+ try {
+ InetAddress dest,src;
+ const unsigned int ttl = (unsigned int)tmp[0];
+ unsigned int ptr = 1;
+ ptr += dest.deserialize(tmp);
+ ptr += src.deserialize(tmp,ptr);
+ if (ptr < tmp.size())
+ _binder.udpSend(_phy,src,dest,reinterpret_cast<const uint8_t *>(tmp.data()) + ptr,tmp.size() - ptr,ttl);
+ } catch ( ... ) {}
+ }
+ break;
}
- }
- if (tc->body.length() > (mlen + 5))
- tc->body = tc->body.substr(mlen + 5);
- else tc->body = "";
- } else break;
- }
- break;
+ tc->readq.erase(tc->readq.begin(),tc->readq.begin() + mlen);
+ }
+ }
+ return;
+ }
+ } catch ( ... ) {
+ _phy.close(sock);
}
}
inline void phyOnTcpWritable(PhySocket *sock,void **uptr)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(*uptr);
- Mutex::Lock _l(tc->writeBuf_m);
- if (tc->writeBuf.length() > 0) {
- long sent = (long)_phy.streamSend(sock,tc->writeBuf.data(),(unsigned long)tc->writeBuf.length(),true);
- if (sent > 0) {
- tc->lastActivity = OSUtils::now();
- if ((unsigned long)sent >= (unsigned long)tc->writeBuf.length()) {
- tc->writeBuf = "";
- _phy.setNotifyWritable(sock,false);
- if (!tc->shouldKeepAlive)
- _phy.close(sock); // will call close handler to delete from _tcpConnections
- } else {
- tc->writeBuf = tc->writeBuf.substr(sent);
+ bool closeit = false;
+ {
+ Mutex::Lock _l(tc->writeq_m);
+ if (tc->writeq.length() > 0) {
+ long sent = (long)_phy.streamSend(sock,tc->writeq.data(),(unsigned long)tc->writeq.length(),true);
+ if (sent > 0) {
+ if ((unsigned long)sent >= (unsigned long)tc->writeq.length()) {
+ tc->writeq.clear();
+ _phy.setNotifyWritable(sock,false);
+
+ if (tc->type == TcpConnection::TCP_HTTP_INCOMING)
+ closeit = true; // HTTP keep alive not supported
+ } else {
+ tc->writeq.erase(tc->writeq.begin(),tc->writeq.begin() + sent);
+ }
}
+ } else {
+ _phy.setNotifyWritable(sock,false);
}
- } else {
- _phy.setNotifyWritable(sock,false);
}
+ if (closeit)
+ _phy.close(sock);
}
inline void phyOnFileDescriptorActivity(PhySocket *sock,void **uptr,bool readable,bool writable) {}
@@ -2155,80 +2574,51 @@ public:
}
}
- inline long nodeDataStoreGetFunction(const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
+ inline void nodeStatePutFunction(enum ZT_StateObjectType type,uint64_t id,const void *data,int len)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- FILE *f = fopen(p.c_str(),"rb");
- if (!f)
- return -1;
- if (fseek(f,0,SEEK_END) != 0) {
- fclose(f);
- return -2;
- }
- long ts = ftell(f);
- if (ts < 0) {
- fclose(f);
- return -2;
- }
- *totalSize = (unsigned long)ts;
- if (fseek(f,(long)readIndex,SEEK_SET) != 0) {
- fclose(f);
- return -2;
- }
- long n = (long)fread(buf,1,bufSize,f);
- fclose(f);
- return n;
+ writeStateObject(type,id,data,len);
+ replicateStateObjectToCluster(type,id,data,len,0);
}
- inline int nodeDataStorePutFunction(const char *name,const void *data,unsigned long len,int secure)
+ inline int nodeStateGetFunction(enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen)
{
- std::string p(_dataStorePrepPath(name));
- if (!p.length())
- return -2;
-
- if (!data) {
- OSUtils::rm(p.c_str());
- return 0;
+ char p[4096];
+ switch(type) {
+ case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_IDENTITY_SECRET:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_PEER_IDENTITY:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "iddb.d/%.10llx",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_NETWORK_CONFIG:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id);
+ break;
+ case ZT_STATE_OBJECT_PLANET:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+ break;
+ case ZT_STATE_OBJECT_MOON:
+ Utils::snprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id);
+ break;
+ default:
+ return -1;
}
-
- FILE *f = fopen(p.c_str(),"wb");
- if (!f)
- return -1;
- if (fwrite(data,len,1,f) == 1) {
- fclose(f);
- if (secure)
- OSUtils::lockDownFile(p.c_str(),false);
- return 0;
- } else {
+ FILE *f = fopen(p,"r");
+ if (f) {
+ int n = (int)fread(data,1,maxlen,f);
fclose(f);
- OSUtils::rm(p.c_str());
- return -1;
+ if (n >= 0)
+ return n;
}
+ return -1;
}
inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{
- unsigned int fromBindingNo = 0;
-
- if (addr->ss_family == AF_INET) {
- if (reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port == 0) {
- // If sender is sending from wildcard (null address), choose the secondary backup
- // port 1/4 of the time. (but only for IPv4)
- fromBindingNo = (++_udpPortPickerCounter & 0x4) >> 2;
- if (!_ports[fromBindingNo])
- fromBindingNo = 0;
- } else {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in *>(localAddr)->sin_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
-
#ifdef ZT_TCP_FALLBACK_RELAY
+ if (addr->ss_family == AF_INET) {
// TCP fallback tunnel support, currently IPv4 only
if ((len >= 16)&&(reinterpret_cast<const InetAddress *>(addr)->ipScope() == InetAddress::IP_SCOPE_GLOBAL)) {
// Engage TCP tunnel fallback if we haven't received anything valid from a global
@@ -2237,46 +2627,59 @@ public:
const uint64_t now = OSUtils::now();
if (((now - _lastDirectReceiveFromGlobal) > ZT_TCP_FALLBACK_AFTER)&&((now - _lastRestart) > ZT_TCP_FALLBACK_AFTER)) {
if (_tcpFallbackTunnel) {
- Mutex::Lock _l(_tcpFallbackTunnel->writeBuf_m);
- if (!_tcpFallbackTunnel->writeBuf.length())
+ Mutex::Lock _l(_tcpFallbackTunnel->writeq_m);
+ if (_tcpFallbackTunnel->writeq.length() == 0)
_phy.setNotifyWritable(_tcpFallbackTunnel->sock,true);
- unsigned long mlen = len + 7;
- _tcpFallbackTunnel->writeBuf.push_back((char)0x17);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03);
- _tcpFallbackTunnel->writeBuf.push_back((char)0x03); // fake TLS 1.2 header
- _tcpFallbackTunnel->writeBuf.push_back((char)((mlen >> 8) & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)(mlen & 0xff));
- _tcpFallbackTunnel->writeBuf.push_back((char)4); // IPv4
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
- _tcpFallbackTunnel->writeBuf.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
- _tcpFallbackTunnel->writeBuf.append((const char *)data,len);
+ const unsigned long mlen = len + 7;
+ _tcpFallbackTunnel->writeq.push_back((char)0x17);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03);
+ _tcpFallbackTunnel->writeq.push_back((char)0x03); // fake TLS 1.2 header
+ _tcpFallbackTunnel->writeq.push_back((char)((mlen >> 8) & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)(mlen & 0xff));
+ _tcpFallbackTunnel->writeq.push_back((char)4); // IPv4
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_addr.s_addr))),4);
+ _tcpFallbackTunnel->writeq.append(reinterpret_cast<const char *>(reinterpret_cast<const void *>(&(reinterpret_cast<const struct sockaddr_in *>(addr)->sin_port))),2);
+ _tcpFallbackTunnel->writeq.append((const char *)data,len);
} else if (((now - _lastSendToGlobalV4) < ZT_TCP_FALLBACK_AFTER)&&((now - _lastSendToGlobalV4) > (ZT_PING_CHECK_INVERVAL / 2))) {
- bool connected = false;
const InetAddress addr(ZT_TCP_FALLBACK_RELAY);
- _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected);
+ TcpConnection *tc = new TcpConnection();
+ {
+ Mutex::Lock _l(_tcpConnections_m);
+ _tcpConnections.push_back(tc);
+ }
+ tc->type = TcpConnection::TCP_TUNNEL_OUTGOING;
+ tc->remoteAddr = addr;
+ tc->lastReceive = OSUtils::now();
+ tc->parent = this;
+ tc->sock = (PhySocket *)0; // set in connect handler
+ tc->messageSize = 0;
+ bool connected = false;
+ _phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&addr),connected,(void *)tc,true);
}
}
_lastSendToGlobalV4 = now;
}
-#endif // ZT_TCP_FALLBACK_RELAY
- } else if (addr->ss_family == AF_INET6) {
- if (reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port != 0) {
- const uint16_t lp = reinterpret_cast<const struct sockaddr_in6 *>(localAddr)->sin6_port;
- if (lp == _portsBE[1])
- fromBindingNo = 1;
- else if (lp == _portsBE[2])
- fromBindingNo = 2;
- }
- } else {
- return -1;
}
+ // Even when relaying we still send via UDP. This way if UDP starts
+ // working we can instantly "fail forward" to it and stop using TCP
+ // proxy fallback, which is slow.
+#endif // ZT_TCP_FALLBACK_RELAY
-#ifdef ZT_BREAK_UDP
- if (OSUtils::fileExists("/tmp/ZT_BREAK_UDP"))
- return 0; // silently break UDP
-#endif
+ switch (_binder.udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) {
+ case -1: // local bound address not found, so see if a cluster peer owns it
+ if (localAddr->ss_family != 0) {
+ return (proxySendViaCluster(*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
+ } else {
+ return -1; // failure
+ }
+ break;
+
+ case 0: // failure
+ return -1;
- return (_bindings[fromBindingNo].udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
+ default: // success
+ return 0;
+ }
}
inline void nodeVirtualNetworkFrameFunction(uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -2362,39 +2765,22 @@ public:
inline void onHttpRequestToServer(TcpConnection *tc)
{
- char tmpn[256];
+ char tmpn[4096];
std::string data;
std::string contentType("text/plain"); // default if not changed in handleRequest()
unsigned int scode = 404;
- bool allow;
- {
- Mutex::Lock _l(_localConfig_m);
- if (_allowManagementFrom.size() == 0) {
- allow = (tc->from.ipScope() == InetAddress::IP_SCOPE_LOOPBACK);
- } else {
- allow = false;
- for(std::vector<InetAddress>::const_iterator i(_allowManagementFrom.begin());i!=_allowManagementFrom.end();++i) {
- if (i->containsAddress(tc->from)) {
- allow = true;
- break;
- }
- }
- }
- }
+ // Note that we check allowed IP ranges when HTTP connections are first detected in
+ // phyOnTcpData(). If we made it here the source IP is okay.
- if (allow) {
- try {
- scode = handleControlPlaneHttpRequest(tc->from,tc->parser.method,tc->url,tc->headers,tc->body,data,contentType);
- } catch (std::exception &exc) {
- fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
- scode = 500;
- } catch ( ... ) {
- fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
- scode = 500;
- }
- } else {
- scode = 401;
+ try {
+ scode = handleControlPlaneHttpRequest(tc->remoteAddr,tc->parser.method,tc->url,tc->headers,tc->readq,data,contentType);
+ } catch (std::exception &exc) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: %s" ZT_EOL_S,exc.what());
+ scode = 500;
+ } catch ( ... ) {
+ fprintf(stderr,"WARNING: unexpected exception processing control HTTP request: unknown exceptino" ZT_EOL_S);
+ scode = 500;
}
const char *scodestr;
@@ -2410,19 +2796,16 @@ public:
default: scodestr = "Error"; break;
}
- Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\n",scode,scodestr);
+ Utils::snprintf(tmpn,sizeof(tmpn),"HTTP/1.1 %.3u %s\r\nCache-Control: no-cache\r\nPragma: no-cache\r\nContent-Type: %s\r\nContent-Length: %lu\r\nConnection: close\r\n\r\n",
+ scode,
+ scodestr,
+ contentType.c_str(),
+ (unsigned long)data.length());
{
- Mutex::Lock _l(tc->writeBuf_m);
- tc->writeBuf.assign(tmpn);
- tc->writeBuf.append("Content-Type: ");
- tc->writeBuf.append(contentType);
- Utils::snprintf(tmpn,sizeof(tmpn),"\r\nContent-Length: %lu\r\n",(unsigned long)data.length());
- tc->writeBuf.append(tmpn);
- if (!tc->shouldKeepAlive)
- tc->writeBuf.append("Connection: close\r\n");
- tc->writeBuf.append("\r\n");
+ Mutex::Lock _l(tc->writeq_m);
+ tc->writeq = tmpn;
if (tc->parser.method != HTTP_HEAD)
- tc->writeBuf.append(data);
+ tc->writeq.append(data);
}
_phy.setNotifyWritable(tc->sock,true);
@@ -2430,8 +2813,7 @@ public:
inline void onHttpResponseFromClient(TcpConnection *tc)
{
- if (!tc->shouldKeepAlive)
- _phy.close(tc->sock); // will call close handler, which deletes from _tcpConnections
+ _phy.close(tc->sock);
}
bool shouldBindInterface(const char *ifname,const InetAddress &ifaddr)
@@ -2475,23 +2857,6 @@ public:
return true;
}
- std::string _dataStorePrepPath(const char *name) const
- {
- std::string p(_homePath);
- p.push_back(ZT_PATH_SEPARATOR);
- char lastc = (char)0;
- for(const char *n=name;(*n);++n) {
- if ((*n == '.')&&(lastc == '.'))
- return std::string(); // don't allow ../../ stuff as a precaution
- if (*n == '/') {
- OSUtils::mkdir(p.c_str());
- p.push_back(ZT_PATH_SEPARATOR);
- } else p.push_back(*n);
- lastc = *n;
- }
- return p;
- }
-
bool _trialBind(unsigned int port)
{
struct sockaddr_in in4;
@@ -2532,10 +2897,10 @@ static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); }
static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData)
{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeEventCallback(event,metaData); }
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); }
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
+static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,const void *data,int len)
+{ reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); }
+static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,uint64_t id,void *data,unsigned int maxlen)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); }
static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
@@ -2544,22 +2909,6 @@ static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t z
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); }
static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
-
-#ifdef ZT_ENABLE_CLUSTER
-static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- const ClusterDefinition::MemberDefinition &md = (*(impl->_clusterDefinition))[toMemberId];
- if (md.clusterEndpoint)
- impl->_phy.udpSend(impl->_clusterMessageSocket,reinterpret_cast<const struct sockaddr *>(&(md.clusterEndpoint)),data,len);
-}
-static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z)
-{
- OneServiceImpl *const impl = reinterpret_cast<OneServiceImpl *>(uptr);
- return (int)(impl->_clusterDefinition->geo().locate(*(reinterpret_cast<const InetAddress *>(addr)),*x,*y,*z));
-}
-#endif
-
static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
{ reinterpret_cast<OneServiceImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); }
@@ -2569,10 +2918,10 @@ static int ShttpOnMessageBegin(http_parser *parser)
tc->currentHeaderField = "";
tc->currentHeaderValue = "";
tc->messageSize = 0;
- tc->url = "";
- tc->status = "";
+ tc->url.clear();
+ tc->status.clear();
tc->headers.clear();
- tc->body = "";
+ tc->readq.clear();
return 0;
}
static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length)
@@ -2589,16 +2938,7 @@ static int ShttpOnStatus(http_parser *parser,const char *ptr,size_t length)
#else
static int ShttpOnStatus(http_parser *parser)
#endif
-{
- /*
- TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->messageSize += (unsigned long)length;
- if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
- return -1;
- tc->status.append(ptr,length);
- */
- return 0;
-}
+{ return 0; }
static int ShttpOnHeaderField(http_parser *parser,const char *ptr,size_t length)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
@@ -2636,14 +2976,12 @@ static int ShttpOnBody(http_parser *parser,const char *ptr,size_t length)
tc->messageSize += (unsigned long)length;
if (tc->messageSize > ZT_MAX_HTTP_MESSAGE_SIZE)
return -1;
- tc->body.append(ptr,length);
+ tc->readq.append(ptr,length);
return 0;
}
static int ShttpOnMessageComplete(http_parser *parser)
{
TcpConnection *tc = reinterpret_cast<TcpConnection *>(parser->data);
- tc->shouldKeepAlive = (http_should_keep_alive(parser) != 0);
- tc->lastActivity = OSUtils::now();
if (tc->type == TcpConnection::TCP_HTTP_INCOMING) {
tc->parent->onHttpRequestToServer(tc);
} else {
diff --git a/service/OneService.hpp b/service/OneService.hpp
index b770a3c0..eba10ca0 100644
--- a/service/OneService.hpp
+++ b/service/OneService.hpp
@@ -33,10 +33,10 @@
#include "../node/InetAddress.hpp"
#ifdef ZT_SDK
- #include "../node/Node.hpp"
- // Use the virtual netcon endpoint instead of a tun/tap port driver
- #include "../src/SocketTap.hpp"
- namespace ZeroTier { typedef SocketTap EthernetTap; }
+#include "../node/Node.hpp"
+// Use the virtual netcon endpoint instead of a tun/tap port driver
+#include "../src/SocketTap.hpp"
+namespace ZeroTier { typedef SocketTap EthernetTap; }
#endif
namespace ZeroTier {
@@ -147,42 +147,15 @@ public:
virtual std::string portDeviceName(uint64_t nwid) const = 0;
#ifdef ZT_SDK
- /**
- * Leaves a network
- */
- virtual void leave(const char *hp) = 0;
-
- /**
- * Joins a network
- */
+ virtual void leave(const char *hp) = 0;
virtual void join(const char *hp) = 0;
-
- /**
- * Returns the homePath given by the client application
- */
- virtual std::string givenHomePath() = 0;
-
- /*
- * Returns a SocketTap that is associated with the given nwid
- */
- virtual EthernetTap * getTap(uint64_t nwid) = 0;
-
- /*
- * Returns a SocketTap that cant function as a route to the specified host
- */
- virtual EthernetTap * getTap(InetAddress &addr) = 0;
-
- /*
- * Returns a pointer to the Node
- */
+ virtual std::string givenHomePath() = 0;
+ virtual EthernetTap * getTap(uint64_t nwid) = 0;
+ virtual EthernetTap * getTap(InetAddress &addr) = 0;
virtual Node * getNode() = 0;
-
- /*
- * Delete all SocketTap interfaces
- */
virtual void removeNets() = 0;
#endif
-
+
/**
* Terminate background service (can be called from other threads)
*/