diff options
| author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2013-10-16 17:47:26 -0400 |
|---|---|---|
| committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2013-10-16 17:47:26 -0400 |
| commit | 46f868bd4fb2fd7b0816ded98974935aacddf5e6 (patch) | |
| tree | 1e892172060447a9959977b466980c506572457a /node/Network.cpp | |
| parent | 58fa6cab4397fe7b0f4fe883e9d1632f5b73f6f9 (diff) | |
| download | infinitytier-46f868bd4fb2fd7b0816ded98974935aacddf5e6.tar.gz infinitytier-46f868bd4fb2fd7b0816ded98974935aacddf5e6.zip | |
Lots of cleanup, more work on certificates, some security fixes.
Diffstat (limited to 'node/Network.cpp')
| -rw-r--r-- | node/Network.cpp | 248 |
1 files changed, 164 insertions, 84 deletions
diff --git a/node/Network.cpp b/node/Network.cpp index 1973b643..68847749 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -39,6 +39,9 @@ #include "Switch.hpp" #include "Packet.hpp" #include "Utils.hpp" +#include "Buffer.hpp" + +#define ZT_NETWORK_CERT_WRITE_BUF_SIZE 524288 namespace ZeroTier { @@ -51,6 +54,7 @@ const char *Network::statusString(const Status s) case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF"; case NETWORK_OK: return "OK"; case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED"; + case NETWORK_NOT_FOUND: return "NOT_FOUND"; } return "(invalid)"; } @@ -58,15 +62,13 @@ const char *Network::statusString(const Status s) Network::~Network() { delete _tap; - if (_destroyOnDelete) { - std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); - std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts"); - Utils::rm(confPath); - Utils::rm(mcdbPath); + Utils::rm(std::string(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf")); + Utils::rm(std::string(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts")); } else { // Causes flush of membership certs to disk clean(); + _dumpMulticastCerts(); } } @@ -87,21 +89,24 @@ SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t nw->_r = renv; nw->_tap = new EthernetTap(renv,tag,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr()); nw->_isOpen = false; + nw->_emulateArp = false; + nw->_emulateNdp = false; nw->_multicastPrefixBits = ZT_DEFAULT_MULTICAST_PREFIX_BITS; nw->_multicastDepth = ZT_DEFAULT_MULTICAST_DEPTH; + nw->_status = NETWORK_WAITING_FOR_FIRST_AUTOCONF; memset(nw->_etWhitelist,0,sizeof(nw->_etWhitelist)); nw->_id = id; nw->_lastConfigUpdate = 0; nw->_destroyOnDelete = false; - if (nw->controller() == renv->identity.address()) // sanity check, this isn't supported for now - throw std::runtime_error("cannot add a network for which I am the netconf master"); + if (nw->controller() == renv->identity.address()) // netconf masters can't really join networks + throw std::runtime_error("cannot join a network for which I am the netconf master"); nw->_restoreState(); nw->_ready = true; // enable handling of Ethernet frames nw->requestConfiguration(); return nw; } -void Network::setConfiguration(const Network::Config &conf) +void Network::setConfiguration(const Network::Config &conf,bool saveToDisk) { Mutex::Lock _l(_lock); try { @@ -113,6 +118,8 @@ void Network::setConfiguration(const Network::Config &conf) _mcRates = conf.multicastRates(); _staticAddresses = conf.staticAddresses(); _isOpen = conf.isOpen(); + _emulateArp = conf.emulateArp(); + _emulateNdp = conf.emulateNdp(); _multicastPrefixBits = conf.multicastPrefixBits(); _multicastDepth = conf.multicastDepth(); @@ -121,16 +128,19 @@ void Network::setConfiguration(const Network::Config &conf) _tap->setIps(_staticAddresses); _tap->setDisplayName((std::string("ZeroTier One [") + conf.name() + "]").c_str()); - // Expand ethertype whitelist into fast-lookup bit field + // Expand ethertype whitelist into fast-lookup bit field (more memoization) memset(_etWhitelist,0,sizeof(_etWhitelist)); std::set<unsigned int> wl(conf.etherTypes()); for(std::set<unsigned int>::const_iterator t(wl.begin());t!=wl.end();++t) _etWhitelist[*t / 8] |= (unsigned char)(1 << (*t % 8)); - // Save most recent configuration to disk in networks.d - std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); - if (!Utils::writeFile(confPath.c_str(),conf.toString())) { - LOG("error: unable to write network configuration file at: %s",confPath.c_str()); + _status = NETWORK_OK; + + if (saveToDisk) { + std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); + if (!Utils::writeFile(confPath.c_str(),conf.toString())) { + LOG("error: unable to write network configuration file at: %s",confPath.c_str()); + } } } } catch ( ... ) { @@ -141,6 +151,9 @@ void Network::setConfiguration(const Network::Config &conf) _mcRates = MulticastRates(); _staticAddresses.clear(); _isOpen = false; + _emulateArp = false; + _emulateNdp = false; + _status = NETWORK_WAITING_FOR_FIRST_AUTOCONF; _lastConfigUpdate = 0; LOG("unexpected exception handling config for network %.16llx, retrying fetch...",(unsigned long long)_id); @@ -150,7 +163,7 @@ void Network::setConfiguration(const Network::Config &conf) void Network::requestConfiguration() { if (controller() == _r->identity.address()) { - // FIXME: Right now the netconf master cannot be a member of its own nets + // netconf master cannot be a member of its own nets LOG("unable to request network configuration for network %.16llx: I am the network master, cannot query self",(unsigned long long)_id); return; } @@ -162,11 +175,13 @@ void Network::requestConfiguration() _r->sw->send(outp,true); } -void Network::addMembershipCertificate(const Address &peer,const CertificateOfMembership &cert) +void Network::addMembershipCertificate(const CertificateOfMembership &cert) { Mutex::Lock _l(_lock); - if (!_isOpen) - _membershipCertificates[peer] = cert; + // We go ahead and accept certs provisionally even if _isOpen is true, since + // that might be changed in short order if the user is fiddling in the UI. + // These will be purged on clean() for open networks eventually. + _membershipCertificates[cert.issuedTo()] = cert; } bool Network::isAllowed(const Address &peer) const @@ -175,80 +190,55 @@ bool Network::isAllowed(const Address &peer) const try { Mutex::Lock _l(_lock); if (_isOpen) - return true; + return true; // network is public std::map<Address,CertificateOfMembership>::const_iterator pc(_membershipCertificates.find(peer)); if (pc == _membershipCertificates.end()) - return false; - return _myCertificate.agreesWith(pc->second); + return false; // no certificate on file + return _myCertificate.agreesWith(pc->second); // is other cert valid against ours? } catch (std::exception &exc) { TRACE("isAllowed() check failed for peer %s: unexpected exception: %s",peer.toString().c_str(),exc.what()); } catch ( ... ) { TRACE("isAllowed() check failed for peer %s: unexpected exception: unknown exception",peer.toString().c_str()); } - return false; + return false; // default position on any failure } void Network::clean() { - std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts"); - Mutex::Lock _l(_lock); - - if ((!_id)||(_isOpen)) { + uint64_t timestampMaxDelta = _myCertificate.timestampMaxDelta(); + if (_isOpen) { + // Open (public) networks do not track certs or cert pushes at all. _membershipCertificates.clear(); - Utils::rm(mcdbPath); - } else { - 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; + _lastPushedMembershipCertificate.clear(); + } else if (timestampMaxDelta) { + // Clean certificates that are no longer valid from the cache. + for(std::map<Address,CertificateOfMembership>::iterator c=(_membershipCertificates.begin());c!=_membershipCertificates.end();) { + if (_myCertificate.agreesWith(c->second)) + ++c; + else _membershipCertificates.erase(c++); } - for(std::map<Address,CertificateOfMembership>::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) { - if (_myCertificate.agreesWith(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++); - } - - if (mcdb) - fclose(mcdb); - if (writeError) { - Utils::rm(mcdbPath); - LOG("error: unable to write to membership cert database at: %s",mcdbPath.c_str()); + // Clean entries from the last pushed tracking map if they're so old as + // to be no longer relevant. + uint64_t forgetIfBefore = Utils::now() - (timestampMaxDelta * 3); + for(std::map<Address,uint64_t>::iterator lp(_lastPushedMembershipCertificate.begin());lp!=_lastPushedMembershipCertificate.end();) { + if (lp->second < forgetIfBefore) + _lastPushedMembershipCertificate.erase(lp++); + else ++lp; } } } -Network::Status Network::status() const -{ - Mutex::Lock _l(_lock); - if (_configuration) - return NETWORK_OK; - return NETWORK_WAITING_FOR_FIRST_AUTOCONF; -} - void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data) { - if (!((Network *)arg)->_ready) + if (!((Network *)arg)->isUp()) return; + const RuntimeEnvironment *_r = ((Network *)arg)->_r; if (_r->shutdownInProgress) return; + try { _r->sw->onLocalEthernet(SharedPtr<Network>((Network *)arg),from,to,etherType,data); } catch (std::exception &exc) { @@ -261,17 +251,14 @@ void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t now) { uint64_t timestampMaxDelta = _myCertificate.timestampMaxDelta(); - if (!timestampMaxDelta) { - LOG("unable to push my certificate to %s for network %.16llx: certificate invalid, missing required timestamp field",peer.toString().c_str(),_id); - return; // required field missing! - } + if (!timestampMaxDelta) + return; // still waiting on my own cert uint64_t &lastPushed = _lastPushedMembershipCertificate[peer]; if ((force)||((now - lastPushed) > (timestampMaxDelta / 2))) { lastPushed = now; Packet outp(peer,_r->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE); - outp.append((uint64_t)_id); _myCertificate.serialize(outp); _r->sw->send(outp,true); } @@ -279,21 +266,114 @@ void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t void Network::_restoreState() { - std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); - std::string confs; - if (Utils::readFile(confPath.c_str(),confs)) { + if (!_id) + return; // sanity check + + Buffer<ZT_NETWORK_CERT_WRITE_BUF_SIZE> buf; + + std::string idstr(idString()); + std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idstr + ".conf"); + std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idstr + ".mcerts"); + + // Read configuration file containing last config from netconf master + { + std::string confs; + if (Utils::readFile(confPath.c_str(),confs)) { + try { + if (confs.length()) + setConfiguration(Config(confs),false); + } catch ( ... ) {} // ignore invalid config on disk, we will re-request from netconf master + } 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(),"wb"); + if (tmp) + fclose(tmp); + } + } + + // Read most recent multicast cert dump + if ((!_isOpen)&&(Utils::fileExists(mcdbPath.c_str()))) { + CertificateOfMembership com; + Mutex::Lock _l(_lock); + + _membershipCertificates.clear(); + try { - if (confs.length()) - setConfiguration(Config(confs)); - } 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); + FILE *mcdb = fopen(mcdbPath.c_str(),"rb"); + if (mcdb) { + for(;;) { + long rlen = (long)fread(buf.data() + buf.size(),1,ZT_NETWORK_CERT_WRITE_BUF_SIZE - buf.size(),mcdb); + if (rlen <= 0) + break; + buf.setSize(buf.size() + (unsigned int)rlen); + unsigned int ptr = 0; + while ((ptr < (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2))&&(ptr < buf.size())) { + ptr += com.deserialize(buf,ptr); + if (com.issuedTo()) + _membershipCertificates[com.issuedTo()] = com; + } + if (ptr) { + memmove(buf.data(),buf.data() + ptr,buf.size() - ptr); + buf.setSize(buf.size() - ptr); + } + } + } + } catch ( ... ) { + // Membership cert dump file invalid. We'll re-learn them off the net. + _membershipCertificates.clear(); + Utils::rm(mcdbPath); + } } - // TODO: restore membership certs +} + +void Network::_dumpMulticastCerts() +{ + Buffer<ZT_NETWORK_CERT_WRITE_BUF_SIZE> buf; + std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".mcerts"); + Mutex::Lock _l(_lock); + + if ((!_id)||(_isOpen)) { + Utils::rm(mcdbPath); + return; + } + + FILE *mcdb = fopen(mcdbPath.c_str(),"wb"); + if (!mcdb) + return; + if (fwrite("ZTMCD0",6,1,mcdb) != 1) { + Utils::rm(mcdbPath); + return; + } + + for(std::map<Address,CertificateOfMembership>::iterator c=(_membershipCertificates.begin());c!=_membershipCertificates.end();++c) { + try { + c->second.serialize(buf); + if (buf.size() >= (ZT_NETWORK_CERT_WRITE_BUF_SIZE / 2)) { + if (fwrite(buf.data(),buf.size(),1,mcdb) != 1) { + fclose(mcdb); + Utils::rm(mcdbPath); + return; + } + buf.clear(); + } + } catch ( ... ) { + // Sanity check... no cert will ever be big enough to overflow buf + fclose(mcdb); + Utils::rm(mcdbPath); + return; + } + } + + if (buf.size()) { + if (fwrite(buf.data(),buf.size(),1,mcdb) != 1) { + fclose(mcdb); + Utils::rm(mcdbPath); + return; + } + } + + fclose(mcdb); } } // namespace ZeroTier |
