diff options
-rw-r--r-- | node/EthernetTap.cpp | 47 | ||||
-rw-r--r-- | node/Network.cpp | 28 | ||||
-rw-r--r-- | node/Network.hpp | 7 | ||||
-rw-r--r-- | node/NodeConfig.cpp | 4 |
4 files changed, 65 insertions, 21 deletions
diff --git a/node/EthernetTap.cpp b/node/EthernetTap.cpp index 86e96f33..d2302e3f 100644 --- a/node/EthernetTap.cpp +++ b/node/EthernetTap.cpp @@ -234,6 +234,7 @@ EthernetTap::EthernetTap( _fd(0) { char procpath[128]; + struct stat sbuf; Mutex::Lock _l(__tapCreateLock); // create only one tap at a time, globally if (mtu > 4096) @@ -246,13 +247,19 @@ EthernetTap::EthernetTap( struct ifreq ifr; memset(&ifr,0,sizeof(ifr)); - { // pick an unused device name + // Try to recall our last device name, or pick an unused one if that fails. + bool recalledDevice = false; + if ((tag)&&(tag[0])) { + Utils::scopy(ifr.ifr_name,sizeof(ifr.ifr_name),tag); + Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.fr_name); + recalledDevice = (stat(procpath,&sbuf) != 0); + } + if (!recalledDevice) { int devno = 0; - struct stat sbuf; do { Utils::snprintf(ifr.ifr_name,sizeof(ifr.ifr_name),"zt%d",devno++); Utils::snprintf(procpath,sizeof(procpath),"/proc/sys/net/ipv4/conf/%s",ifr.ifr_name); - } while (stat(procpath,&sbuf) == 0); + } while (stat(procpath,&sbuf) == 0); // try zt#++ until we find one that does not exist } ifr.ifr_flags = IFF_TAP | IFF_NO_PI; @@ -359,17 +366,33 @@ EthernetTap::EthernetTap( if (stat("/dev/zt0",&stattmp)) throw std::runtime_error("/dev/zt# tap devices do not exist and unable to load kernel extension"); - // Open the first available device (ones in use will fail with resource busy) - for(int i=0;i<256;++i) { - Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); - if (stat(devpath,&stattmp)) - throw std::runtime_error("no more TAP devices available"); - _fd = ::open(devpath,O_RDWR); - if (_fd > 0) { - Utils::snprintf(_dev,sizeof(_dev),"zt%d",i); - break; + // Try to reopen the last device we had, if we had one and it's still unused. + bool recalledDevice = false; + if ((tag)&&(tag[0])) { + Utils::snprintf(devpath,sizeof(devpath),"/dev/%s",tag); + if (stat(devpath,&stattmp) == 0) { + _fd = ::open(devpath,O_RDWR); + if (_fd > 0) { + Utils::scopy(_dev,sizeof(_dev),tag); + recalledDevice = true; + } } } + + // Open the first unused tap device if we didn't recall a previous one. + if (!recalledDevice) { + for(int i=0;i<256;++i) { + Utils::snprintf(devpath,sizeof(devpath),"/dev/zt%d",i); + if (stat(devpath,&stattmp)) + throw std::runtime_error("no more TAP devices available"); + _fd = ::open(devpath,O_RDWR); + if (_fd > 0) { + Utils::snprintf(_dev,sizeof(_dev),"zt%d",i); + break; + } + } + } + if (_fd <= 0) throw std::runtime_error("unable to open TAP device or no more devices available"); diff --git a/node/Network.cpp b/node/Network.cpp index db252f73..19190207 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -75,7 +75,7 @@ Network::~Network() } } -SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id) +SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,NodeConfig *nc,uint64_t 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 @@ -85,6 +85,7 @@ SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t SharedPtr<Network> nw(new Network()); nw->_id = id; + nw->_nc = nc; nw->_mac = renv->identity.address().toMAC(); nw->_r = renv; nw->_tap = (EthernetTap *)0; @@ -269,12 +270,31 @@ void Network::_pushMembershipCertificate(const Address &peer,bool force,uint64_t void Network::threadMain() throw() { + // Setup thread -- this exits when tap is constructed. It's here + // because opening the tap can take some time on some platforms. + 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]; +#ifdef __WINDOWS__ + // Windows tags interfaces by their network IDs, which are shoved into the + // registry to mark persistent instance of the tap device. + char tag[24]; Utils::snprintf(tag,sizeof(tag),"%.16llx",(unsigned long long)_id); +#else + // Unix tries to get the same device name next time, if possible. + std::string tagstr; + char lcentry[128]; + Utils::snprintf(lcentry,sizeof(lcentry),"_dev_for_%.16llx",(unsigned long long)_id); + tagstr = _nc->getLocalConfig(lcentry); + const char *tag = (tagstr.length() > 0) ? tagstr.c_str() : (const char *)0; +#endif + _tap = new EthernetTap(_r,tag,_mac,ZT_IF_MTU,&_CBhandleTapData,this); + +#ifndef __WINDOWS__ + std::string dn(_tap->deviceName()); + if ((!tag)||(dn != tag)) + _nc->putLocalConfig(lcentry,dn); +#endif } catch (std::exception &exc) { LOG("network %.16llx failed to initialize: %s",_id,exc.what()); _netconfFailure = NETCONF_FAILURE_INIT_FAILED; diff --git a/node/Network.hpp b/node/Network.hpp index 7f07d753..d6a21c95 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -82,8 +82,7 @@ private: // Only NodeConfig can create, only SharedPtr can delete // Actual construction happens in newInstance() - Network() throw() : _tap((EthernetTap *)0) {} - + Network() throw() {} ~Network(); /** @@ -96,11 +95,12 @@ private: * cough). * * @param renv Runtime environment + * @param nc Parent NodeConfig * @param id Network ID * @return Reference counted pointer to new network * @throws std::runtime_error Unable to create tap device or other fatal error */ - static SharedPtr<Network> newInstance(const RuntimeEnvironment *renv,uint64_t id); + static SharedPtr<Network> newInstance(const RuntimeEnvironment *renv,NodeConfig *nc,uint64_t id); /** * Causes all persistent disk presence to be erased on delete @@ -406,6 +406,7 @@ private: void _dumpMulticastCerts(); uint64_t _id; + NodeConfig *_nc; MAC _mac; const RuntimeEnvironment *_r; EthernetTap *volatile _tap; diff --git a/node/NodeConfig.cpp b/node/NodeConfig.cpp index 64f27f29..88776a1b 100644 --- a/node/NodeConfig.cpp +++ b/node/NodeConfig.cpp @@ -91,7 +91,7 @@ NodeConfig::NodeConfig(const RuntimeEnvironment *renv,const char *authToken,unsi for(std::vector<uint64_t>::iterator n(configuredNets.begin());n!=configuredNets.end();++n) { try { - _networks[*n] = Network::newInstance(_r,*n); + _networks[*n] = Network::newInstance(_r,this,*n); } catch (std::exception &exc) { LOG("unable to create network %.16llx: %s",(unsigned long long)*n,exc.what()); } catch ( ... ) { @@ -246,7 +246,7 @@ std::vector<std::string> NodeConfig::execute(const char *command) _P("409 already a member of %.16llx",(unsigned long long)nwid); } else { try { - SharedPtr<Network> nw(Network::newInstance(_r,nwid)); + SharedPtr<Network> nw(Network::newInstance(_r,this,nwid)); _networks[nwid] = nw; _P("200 join %.16llx OK",(unsigned long long)nwid); } catch (std::exception &exc) { |