From e73c4cb68b7461b8a2bdf7b6e5919bd0fcef2c1e Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Aug 2013 00:05:39 -0400 Subject: [PATCH] Whole bunch of stuff: netconf, bug fixes, tweaks to ping and firewall opener timing code. --- node/EthernetTap.cpp | 2 +- node/Multicaster.hpp | 2 +- node/Network.cpp | 81 ++++++++++++++++++++++++++++++++++++++++-- node/Network.hpp | 33 ++++++++++++----- node/Node.cpp | 8 +++-- node/NodeConfig.cpp | 36 +++++++++++++++++-- node/NodeConfig.hpp | 4 +-- node/PacketDecoder.cpp | 1 + node/Topology.hpp | 4 +-- 9 files changed, 149 insertions(+), 22 deletions(-) diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index 870baceba..b2682006b 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -114,7 +114,7 @@ EthernetTap::EthernetTap( _fd = ::open("/dev/net/tun",O_RDWR); if (_fd <= 0) - throw std::runtime_error("could not open TUN/TAP device"); + throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno)); struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index ac89f84b6..229b3c06f 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -68,7 +68,7 @@ class Multicaster { public: /** - * 256-bit simple bloom filter included with multicast frame packets + * Simple bit field bloom filter included with multicast frame packets */ typedef BloomFilter MulticastBloomFilter; diff --git a/node/Network.cpp b/node/Network.cpp index 6deb31fe1..f0770610d 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -25,6 +25,8 @@ * LLC. Start here: http://www.zerotier.com/ */ +#include +#include #include #include @@ -35,6 +37,7 @@ #include "Network.hpp" #include "Switch.hpp" #include "Packet.hpp" +#include "Utils.hpp" namespace ZeroTier { @@ -106,14 +109,44 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t id) _r(renv), _tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this), _id(id), - _lastConfigUpdate(0) + _lastConfigUpdate(0), + _destroyOnDelete(false) { if (controller() == _r->identity.address()) throw std::runtime_error("configuration error: cannot add a network for which I am the netconf master"); + + std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf"); + std::string confs; + if (Utils::readFile(confPath.c_str(),confs)) { + try { + if (confs.length()) { + Config conf(confs); + if (conf.containsAllFields()) + setConfiguration(Config(conf)); + } + } catch ( ... ) {} // ignore invalid config on disk, we will re-request + } else { + // If the conf file isn't present, "touch" it so we'll remember + // the existence of this network. + FILE *tmp = fopen(confPath.c_str(),"w"); + if (tmp) + fclose(tmp); + } + + requestConfiguration(); } Network::~Network() { + if (_destroyOnDelete) { + std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf"); + std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts"); + unlink(confPath.c_str()); + unlink(mcdbPath.c_str()); + } else { + // Causes flush of membership certs to disk + clean(); + } } void Network::setConfiguration(const Network::Config &conf) @@ -124,6 +157,11 @@ void Network::setConfiguration(const Network::Config &conf) _configuration = conf; _myCertificate = conf.certificateOfMembership(); _lastConfigUpdate = Utils::now(); + + std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf"); + if (!Utils::writeFile(confPath.c_str(),conf.toString())) { + LOG("error: unable to write network configuration file at: %s",confPath.c_str()); + } } } @@ -136,9 +174,17 @@ void Network::requestConfiguration() TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str()); Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST); outp.append((uint64_t)_id); + outp.append((uint16_t)0); // no meta-data _r->sw->send(outp,true); } +void Network::addMembershipCertificate(const Address &peer,const Certificate &cert) +{ + Mutex::Lock _l(_lock); + if (!_configuration.isOpen()) + _membershipCertificates[peer] = cert; +} + bool Network::isAllowed(const Address &peer) const { // Exceptions can occur if we do not yet have *our* configuration. @@ -164,10 +210,39 @@ void Network::clean() if (_configuration.isOpen()) _membershipCertificates.clear(); else { + std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts"); + FILE *mcdb = fopen(mcdbPath.c_str(),"wb"); + bool writeError = false; + if (!mcdb) { + LOG("error: unable to open membership cert database at: %s",mcdbPath.c_str()); + } else { + if ((writeError)||(fwrite("MCDB0",5,1,mcdb) != 1)) // version + writeError = true; + } + for(std::map::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) { - if (_myCertificate.qualifyMembership(i->second)) + if (_myCertificate.qualifyMembership(i->second)) { + if ((!writeError)&&(mcdb)) { + char tmp[ZT_ADDRESS_LENGTH]; + i->first.copyTo(tmp,ZT_ADDRESS_LENGTH); + if ((writeError)||(fwrite(tmp,ZT_ADDRESS_LENGTH,1,mcdb) != 1)) + writeError = true; + std::string c(i->second.toString()); + uint32_t cl = Utils::hton((uint32_t)c.length()); + if ((writeError)||(fwrite(&cl,sizeof(cl),1,mcdb) != 1)) + writeError = true; + if ((writeError)||(fwrite(c.data(),c.length(),1,mcdb) != 1)) + writeError = true; + } ++i; - else _membershipCertificates.erase(i++); + } else _membershipCertificates.erase(i++); + } + + if (mcdb) + fclose(mcdb); + if (writeError) { + unlink(mcdbPath.c_str()); + LOG("error: unable to write to membership cert database at: %s",mcdbPath.c_str()); } } } diff --git a/node/Network.hpp b/node/Network.hpp index d3bb5ad7d..6b7b30558 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -208,6 +208,11 @@ public: { } + inline bool containsAllFields() const + { + return (contains("nwid")&&contains("peer")); + } + inline std::string toString() const { return Dictionary::toString(); @@ -241,7 +246,7 @@ public: */ inline bool isOpen() const { - return (get("isOpen") == "1"); + return (get("isOpen","0") == "1"); } /** @@ -267,6 +272,14 @@ private: ~Network(); + /** + * Causes all persistent disk presence to be erased on delete + */ + inline void destroyOnDelete() + { + _destroyOnDelete = true; + } + public: /** * @return Network ID @@ -350,16 +363,16 @@ public: * @param peer Peer that owns certificate * @param cert Certificate itself */ - inline void addMembershipCertificate(const Address &peer,const Certificate &cert) - { - Mutex::Lock _l(_lock); - _membershipCertificates[peer] = cert; - } + void addMembershipCertificate(const Address &peer,const Certificate &cert); + /** + * @param peer Peer address to check + * @return True if peer is allowed to communicate on this network + */ bool isAllowed(const Address &peer) const; /** - * Perform periodic database cleaning such as removing expired membership certificates + * Perform cleanup and possibly save state */ void clean(); @@ -377,16 +390,20 @@ private: const RuntimeEnvironment *_r; + // Tap and tap multicast memberships EthernetTap _tap; - std::set _multicastGroups; + + // Membership certificates supplied by peers std::map _membershipCertificates; + // Configuration from network master node Config _configuration; Certificate _myCertificate; uint64_t _id; volatile uint64_t _lastConfigUpdate; + volatile bool _destroyOnDelete; Mutex _lock; diff --git a/node/Node.cpp b/node/Node.cpp index da752bb27..03710dced 100644 --- a/node/Node.cpp +++ b/node/Node.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #endif #include "Condition.hpp" @@ -340,6 +341,9 @@ Node::ReasonForTermination Node::run() unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "status").c_str()); unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine").c_str()); + // Make sure networks.d exists + mkdir((_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str(),0700); + // Load or generate config authentication secret std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret"); std::string configAuthToken; @@ -504,7 +508,6 @@ Node::ReasonForTermination Node::run() _r->topology->eachPeer(Topology::CollectPeersWithDirectPath(needPing)); } else { _r->topology->eachPeer(Topology::CollectPeersThatNeedPing(needPing)); - _r->topology->eachPeer(Topology::CollectPeersThatNeedFirewallOpener(needFirewallOpener)); } for(std::vector< SharedPtr >::iterator p(needPing.begin());p!=needPing.end();++p) { @@ -517,6 +520,7 @@ Node::ReasonForTermination Node::run() } } + _r->topology->eachPeer(Topology::CollectPeersThatNeedFirewallOpener(needFirewallOpener)); for(std::vector< SharedPtr >::iterator p(needFirewallOpener.begin());p!=needFirewallOpener.end();++p) { try { (*p)->sendFirewallOpener(_r,now); @@ -537,7 +541,7 @@ Node::ReasonForTermination Node::run() if ((now - lastClean) >= ZT_DB_CLEAN_PERIOD) { lastClean = now; _r->topology->clean(); - _r->nc->cleanAllNetworks(); + _r->nc->clean(); } try { diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 846945f32..b6d58e409 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -72,7 +72,7 @@ void NodeConfig::whackAllTaps() n->second->tap().whack(); } -void NodeConfig::cleanAllNetworks() +void NodeConfig::clean() { Mutex::Lock _l(_networks_m); for(std::map< uint64_t,SharedPtr >::const_iterator n(_networks.begin());n!=_networks.end();++n) @@ -145,9 +145,39 @@ std::vector NodeConfig::execute(const char *command) tmp.c_str()); } } else if (cmd[0] == "join") { - _P("404 join Not implemented yet."); + if (cmd.size() > 1) { + uint64_t nwid = strtoull(cmd[1].c_str(),(char **)0,16); + if (nwid > 0) { + Mutex::Lock _l(_networks_m); + try { + SharedPtr nw(new Network(_r,nwid)); + _networks[nwid] = nw; + _P("200 join %.16llx OK",(unsigned long long)nwid); + } catch (std::exception &exc) { + _P("500 join %.16llx ERROR: %s",(unsigned long long)nwid,exc.what()); + } catch ( ... ) { + _P("500 join %.16llx ERROR: (unknown exception)",(unsigned long long)nwid); + } + } else { + _P("400 join requires a network ID (>0) in hexadecimal format"); + } + } else { + _P("400 join requires a network ID (>0) in hexadecimal format"); + } } else if (cmd[0] == "leave") { - _P("404 leave Not implemented yet."); + if (cmd.size() > 1) { + Mutex::Lock _l(_networks_m); + uint64_t nwid = strtoull(cmd[1].c_str(),(char **)0,16); + std::map< uint64_t,SharedPtr >::iterator nw(_networks.find(nwid)); + if (nw == _networks.end()) { + _P("404 leave %.16llx ERROR: not a member of that network",(unsigned long long)nwid); + } else { + nw->second->destroyOnDelete(); + _networks.erase(nw); + } + } else { + _P("400 leave requires a network ID (>0) in hexadecimal format"); + } } else { _P("404 %s No such command. Use 'help' for help.",cmd[0].c_str()); } diff --git a/node/NodeConfig.hpp b/node/NodeConfig.hpp index f89cf2cbb..b9858e4f9 100644 --- a/node/NodeConfig.hpp +++ b/node/NodeConfig.hpp @@ -95,9 +95,9 @@ public: void whackAllTaps(); /** - * Call clean() on all networks + * Perform cleanup and possibly update saved state */ - void cleanAllNetworks(); + void clean(); /** * @param nwid Network ID diff --git a/node/PacketDecoder.cpp b/node/PacketDecoder.cpp index 0262c2a87..062cdbc27 100644 --- a/node/PacketDecoder.cpp +++ b/node/PacketDecoder.cpp @@ -610,6 +610,7 @@ bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const char tmp[128]; try { uint64_t nwid = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID); + TRACE("NETWORK_CONFIG_REQUEST for %.16llx from %s",(unsigned long long)nwid,source().toString().c_str()); #ifndef __WINDOWS__ if (_r->netconfService) { unsigned int dictLen = at(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN); diff --git a/node/Topology.hpp b/node/Topology.hpp index b917738e4..dbccff801 100644 --- a/node/Topology.hpp +++ b/node/Topology.hpp @@ -200,7 +200,7 @@ public: inline void operator()(Topology &t,const SharedPtr &p) { - if ((p->hasDirectPath())&&((_now - p->lastFirewallOpener()) >= ZT_FIREWALL_OPENER_DELAY)) + if ((p->hasDirectPath())&&((_now - std::max(p->lastFirewallOpener(),p->lastDirectSend())) >= ZT_FIREWALL_OPENER_DELAY)) _v.push_back(p); } @@ -223,7 +223,7 @@ public: inline void operator()(Topology &t,const SharedPtr &p) { - if (((p->hasActiveDirectPath(_now))||(t.isSupernode(p->address())))&&((_now - p->lastDirectSend()) >= ZT_PEER_DIRECT_PING_DELAY)) + if ( ((t.isSupernode(p->address()))&&((_now - p->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)) || ((p->hasActiveDirectPath(_now))&&((_now - p->lastDirectSend()) >= ZT_PEER_DIRECT_PING_DELAY)) ) _v.push_back(p); }