summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.gitignore3
-rw-r--r--controller/SqliteNetworkController.cpp62
-rw-r--r--controller/SqliteNetworkController.hpp7
-rw-r--r--make-linux.mk3
-rw-r--r--node/InetAddress.hpp2
-rw-r--r--node/Peer.cpp9
-rw-r--r--node/Switch.cpp85
-rw-r--r--osdep/OSUtils.hpp1
-rw-r--r--selftest.cpp1
-rw-r--r--tests/http/agent.js8
-rw-r--r--tests/http/big-test-hosts2
-rwxr-xr-xtests/http/big-test-kill.sh13
-rwxr-xr-xtests/http/big-test-ready.sh30
-rwxr-xr-xtests/http/big-test-start.sh26
-rwxr-xr-xtests/http/docker-main.sh4
15 files changed, 161 insertions, 95 deletions
diff --git a/.gitignore b/.gitignore
index cfdd4bd0..211ebf83 100755
--- a/.gitignore
+++ b/.gitignore
@@ -53,8 +53,7 @@ node_modules
cluster-geo/cluster-geo/config.js
cluster-geo/cluster-geo/cache.*
tests/http/zerotier-one
-tests/http/result_*
-tests/http/big-test-out
+tests/http/big-test-hosts
# MacGap wrapper build files
/ext/mac-ui-macgap1-wrapper/src/MacGap.xcodeproj/project.xcworkspace/xcuserdata/*
diff --git a/controller/SqliteNetworkController.cpp b/controller/SqliteNetworkController.cpp
index 52b47665..049db04e 100644
--- a/controller/SqliteNetworkController.cpp
+++ b/controller/SqliteNetworkController.cpp
@@ -71,6 +71,9 @@
// than this (ms).
#define ZT_NETCONF_MIN_REQUEST_PERIOD 1000
+// Delay between backups in milliseconds
+#define ZT_NETCONF_BACKUP_PERIOD 60000
+
namespace ZeroTier {
namespace {
@@ -122,6 +125,7 @@ struct NetworkRecord {
SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,const char *circuitTestPath) :
_node(node),
+ _backupThreadRun(true),
_dbPath(dbPath),
_circuitTestPath(circuitTestPath),
_db((sqlite3 *)0)
@@ -247,10 +251,15 @@ SqliteNetworkController::SqliteNetworkController(Node *node,const char *dbPath,c
throw std::runtime_error("SqliteNetworkController unable to read instanceId (it's NULL)");
_instanceId = iid;
}
+
+ _backupThread = Thread::start(this);
}
SqliteNetworkController::~SqliteNetworkController()
{
+ _backupThreadRun = false;
+ Thread::join(_backupThread);
+
Mutex::Lock _l(_lock);
if (_db) {
sqlite3_finalize(_sGetNetworkById);
@@ -991,6 +1000,59 @@ unsigned int SqliteNetworkController::handleControlPlaneHttpDELETE(
return 404;
}
+void SqliteNetworkController::threadMain()
+ throw()
+{
+ uint64_t lastBackupTime = 0;
+ while (_backupThreadRun) {
+ if ((OSUtils::now() - lastBackupTime) >= ZT_NETCONF_BACKUP_PERIOD) {
+ lastBackupTime = OSUtils::now();
+
+ char backupPath[4096],backupPath2[4096];
+ Utils::snprintf(backupPath,sizeof(backupPath),"%s.backupInProgress",_dbPath.c_str());
+ Utils::snprintf(backupPath2,sizeof(backupPath),"%s.backup",_dbPath.c_str());
+ OSUtils::rm(backupPath); // delete any unfinished backups
+
+ sqlite3 *bakdb = (sqlite3 *)0;
+ sqlite3_backup *bak = (sqlite3_backup *)0;
+ if (sqlite3_open_v2(backupPath,&bakdb,SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,(const char *)0) != SQLITE_OK) {
+ fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_open_v2()"ZT_EOL_S);
+ continue;
+ }
+ bak = sqlite3_backup_init(bakdb,"main",_db,"main");
+ if (!bak) {
+ sqlite3_close(bakdb);
+ OSUtils::rm(backupPath); // delete any unfinished backups
+ fprintf(stderr,"SqliteNetworkController: CRITICAL: backup failed on sqlite3_backup_init()"ZT_EOL_S);
+ continue;
+ }
+
+ int rc = SQLITE_OK;
+ for(;;) {
+ if (!_backupThreadRun) {
+ sqlite3_backup_finish(bak);
+ sqlite3_close(bakdb);
+ OSUtils::rm(backupPath);
+ return;
+ }
+ _lock.lock();
+ rc = sqlite3_backup_step(bak,64);
+ _lock.unlock();
+ if ((rc == SQLITE_OK)||(rc == SQLITE_LOCKED)||(rc == SQLITE_BUSY))
+ Thread::sleep(50);
+ else break;
+ }
+
+ sqlite3_backup_finish(bak);
+ sqlite3_close(bakdb);
+
+ OSUtils::rm(backupPath2);
+ ::rename(backupPath,backupPath2);
+ }
+ Thread::sleep(250);
+ }
+}
+
unsigned int SqliteNetworkController::_doCPGet(
const std::vector<std::string> &path,
const std::map<std::string,std::string> &urlArgs,
diff --git a/controller/SqliteNetworkController.hpp b/controller/SqliteNetworkController.hpp
index a3d5dfc7..0e2bb63e 100644
--- a/controller/SqliteNetworkController.hpp
+++ b/controller/SqliteNetworkController.hpp
@@ -39,6 +39,7 @@
#include "../node/Constants.hpp"
#include "../node/NetworkController.hpp"
#include "../node/Mutex.hpp"
+#include "../osdep/Thread.hpp"
// Number of in-memory last log entries to maintain per user
#define ZT_SQLITENETWORKCONTROLLER_IN_MEMORY_LOG_SIZE 32
@@ -86,6 +87,10 @@ public:
std::string &responseBody,
std::string &responseContentType);
+ // threadMain() for backup thread -- do not call directly
+ void threadMain()
+ throw();
+
private:
enum IpAssignmentType {
// IP assignment is a static IP address
@@ -112,6 +117,8 @@ private:
static void _circuitTestCallback(ZT_Node *node,ZT_CircuitTest *test,const ZT_CircuitTestReport *report);
Node *_node;
+ Thread _backupThread;
+ volatile bool _backupThreadRun;
std::string _dbPath;
std::string _circuitTestPath;
std::string _instanceId;
diff --git a/make-linux.mk b/make-linux.mk
index 5e0a2072..eddef7fc 100644
--- a/make-linux.mk
+++ b/make-linux.mk
@@ -88,6 +88,9 @@ else
LDFLAGS=-pie -Wl,-z,relro,-z,now
STRIP=strip --strip-all
endif
+ifeq ($(ZT_TRACE),1)
+ DEFS+=-DZT_TRACE
+endif
# Uncomment for gprof profile build
#CFLAGS=-Wall -g -pg -pthread $(INCLUDES) $(DEFS)
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index c4d5cfda..74efc943 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -411,7 +411,7 @@ struct InetAddress : public sockaddr_storage
// TODO: Ethernet address (but accept for forward compatibility)
return 7;
case 0x02:
- // TODO: Bluetooth address (but accept for forward compatibility)
+ // TODO: Bluetooth address (but accept for forward compatibility)
return 7;
case 0x03:
// TODO: Other address types (but accept for forward compatibility)
diff --git a/node/Peer.cpp b/node/Peer.cpp
index 9d0d78e5..0b981c8e 100644
--- a/node/Peer.cpp
+++ b/node/Peer.cpp
@@ -83,10 +83,10 @@ void Peer::received(
Packet::Verb inReVerb)
{
#ifdef ZT_ENABLE_CLUSTER
- InetAddress redirectTo;
if ((RR->cluster)&&(hops == 0)) {
// Note: findBetterEndpoint() is first since we still want to check
// for a better endpoint even if we don't actually send a redirect.
+ InetAddress redirectTo;
if ( (RR->cluster->findBetterEndpoint(redirectTo,_id.address(),remoteAddr,false)) && (verb != Packet::VERB_OK)&&(verb != Packet::VERB_ERROR)&&(verb != Packet::VERB_RENDEZVOUS)&&(verb != Packet::VERB_PUSH_DIRECT_PATHS) ) {
if (_vProto >= 5) {
// For newer peers we can send a more idiomatic verb: PUSH_DIRECT_PATHS.
@@ -141,13 +141,6 @@ void Peer::received(
else if (verb == Packet::VERB_MULTICAST_FRAME)
_lastMulticastFrame = now;
-#ifdef ZT_ENABLE_CLUSTER
- // If we think this peer belongs elsewhere, don't learn this path or
- // do other connection init stuff.
- if (redirectTo)
- return;
-#endif
-
if ((now - _lastAnnouncedTo) >= ((ZT_MULTICAST_LIKE_EXPIRE / 2) - 1000)) {
_lastAnnouncedTo = now;
needMulticastGroupAnnounce = true;
diff --git a/node/Switch.cpp b/node/Switch.cpp
index b7a9c522..97befbc6 100644
--- a/node/Switch.cpp
+++ b/node/Switch.cpp
@@ -154,25 +154,84 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
MulticastGroup mg(to,0);
if (to.isBroadcast()) {
- if (
- (etherType == ZT_ETHERTYPE_ARP)&&
- (len >= 28)&&
- (
- (((const unsigned char *)data)[2] == 0x08)&&
- (((const unsigned char *)data)[3] == 0x00)&&
- (((const unsigned char *)data)[4] == 6)&&
- (((const unsigned char *)data)[5] == 4)&&
- (((const unsigned char *)data)[7] == 0x01)
- )
- ) {
- // Cram IPv4 IP into ADI field to make IPv4 ARP broadcast channel specific and scalable
- // Also: enableBroadcast() does not apply to ARP since it's required for IPv4
+ if ( (etherType == ZT_ETHERTYPE_ARP) && (len >= 28) && ((((const uint8_t *)data)[2] == 0x08)&&(((const uint8_t *)data)[3] == 0x00)&&(((const uint8_t *)data)[4] == 6)&&(((const uint8_t *)data)[5] == 4)&&(((const uint8_t *)data)[7] == 0x01)) ) {
+ /* IPv4 ARP is one of the few special cases that we impose upon what is
+ * otherwise a straightforward Ethernet switch emulation. Vanilla ARP
+ * is dumb old broadcast and simply doesn't scale. ZeroTier multicast
+ * groups have an additional field called ADI (additional distinguishing
+ * information) which was added specifically for ARP though it could
+ * be used for other things too. We then take ARP broadcasts and turn
+ * them into multicasts by stuffing the IP address being queried into
+ * the 32-bit ADI field. In practice this uses our multicast pub/sub
+ * system to implement a kind of extended/distributed ARP table. */
mg = MulticastGroup::deriveMulticastGroupForAddressResolution(InetAddress(((const unsigned char *)data) + 24,4,0));
} else if (!nconf->enableBroadcast()) {
// Don't transmit broadcasts if this network doesn't want them
TRACE("%.16llx: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled",network->id());
return;
}
+ } else if ((etherType == ZT_ETHERTYPE_IPV6)&&(len >= (40 + 8 + 16))) {
+ /* IPv6 NDP emulation on ZeroTier-RFC4193 addressed networks! This allows
+ * for multicast-free operation in IPv6 networks, which both improves
+ * performance and is friendlier to mobile and (especially) IoT devices.
+ * In the future there may be a no-multicast build option for embedded
+ * and IoT use and this will be the preferred addressing mode. Note that
+ * it plays nice with our L2 emulation philosophy and even with bridging.
+ * While "real" devices behind the bridge can't have ZT-RFC4193 addresses
+ * themselves, they can look these addresses up with NDP and it will
+ * work just fine. */
+ if ((reinterpret_cast<const uint8_t *>(data)[6] == 0x3a)&&(reinterpret_cast<const uint8_t *>(data)[40] == 0x87)) { // ICMPv6 neighbor solicitation
+ for(std::vector<InetAddress>::const_iterator sip(nconf->staticIps().begin()),sipend(nconf->staticIps().end());sip!=sipend;++sip) {
+ if ((sip->ss_family == AF_INET6)&&(Utils::ntoh((uint16_t)reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_port) == 88)) {
+ const uint8_t *my6 = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&(*sip))->sin6_addr.s6_addr);
+ if ((my6[0] == 0xfd)&&(my6[9] == 0x99)&&(my6[10] == 0x93)) { // ZT-RFC4193 == fd__:____:____:____:__99:93__:____:____ / 88
+ const uint8_t *pkt6 = reinterpret_cast<const uint8_t *>(data) + 40 + 8;
+ unsigned int ptr = 0;
+ while (ptr != 11) {
+ if (pkt6[ptr] != my6[ptr])
+ break;
+ ++ptr;
+ }
+ if (ptr == 11) { // /88 matches an assigned address on this network
+ const Address atPeer(pkt6 + ptr,5);
+ if (atPeer != RR->identity.address()) {
+ const MAC atPeerMac(atPeer,network->id());
+ TRACE("ZT-RFC4193 NDP emulation: %.16llx: forging response for %s/%s",network->id(),atPeer.toString().c_str(),atPeerMac.toString().c_str());
+
+ uint8_t adv[72];
+ adv[0] = 0x60; adv[1] = 0x00; adv[2] = 0x00; adv[3] = 0x00;
+ adv[4] = 0x00; adv[5] = 0x20;
+ adv[6] = 0x3a; adv[7] = 0xff;
+ for(int i=0;i<16;++i) adv[8 + i] = pkt6[i];
+ for(int i=0;i<16;++i) adv[24 + i] = my6[i];
+ adv[40] = 0x88; adv[41] = 0x00;
+ adv[42] = 0x00; adv[43] = 0x00; // future home of checksum
+ adv[44] = 0x60; adv[45] = 0x00; adv[46] = 0x00; adv[47] = 0x00;
+ for(int i=0;i<16;++i) adv[48 + i] = pkt6[i];
+ adv[64] = 0x02; adv[65] = 0x01;
+ adv[66] = atPeerMac[0]; adv[67] = atPeerMac[1]; adv[68] = atPeerMac[2]; adv[69] = atPeerMac[3]; adv[70] = atPeerMac[4]; adv[71] = atPeerMac[5];
+
+ uint16_t pseudo_[36];
+ uint8_t *const pseudo = reinterpret_cast<uint8_t *>(pseudo_);
+ for(int i=0;i<32;++i) pseudo[i] = adv[8 + i];
+ pseudo[32] = 0x00; pseudo[33] = 0x00; pseudo[34] = 0x00; pseudo[35] = 0x20;
+ pseudo[36] = 0x00; pseudo[37] = 0x00; pseudo[38] = 0x00; pseudo[39] = 0x3a;
+ for(int i=0;i<32;++i) pseudo[40 + i] = adv[40 + i];
+ uint32_t checksum = 0;
+ for(int i=0;i<36;++i) checksum += Utils::hton(pseudo_[i]);
+ while ((checksum >> 16)) checksum = (checksum & 0xffff) + (checksum >> 16);
+ checksum = ~checksum;
+ adv[42] = (checksum >> 8) & 0xff;
+ adv[43] = checksum & 0xff;
+
+ RR->node->putFrame(network->id(),atPeerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
+ return; // stop processing: we have handled this frame with a spoofed local reply so no need to send it anywhere
+ }
+ }
+ }
+ }
+ }
+ }
}
/* Learn multicast groups for bridged-in hosts.
diff --git a/osdep/OSUtils.hpp b/osdep/OSUtils.hpp
index 5de35eba..43fd2813 100644
--- a/osdep/OSUtils.hpp
+++ b/osdep/OSUtils.hpp
@@ -95,7 +95,6 @@ public:
static inline bool rm(const std::string &path) throw() { return rm(path.c_str()); }
static inline bool mkdir(const char *path)
- throw()
{
#ifdef __WINDOWS__
if (::PathIsDirectoryA(path))
diff --git a/selftest.cpp b/selftest.cpp
index 0787925f..fa8df48b 100644
--- a/selftest.cpp
+++ b/selftest.cpp
@@ -41,6 +41,7 @@
#include "node/InetAddress.hpp"
#include "node/Utils.hpp"
#include "node/Identity.hpp"
+#include "node/Buffer.hpp"
#include "node/Packet.hpp"
#include "node/Salsa20.hpp"
#include "node/MAC.hpp"
diff --git a/tests/http/agent.js b/tests/http/agent.js
index bc7c475e..e90ee482 100644
--- a/tests/http/agent.js
+++ b/tests/http/agent.js
@@ -4,20 +4,20 @@
// Customizable parameters:
// Maximum interval between test attempts
-var TEST_INTERVAL_MAX = 60000;
+var TEST_INTERVAL_MAX = (60000 * 5);
// Test timeout in ms
-var TEST_TIMEOUT = 30000;
+var TEST_TIMEOUT = 60000;
// Where should I contact to register and query a list of other test agents?
-var SERVER_HOST = '104.238.141.145';
+var SERVER_HOST = '174.136.102.178';
var SERVER_PORT = 18080;
// Which port should agents use for their HTTP?
var AGENT_PORT = 18888;
// Payload size in bytes
-var PAYLOAD_SIZE = 10000;
+var PAYLOAD_SIZE = 5000;
// ---------------------------------------------------------------------------
diff --git a/tests/http/big-test-hosts b/tests/http/big-test-hosts
deleted file mode 100644
index 93b6f23f..00000000
--- a/tests/http/big-test-hosts
+++ /dev/null
@@ -1,2 +0,0 @@
-root@104.156.246.48
-root@104.156.252.136
diff --git a/tests/http/big-test-kill.sh b/tests/http/big-test-kill.sh
index 59f36788..29dbd638 100755
--- a/tests/http/big-test-kill.sh
+++ b/tests/http/big-test-kill.sh
@@ -1,18 +1,9 @@
#!/bin/bash
-# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits
-NUM_CONTAINERS=100
-CONTAINER_IMAGE=zerotier/http-test
-
-#
-# This script is designed to be run on Docker hosts to run NUM_CONTAINERS
-#
-# It can then be run on each Docker host via pssh or similar to run very
-# large scale tests.
-#
+# Kills all running Docker containers on all big-test-hosts
export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-pssh -h big-test-hosts -i -t 0 -p 256 "docker ps -aq | xargs -r docker rm -f"
+pssh -h big-test-hosts -i -t 0 -p 256 "sudo docker ps -aq | xargs -r sudo docker rm -f"
exit 0
diff --git a/tests/http/big-test-ready.sh b/tests/http/big-test-ready.sh
deleted file mode 100755
index aa540bba..00000000
--- a/tests/http/big-test-ready.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/bin/bash
-
-# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits
-NUM_CONTAINERS=100
-CONTAINER_IMAGE=zerotier/http-test
-
-#
-# This script is designed to be run on Docker hosts to run NUM_CONTAINERS
-#
-# It can then be run on each Docker host via pssh or similar to run very
-# large scale tests.
-#
-
-export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-
-# Kill and clean up old test containers if any -- note that this kills all containers on the system!
-#docker ps -q | xargs -n 1 docker kill
-#docker ps -aq | xargs -n 1 docker rm
-
-# Pull latest if needed -- change this to your image name and/or where to pull it from
-#docker pull $CONTAINER_IMAGE
-
-# Run NUM_CONTAINERS
-#for ((n=0;n<$NUM_CONTAINERS;n++)); do
-# docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE
-#done
-
-pssh -h big-test-hosts -i -t 0 -p 256 "docker pull $CONTAINER_IMAGE"
-
-exit 0
diff --git a/tests/http/big-test-start.sh b/tests/http/big-test-start.sh
index f300ac61..3ef4a316 100755
--- a/tests/http/big-test-start.sh
+++ b/tests/http/big-test-start.sh
@@ -1,30 +1,12 @@
#!/bin/bash
-# Edit as needed -- note that >1000 per host is likely problematic due to Linux kernel limits
-NUM_CONTAINERS=50
+# More than 500 container seems to result in a lot of sporadic failures, probably due to Linux kernel scaling issues with virtual network ports
+# 250 with a 16GB RAM VM like Amazon m4.xlarge seems good
+NUM_CONTAINERS=250
CONTAINER_IMAGE=zerotier/http-test
-#
-# This script is designed to be run on Docker hosts to run NUM_CONTAINERS
-#
-# It can then be run on each Docker host via pssh or similar to run very
-# large scale tests.
-#
-
export PATH=/bin:/usr/bin:/usr/local/bin:/usr/sbin:/sbin
-# Kill and clean up old test containers if any -- note that this kills all containers on the system!
-#docker ps -q | xargs -n 1 docker kill
-#docker ps -aq | xargs -n 1 docker rm
-
-# Pull latest if needed -- change this to your image name and/or where to pull it from
-#docker pull $CONTAINER_IMAGE
-
-# Run NUM_CONTAINERS
-#for ((n=0;n<$NUM_CONTAINERS;n++)); do
-# docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE
-#done
-
-pssh -h big-test-hosts -o big-test-out -t 0 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.25; done"
+pssh -h big-test-hosts -o big-test-out -t 0 -p 256 "for ((n=0;n<$NUM_CONTAINERS;n++)); do sudo docker run --device=/dev/net/tun --privileged -d $CONTAINER_IMAGE; sleep 0.1; done"
exit 0
diff --git a/tests/http/docker-main.sh b/tests/http/docker-main.sh
index f9e11de5..29cdced9 100755
--- a/tests/http/docker-main.sh
+++ b/tests/http/docker-main.sh
@@ -4,11 +4,13 @@ export PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
/zerotier-one -d >>zerotier-one.out 2>&1
+# Wait for ZeroTier to start and join the network
while [ ! -d "/proc/sys/net/ipv6/conf/zt0" ]; do
sleep 0.25
done
-sleep 2
+# Wait just a bit longer for stuff to settle
+sleep 5
exec node --harmony /agent.js >>agent.out 2>&1
#exec node --harmony /agent.js