From 0b84c10ccc8db26151b25f38869bb462656495d2 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Jan 2015 16:35:20 -0500 Subject: [PATCH 1/3] Add confirmation step to new netconf, with the caveat that it will be disabled for older netconf servers to avoid race. Also add some comments. --- node/IncomingPacket.cpp | 39 ++++++++++++++++++++++++++++++------ node/Network.cpp | 2 +- node/NetworkConfigMaster.cpp | 15 +++++++++++++- node/Peer.hpp | 26 ++++++++++++++++++++++++ version.h | 2 +- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index f5379458c..8e6de0e9d 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -123,6 +123,14 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr } break; + case Packet::ERROR_UNSUPPORTED_OPERATION: + if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) { + SharedPtr network(RR->nc->network(at(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD))); + if ((network)&&(network->controller() == source())) + network->setNotFound(); + } + break; + case Packet::ERROR_IDENTITY_COLLISION: // TODO: if it comes from a supernode, regenerate a new identity // if (RR->topology->isSupernode(source())) {} @@ -286,9 +294,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision); // If a supernode has a version higher than ours, this causes a software - // update check to run now. This might bum-rush download.zerotier.com, but - // it's hosted on S3 so hopefully it can take it. This should cause updates - // to propagate out very quickly. + // update check to run now. if ((RR->updater)&&(RR->topology->isSupernode(peer->address()))) RR->updater->sawRemoteVersion(vMajor,vMinor,vRevision); } break; @@ -307,12 +313,33 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr &p case Packet::VERB_NETWORK_CONFIG_REQUEST: { SharedPtr nw(RR->nc->network(at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID))); if ((nw)&&(nw->controller() == source())) { - // OK(NETWORK_CONFIG_REQUEST) is only accepted from a network's - // controller. unsigned int dictlen = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN); std::string dict((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,dictlen),dictlen); if (dict.length()) { - nw->setConfiguration(Dictionary(dict)); + if (nw->setConfiguration(Dictionary(dict)) == 2) { // 2 == accepted and actually new + /* If this configuration was indeed new, we do another + * netconf request with its timestamp. We do this in + * order to (a) tell the netconf server we got it (it + * won't send a duplicate if ts == current), and (b) + * get another one if the netconf is changing rapidly + * until we finally have the final version. + * + * Note that we don't do this for netconf masters with + * versions <= 1.0.3, since those regenerate a new netconf + * with a new timestamp every time. In that case this double + * confirmation would create a race condition. */ + if (peer->atLeastVersion(1,0,3)) { + SharedPtr nc(nw->config2()); + if ((nc)&&(nc->timestamp() > 0)) { // sanity check + Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); + outp.append((uint64_t)nw->id()); + outp.append((uint16_t)0); // no meta-data + outp.append((uint64_t)nc->timestamp()); + outp.armor(peer->key(),true); + _fromSock->send(_remoteAddress,outp.data(),outp.size()); + } + } + } TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str()); } } diff --git a/node/Network.cpp b/node/Network.cpp index df741026c..aac8d5d61 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -274,7 +274,7 @@ int Network::setConfiguration(const Dictionary &conf,bool saveToDisk) { Mutex::Lock _l(_lock); if ((_config)&&(*_config == *newConfig)) - return 1; // OK but duplicate + return 1; // OK config, but duplicate of what we already have } if (applyConfiguration(newConfig)) { if (saveToDisk) { diff --git a/node/NetworkConfigMaster.cpp b/node/NetworkConfigMaster.cpp index cbf7252cd..3be2ad8cc 100644 --- a/node/NetworkConfigMaster.cpp +++ b/node/NetworkConfigMaster.cpp @@ -94,8 +94,9 @@ void NetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uin Utils::snprintf(nwKey,sizeof(nwKey),"zt1:network:%s:~",nwids); Utils::snprintf(revKey,sizeof(revKey),"zt1:network:%s:revision",nwids); - TRACE("netconf: request from %s for %s (if newer than %llu)",addrs,nwids,(unsigned long long)haveTimestamp); + TRACE("netconf: %s : %s if > %llu",nwids,addrs,(unsigned long long)haveTimestamp); + // Check to make sure network itself exists and is valid if (!_hget(nwKey,"id",tmps2)) { LOG("netconf: Redis error retrieving %s/id",nwKey); return; @@ -111,6 +112,7 @@ void NetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uin return; } + // Get network revision if (!_get(revKey,revision)) { LOG("netconf: Redis error retrieving %s",revKey); return; @@ -118,20 +120,26 @@ void NetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uin if (!revision.length()) revision = "0"; + // Get network member record for this peer if (!_hgetall(memberKey,memberRecord)) { LOG("netconf: Redis error retrieving %s",memberKey); return; } + // If there is no member record, init a new one -- for public networks this + // auto-authorizes, and for private nets it makes the peer show up in the UI + // so the admin can authorize or delete/hide it. if ((memberRecord.size() == 0)||(memberRecord.get("id","") != addrs)||(memberRecord.get("nwid","") != nwids)) { if (!_initNewMember(nwid,member,metaData,memberRecord)) return; } if (memberRecord.getBoolean("authorized")) { + // Get current netconf and netconf timestamp uint64_t ts = memberRecord.getHexUInt("netconfTimestamp",0); std::string netconf(memberRecord.get("netconf","")); + // Update statistics for this node Dictionary upd; upd.setHex("netconfClientTimestamp",haveTimestamp); if (fromAddr) @@ -139,11 +147,16 @@ void NetworkConfigMaster::doNetworkConfigRequest(const InetAddress &fromAddr,uin upd.setHex("lastSeen",Utils::now()); _hmset(memberKey,upd); + // Attempt to generate netconf for this node if there isn't + // one or it's not in step with the network's revision. if (((ts == 0)||(netconf.length() == 0))||(memberRecord.get("netconfRevision","") != revision)) { if (!_generateNetconf(nwid,member,metaData,netconf,ts)) return; } + // If the netconf we have (or just generated) is newer than what + // the client reports that it has, send it. Otherwise we just + // ignore the message since the client is up to date. if (ts > haveTimestamp) { TRACE("netconf: sending %u bytes of netconf data to %s",netconf.length(),addrs); Packet outp(member,RR->identity.address(),Packet::VERB_OK); diff --git a/node/Peer.hpp b/node/Peer.hpp index 142002a8f..09bf0e043 100644 --- a/node/Peer.hpp +++ b/node/Peer.hpp @@ -341,6 +341,32 @@ public: inline unsigned int remoteVersionMinor() const throw() { return _vMinor; } inline unsigned int remoteVersionRevision() const throw() { return _vRevision; } + /** + * Check whether this peer's version is both known and is at least what is specified + * + * @param major Major version to check against + * @param minor Minor version + * @param rev Revision + * @return True if peer's version is at least supplied tuple + */ + inline bool atLeastVersion(unsigned int major,unsigned int minor,unsigned int rev) + throw() + { + if ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)) { + if (_vMajor > major) + return true; + else if (_vMajor == major) { + if (_vMinor > minor) + return true; + else if (_vMinor == minor) { + if (_vRevision >= rev) + return true; + } + } + } + return false; + } + inline bool remoteVersionKnown() const throw() { return ((_vMajor > 0)||(_vMinor > 0)||(_vRevision > 0)); } /** diff --git a/version.h b/version.h index 60e058dfd..fabfa3843 100644 --- a/version.h +++ b/version.h @@ -41,6 +41,6 @@ /** * Revision */ -#define ZEROTIER_ONE_VERSION_REVISION 2 +#define ZEROTIER_ONE_VERSION_REVISION 3 #endif From c8c5d3b06e241ca0817eeddb7b484bdc5927f828 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Jan 2015 18:25:41 -0500 Subject: [PATCH 2/3] Create netconf master if redis options are in local.conf. --- node/NetworkConfigMaster.hpp | 7 +++++++ node/Node.cpp | 15 +++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/node/NetworkConfigMaster.hpp b/node/NetworkConfigMaster.hpp index 25d5bb1ca..c6993c562 100644 --- a/node/NetworkConfigMaster.hpp +++ b/node/NetworkConfigMaster.hpp @@ -30,6 +30,13 @@ #include "Constants.hpp" +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_HOST "netconf.redisHost" +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_PORT "netconf.redisPort" +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_PORT_DEFAULT 6379 +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_AUTH "netconf.redisAuth" +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_DBNUM "netconf.redisDatabaseNumber" +#define ZT_LOCAL_CONFIG_NETCONF_REDIS_DBNUM_DEFAULT 0 + #ifdef ZT_ENABLE_NETCONF_MASTER #include diff --git a/node/Node.cpp b/node/Node.cpp index d65e4e393..b9f027e31 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -310,6 +310,21 @@ Node::ReasonForTermination Node::run() } RR->node = this; +#ifdef ZT_ENABLE_NETCONF_MASTER + { + std::string redisHost(RR->nc->getLocalConfig(ZT_LOCAL_CONFIG_NETCONF_REDIS_HOST)); + if (redisHost.length() > 0) { + unsigned int redisPort = Utils::strToUInt(RR->nc->getLocalConfig(ZT_LOCAL_CONFIG_NETCONF_REDIS_PORT).c_str()); + if ((redisPort == 0)||(redisPort > 0xffff)) + redisPort = ZT_LOCAL_CONFIG_NETCONF_REDIS_PORT_DEFAULT; + std::string redisAuth(RR->nc->getLocalConfig(ZT_LOCAL_CONFIG_NETCONF_REDIS_AUTH)); + std::string redisDatabaseNumberStr(RR->nc->getLocalConfig(ZT_LOCAL_CONFIG_NETCONF_REDIS_DBNUM)); + unsigned int redisDatabaseNumber = (redisDatabaseNumberStr.length() > 0) ? Utils::strToUInt(redisDatabaseNumberStr.c_str()) : (unsigned int)ZT_LOCAL_CONFIG_NETCONF_REDIS_DBNUM_DEFAULT; + RR->netconfMaster = new NetworkConfigMaster(RR,redisHost.c_str(),redisPort,redisAuth.c_str(),redisDatabaseNumber); + } + } +#endif + #ifdef ZT_AUTO_UPDATE if (ZT_DEFAULTS.updateLatestNfoURL.length()) { RR->updater = new SoftwareUpdater(RR); From eab3ca1401b083b3d91d30d2c6f571dcb5c8a2a6 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Fri, 9 Jan 2015 18:44:17 -0500 Subject: [PATCH 3/3] docs --- testnet/MTQ.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testnet/MTQ.hpp b/testnet/MTQ.hpp index 2a2fe1cda..c2f53e21b 100644 --- a/testnet/MTQ.hpp +++ b/testnet/MTQ.hpp @@ -1,6 +1,6 @@ /* * ZeroTier One - Global Peer to Peer Ethernet - * Copyright (C) 2012-2014 ZeroTier Networks LLC + * Copyright (C) 2011-2015 ZeroTier Networks LLC * * 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