Răsfoiți Sursa

Fix for GitHub issue #37: remember *nix device names.

Adam Ierymenko 11 ani în urmă
părinte
comite
3f912eb4ad
4 a modificat fișierele cu 65 adăugiri și 21 ștergeri
  1. 35 12
      node/EthernetTap.cpp
  2. 24 4
      node/Network.cpp
  3. 4 3
      node/Network.hpp
  4. 2 2
      node/NodeConfig.cpp

+ 35 - 12
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");
 

+ 24 - 4
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;

+ 4 - 3
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;

+ 2 - 2
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) {