diff options
| author | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-01-27 23:13:36 -0800 |
|---|---|---|
| committer | Adam Ierymenko <adam.ierymenko@zerotier.com> | 2014-01-27 23:13:36 -0800 |
| commit | f80ec871f6f4c63a28b9b8192477114b7aac696d (patch) | |
| tree | b2b02a40d9d9e0ee9f0dd0478fe192e966e274bc /node/Network.cpp | |
| parent | e0cb5caef21243cf045cecc25ed064869d1594d4 (diff) | |
| download | infinitytier-f80ec871f6f4c63a28b9b8192477114b7aac696d.tar.gz infinitytier-f80ec871f6f4c63a28b9b8192477114b7aac696d.zip | |
Make EthernetTap creation occur in a background thread in Network since it's a time consuming operation on Windows. This fixes one of the last remaining Windows problems.
Diffstat (limited to 'node/Network.cpp')
| -rw-r--r-- | node/Network.cpp | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/node/Network.cpp b/node/Network.cpp index 37d93b99..8ba597ba 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -46,6 +46,7 @@ const char *Network::statusString(const Status s) throw() { switch(s) { + case NETWORK_INITIALIZING: return "INITIALIZING"; case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF"; case NETWORK_OK: return "OK"; case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED"; @@ -56,6 +57,8 @@ const char *Network::statusString(const Status s) Network::~Network() { + Thread::join(_setupThread); + std::string devPersistentId(_tap->persistentId()); delete _tap; @@ -73,46 +76,50 @@ Network::~Network() SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id) { - // Tag to identify tap device -- used on some OSes like Windows - char tag[32]; - Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)id); - - // We construct Network via a static method to ensure that it is immediately - // wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet - // tap device, a SharedPtr<> wrap can occur in the Ethernet frame handler - // that then causes the Network instance to be deleted before it is finished - // being constructed. C++ edge cases, how I love thee. + /* We construct Network via a static method to ensure that it is immediately + * wrapped in a SharedPtr<>. Otherwise if there is traffic on the Ethernet + * tap device, a SharedPtr<> wrap can occur in the Ethernet frame handler + * that then causes the Network instance to be deleted before it is finished + * being constructed. C++ edge cases, how I love thee. */ + SharedPtr<Network> nw(new Network()); nw->_id = id; - nw->_ready = false; // disable handling of Ethernet frames during construct + nw->_mac = renv->identity.address().toMAC(); nw->_r = renv; - nw->_tap = new EthernetTap(renv,tag,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,nw.ptr()); + nw->_tap = (EthernetTap *)0; nw->_lastConfigUpdate = 0; - nw->_status = NETWORK_WAITING_FOR_FIRST_AUTOCONF; nw->_destroyOnDelete = false; + nw->_netconfFailure = NETCONF_FAILURE_NONE; + 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(); + + nw->_setupThread = Thread::start<Network>(nw.ptr()); return nw; } -void Network::setConfiguration(const Dictionary &conf,bool saveToDisk) +bool Network::setConfiguration(const Dictionary &conf,bool saveToDisk) { + Mutex::Lock _l(_lock); + + EthernetTap *t = _tap; + if (!t) { + TRACE("BUG: setConfiguration() called while tap is null!"); + return false; // can't accept config in initialization state + } + try { SharedPtr<NetworkConfig> newConfig(new NetworkConfig(conf)); if ((newConfig->networkId() == _id)&&(newConfig->issuedTo() == _r->identity.address())) { - Mutex::Lock _l(_lock); _config = newConfig; if (newConfig->staticIps().size()) - _tap->setIps(newConfig->staticIps()); - _tap->setDisplayName((std::string("ZeroTier One [") + newConfig->name() + "]").c_str()); + t->setIps(newConfig->staticIps()); + t->setDisplayName((std::string("ZeroTier One [") + newConfig->name() + "]").c_str()); _lastConfigUpdate = Utils::now(); - _status = NETWORK_OK; + _netconfFailure = NETCONF_FAILURE_NONE; if (saveToDisk) { std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf"); @@ -122,6 +129,8 @@ void Network::setConfiguration(const Dictionary &conf,bool saveToDisk) Utils::lockDownFile(confPath.c_str(),false); } } + + return true; } else { LOG("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id); } @@ -130,10 +139,15 @@ void Network::setConfiguration(const Dictionary &conf,bool saveToDisk) } catch ( ... ) { LOG("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id); } + + return false; } void Network::requestConfiguration() { + if (!_tap) + return; // don't bother requesting until we are initialized + if (controller() == _r->identity.address()) { // 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); @@ -190,6 +204,7 @@ bool Network::isAllowed(const Address &peer) const void Network::clean() { Mutex::Lock _l(_lock); + if ((_config)&&(_config->isOpen())) { // Open (public) networks do not track certs or cert pushes at all. _membershipCertificates.clear(); @@ -215,7 +230,7 @@ void Network::clean() void Network::_CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data) { - if (!((Network *)arg)->isUp()) + if (((Network *)arg)->status() != NETWORK_OK) return; const RuntimeEnvironment *_r = ((Network *)arg)->_r; @@ -250,6 +265,31 @@ void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t } } +void Network::threadMain() +{ + try { + // Setup thread -- this exits when tap is constructed. It's here + // because opening the tap can take some time on some platforms. + char tag[32]; + Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)_id); + _tap = new EthernetTap(_r,tag,_mac,ZT_IF_MTU,&_CBhandleTapData,this); + } catch (std::exception &exc) { + LOG("network %.16llx failed to initialize: %s",_id,exc.what()); + _netconfFailure = NETCONF_FAILURE_INIT_FAILED; + } catch ( ... ) { + LOG("network %.16llx failed to initialize: unknown error",_id); + _netconfFailure = NETCONF_FAILURE_INIT_FAILED; + } + + try { + _restoreState(); + requestConfiguration(); + } catch ( ... ) { + TRACE("BUG: exception in network setup thread in _restoreState() or requestConfiguration()!"); + _lastConfigUpdate = 0; // call requestConfiguration() again + } +} + void Network::_restoreState() { if (!_id) |
