Browse Source

Delete persistent tap device on Windows when we leave a network.

Adam Ierymenko 11 years ago
parent
commit
afbbf61588
3 changed files with 101 additions and 28 deletions
  1. 76 25
      node/EthernetTap.cpp
  2. 20 0
      node/EthernetTap.hpp
  3. 5 3
      node/Network.cpp

+ 76 - 25
node/EthernetTap.cpp

@@ -570,6 +570,11 @@ std::string EthernetTap::deviceName() const
 	return std::string(_dev);
 }
 
+std::string EthernetTap::persistentId() const
+{
+	return std::string();
+}
+
 #ifdef __LINUX__
 bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
 {
@@ -901,6 +906,11 @@ void EthernetTap::threadMain()
 	}
 }
 
+bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid)
+{
+	return false;
+}
+
 } // namespace ZeroTier
 
 #endif // __UNIX_LIKE__ //////////////////////////////////////////////////////
@@ -1278,31 +1288,9 @@ bool EthernetTap::addIP(const InetAddress &ip)
 			}
 		}
 
-		// Update registry to contain all non-link-local IPs for this interface
-		std::string regMultiIps,regMultiNetmasks;
-		for(std::set<InetAddress>::const_iterator i(haveIps.begin());i!=haveIps.end();++i) {
-			if (!i->isLinkLocal()) {
-				regMultiIps.append(i->toIpString());
-				regMultiIps.push_back((char)0);
-				regMultiNetmasks.append(i->netmask().toIpString());
-				regMultiNetmasks.push_back((char)0);
-			}
-		}
-		HKEY tcpIpInterfaces;
-		if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) {
-			if (regMultiIps.length()) {
-				regMultiIps.push_back((char)0);
-				regMultiNetmasks.push_back((char)0);
-				RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress",REG_MULTI_SZ,regMultiIps.data(),(DWORD)regMultiIps.length());
-				RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask",REG_MULTI_SZ,regMultiNetmasks.data(),(DWORD)regMultiNetmasks.length());
-			} else {
-				RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress");
-				RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask");
-			}
-		}
-		RegCloseKey(tcpIpInterfaces);
+		_syncIpsWithRegistry(haveIps);
 	} catch (std::exception &exc) {
-		LOG("unexpected exception adding IP address to %s: %s",ip.toString().c_str(),deviceName().c_str(),exc.what());
+		LOG("unexpected exception adding IP address %s to %s: %s",ip.toString().c_str(),deviceName().c_str(),exc.what());
 	} catch ( ... ) {
 		LOG("unexpected exception adding IP address %s to %s: unknown exception",ip.toString().c_str(),deviceName().c_str());
 	}
@@ -1331,13 +1319,18 @@ bool EthernetTap::removeIP(const InetAddress &ip)
 					if (addr == ip) {
 						DeleteUnicastIpAddressEntry(&(ipt->Table[i]));
 						FreeMibTable(ipt);
+						_syncIpsWithRegistry(ips());
 						return true;
 					}
 				}
 			}
 			FreeMibTable((PVOID)ipt);
 		}
-	} catch ( ... ) {}
+	} catch (std::exception &exc) {
+		LOG("unexpected exception removing IP address %s from %s: %s",ip.toString().c_str(),deviceName().c_str(),exc.what());
+	} catch ( ... ) {
+		LOG("unexpected exception removing IP address %s from %s: unknown exception",ip.toString().c_str(),deviceName().c_str());
+	}
 	return false;
 }
 
@@ -1398,6 +1391,11 @@ std::string EthernetTap::deviceName() const
 	return _myDeviceInstanceId;
 }
 
+std::string EthernetTap::persistentId() const
+{
+	return _myDeviceInstanceIdPath;
+}
+
 bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
 {
 	std::set<MulticastGroup> newGroups;
@@ -1501,6 +1499,59 @@ void EthernetTap::threadMain()
 	CancelIo(_tap);
 }
 
+bool EthernetTap::deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid)
+{
+#ifdef _WIN64
+	BOOL is64Bit = TRUE;
+	const char *devcon = "\\devcon_x64.exe";
+#else
+	BOOL is64Bit = FALSE;
+	IsWow64Process(GetCurrentProcess(),&is64Bit);
+	const char *devcon = ((is64Bit == TRUE) ? "\\devcon_x64.exe" : "\\devcon_x86.exe");
+#endif
+
+	STARTUPINFOA startupInfo;
+	startupInfo.cb = sizeof(startupInfo);
+	PROCESS_INFORMATION processInfo;
+	memset(&startupInfo,0,sizeof(STARTUPINFOA));
+	memset(&processInfo,0,sizeof(PROCESS_INFORMATION));
+	if (CreateProcessA(NULL,(LPSTR)(std::string("\"") + _r->homePath + devcon + "\" remove @" + pid).c_str(),NULL,NULL,FALSE,0,NULL,NULL,&startupInfo,&processInfo)) {
+		WaitForSingleObject(processInfo.hProcess,INFINITE);
+		CloseHandle(processInfo.hProcess);
+		CloseHandle(processInfo.hThread);
+		return true;
+	}
+
+	return false;
+}
+
+void EthernetTap::_syncIpsWithRegistry(const std::set<InetAddress> &haveIps)
+{
+	// Update registry to contain all non-link-local IPs for this interface
+	std::string regMultiIps,regMultiNetmasks;
+	for(std::set<InetAddress>::const_iterator i(haveIps.begin());i!=haveIps.end();++i) {
+		if (!i->isLinkLocal()) {
+			regMultiIps.append(i->toIpString());
+			regMultiIps.push_back((char)0);
+			regMultiNetmasks.append(i->netmask().toIpString());
+			regMultiNetmasks.push_back((char)0);
+		}
+	}
+	HKEY tcpIpInterfaces;
+	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,"SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters\\Interfaces",0,KEY_READ|KEY_WRITE,&tcpIpInterfaces) == ERROR_SUCCESS) {
+		if (regMultiIps.length()) {
+			regMultiIps.push_back((char)0);
+			regMultiNetmasks.push_back((char)0);
+			RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress",REG_MULTI_SZ,regMultiIps.data(),(DWORD)regMultiIps.length());
+			RegSetKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask",REG_MULTI_SZ,regMultiNetmasks.data(),(DWORD)regMultiNetmasks.length());
+		} else {
+			RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"IPAddress");
+			RegDeleteKeyValueA(tcpIpInterfaces,_myDeviceInstanceId.c_str(),"SubnetMask");
+		}
+	}
+	RegCloseKey(tcpIpInterfaces);
+}
+
 } // namespace ZeroTier
 
 #endif // __WINDOWS__ ////////////////////////////////////////////////////////

+ 20 - 0
node/EthernetTap.hpp

@@ -175,6 +175,11 @@ public:
 	 */
 	std::string deviceName() const;
 
+	/**
+	 * @return OS-internal persistent device ID or empty string if not applicable to this platform or not persistent
+	 */
+	std::string persistentId() const;
+
 	/**
 	 * Fill or modify a set to contain multicast groups for this device
 	 *
@@ -195,6 +200,19 @@ public:
 	void threadMain()
 		throw();
 
+	/**
+	 * Remove persistent tap device by device name
+	 *
+	 * This has no effect on platforms that do not have persistent taps.
+	 * On platforms like Windows with persistent devices the device is
+	 * uninstalled.
+	 *
+	 * @param _r Runtime environment
+	 * @param pdev Device name as returned by persistentId() while tap is running
+	 * @return True if a device was deleted
+	 */
+	static bool deletePersistentTapDevice(const RuntimeEnvironment *_r,const char *pid);
+
 private:
 	const MAC _mac;
 	const unsigned int _mtu;
@@ -215,6 +233,8 @@ private:
 #endif
 
 #ifdef __WINDOWS__
+	void _syncIpsWithRegistry(const std::set<InetAddress> &haveIps);
+
 	HANDLE _tap;
 	OVERLAPPED _tapOvlRead,_tapOvlWrite;
 	char _tapReadBuf[ZT_IF_MTU + 32];

+ 5 - 3
node/Network.cpp

@@ -36,6 +36,7 @@
 #include "Switch.hpp"
 #include "Packet.hpp"
 #include "Buffer.hpp"
+#include "EthernetTap.hpp"
 
 #define ZT_NETWORK_CERT_WRITE_BUF_SIZE 131072
 
@@ -55,13 +56,14 @@ const char *Network::statusString(const Status s)
 
 Network::~Network()
 {
+	std::string devPersistentId(_tap->persistentId());
 	delete _tap;
+
 	if (_destroyOnDelete) {
 		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"));
-
-		// TODO: on Windows we need to also remove the tap interface since they're
-		// sticky on that platform.
+		if (devPersistentId.length())
+			EthernetTap::deletePersistentTapDevice(_r,devPersistentId.c_str());
 	} else {
 		// Causes flush of membership certs to disk
 		clean();