Browse Source

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.

Adam Ierymenko 11 years ago
parent
commit
f80ec871f6

+ 10 - 5
ZeroTierUI/mainwindow.cpp

@@ -121,7 +121,8 @@ MainWindow::MainWindow(QWidget *parent) :
 	ui->bottomContainerWidget->setVisible(false);
 	ui->bottomContainerWidget->setVisible(false);
 	ui->networkListWidget->setVisible(false);
 	ui->networkListWidget->setVisible(false);
 
 
-	this->pollServiceTimerId = this->startTimer(1000);
+	this->firstTimerTick = true;
+	this->pollServiceTimerId = this->startTimer(200);
 	this->cyclesSinceResponseFromService = 0;
 	this->cyclesSinceResponseFromService = 0;
 }
 }
 
 
@@ -133,15 +134,19 @@ MainWindow::~MainWindow()
 	mainWindow = (MainWindow *)0;
 	mainWindow = (MainWindow *)0;
 }
 }
 
 
-void MainWindow::timerEvent(QTimerEvent *event)
+void MainWindow::timerEvent(QTimerEvent *event) // event can be null since code also calls this directly
 {
 {
-	event->accept();
-
 	if (this->isHidden())
 	if (this->isHidden())
 		return;
 		return;
-	if (pollServiceTimerId < 0)
+	if (this->pollServiceTimerId < 0)
 		return;
 		return;
 
 
+	if (this->firstTimerTick) {
+		this->firstTimerTick = false;
+		this->killTimer(this->pollServiceTimerId);
+		this->pollServiceTimerId = this->startTimer(1500);
+	}
+
 	if (!zeroTierClient) {
 	if (!zeroTierClient) {
 		std::string authToken;
 		std::string authToken;
 		if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {
 		if (!ZeroTier::Utils::readFile(ZeroTier::Node::LocalClient::authTokenDefaultUserPath().c_str(),authToken)) {

+ 1 - 0
ZeroTierUI/mainwindow.h

@@ -86,6 +86,7 @@ private:
 	QString myAddress;
 	QString myAddress;
 	QString myStatus;
 	QString myStatus;
 	QString myVersion;
 	QString myVersion;
+	bool firstTimerTick;
 	int pollServiceTimerId;
 	int pollServiceTimerId;
 	unsigned int numPeers;
 	unsigned int numPeers;
 	unsigned int cyclesSinceResponseFromService;
 	unsigned int cyclesSinceResponseFromService;

+ 3 - 0
ZeroTierUI/stylesheet.css

@@ -64,6 +64,9 @@ QListWidget.ipAddressList::item {
 }
 }
 QListWidget.ipAddressList::item:selected {
 QListWidget.ipAddressList::item:selected {
 	background: transparent;
 	background: transparent;
+        border-top: 0;
+        border-left: 0;
+        border-right: 0;
 	border-bottom: 1px solid transparent;
 	border-bottom: 1px solid transparent;
 }
 }
 QListWidget.ipAddressList::item:hover {
 QListWidget.ipAddressList::item:hover {

+ 23 - 12
node/EthernetTap.cpp

@@ -336,8 +336,6 @@ EthernetTap::EthernetTap(
 		}
 		}
 	}
 	}
 
 
-	whack(); // turns on IPv6 on OSX
-
 	::pipe(_shutdownSignalPipe);
 	::pipe(_shutdownSignalPipe);
 
 
 	_thread = Thread::start(this);
 	_thread = Thread::start(this);
@@ -378,7 +376,7 @@ EthernetTap::~EthernetTap()
 #endif // __APPLE__
 #endif // __APPLE__
 }
 }
 
 
-#ifdef __APPLE__
+/*
 void EthernetTap::whack()
 void EthernetTap::whack()
 {
 {
 	const char *ipconfig = UNIX_COMMANDS[ZT_MAC_IPCONFIG_COMMAND];
 	const char *ipconfig = UNIX_COMMANDS[ZT_MAC_IPCONFIG_COMMAND];
@@ -393,9 +391,7 @@ void EthernetTap::whack()
 		}
 		}
 	}
 	}
 }
 }
-#else
-void EthernetTap::whack() {}
-#endif // __APPLE__ / !__APPLE__
+*/
 
 
 void EthernetTap::setDisplayName(const char *dn)
 void EthernetTap::setDisplayName(const char *dn)
 {
 {
@@ -979,7 +975,8 @@ EthernetTap::EthernetTap(
 	_arg(arg),
 	_arg(arg),
 	_tap(INVALID_HANDLE_VALUE),
 	_tap(INVALID_HANDLE_VALUE),
 	_injectSemaphore(INVALID_HANDLE_VALUE),
 	_injectSemaphore(INVALID_HANDLE_VALUE),
-	_run(true)
+	_run(true),
+	_initialized(false)
 {
 {
 	char subkeyName[4096];
 	char subkeyName[4096];
 	char subkeyClass[4096];
 	char subkeyClass[4096];
@@ -1203,11 +1200,15 @@ EthernetTap::EthernetTap(
 	// Start background thread that actually performs I/O
 	// Start background thread that actually performs I/O
 	_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
 	_injectSemaphore = CreateSemaphore(NULL,0,1,NULL);
 	_thread = Thread::start(this);
 	_thread = Thread::start(this);
+
+	// Certain functions can now work (e.g. ips())
+	_initialized = true;
 }
 }
 
 
 EthernetTap::~EthernetTap()
 EthernetTap::~EthernetTap()
 {
 {
 	_run = false;
 	_run = false;
+
 	ReleaseSemaphore(_injectSemaphore,1,NULL);
 	ReleaseSemaphore(_injectSemaphore,1,NULL);
 	Thread::join(_thread);
 	Thread::join(_thread);
 	CloseHandle(_tap);
 	CloseHandle(_tap);
@@ -1237,12 +1238,10 @@ EthernetTap::~EthernetTap()
 	}
 	}
 }
 }
 
 
-void EthernetTap::whack()
-{
-}
-
 void EthernetTap::setDisplayName(const char *dn)
 void EthernetTap::setDisplayName(const char *dn)
 {
 {
+	if (!_initialized)
+		return;
 	HKEY ifp;
 	HKEY ifp;
 	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _myDeviceInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) {
 	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE,(std::string("SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\") + _myDeviceInstanceId).c_str(),0,KEY_READ|KEY_WRITE,&ifp) == ERROR_SUCCESS) {
 		RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1));
 		RegSetKeyValueA(ifp,"Connection","Name",REG_SZ,(LPCVOID)dn,(DWORD)(strlen(dn)+1));
@@ -1252,6 +1251,8 @@ void EthernetTap::setDisplayName(const char *dn)
 
 
 bool EthernetTap::addIP(const InetAddress &ip)
 bool EthernetTap::addIP(const InetAddress &ip)
 {
 {
+	if (!_initialized)
+		return false;
 	if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
 	if (!ip.netmaskBits()) // sanity check... netmask of 0.0.0.0 is WUT?
 		return false;
 		return false;
 
 
@@ -1299,6 +1300,8 @@ bool EthernetTap::addIP(const InetAddress &ip)
 
 
 bool EthernetTap::removeIP(const InetAddress &ip)
 bool EthernetTap::removeIP(const InetAddress &ip)
 {
 {
+	if (!_initialized)
+		return false;
 	try {
 	try {
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
 		std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
@@ -1339,6 +1342,9 @@ std::set<InetAddress> EthernetTap::ips() const
 	static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
 	static const InetAddress linkLocalLoopback("fe80::1",64); // what is this and why does Windows assign it?
 	std::set<InetAddress> addrs;
 	std::set<InetAddress> addrs;
 
 
+	if (!_initialized)
+		return addrs;
+
 	try {
 	try {
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		MIB_UNICASTIPADDRESS_TABLE *ipt = (MIB_UNICASTIPADDRESS_TABLE *)0;
 		std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
 		std::pair<NET_LUID,NET_IFINDEX> ifidx = _findAdapterByGuid(_deviceGuid);
@@ -1369,6 +1375,8 @@ std::set<InetAddress> EthernetTap::ips() const
 
 
 void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
 void EthernetTap::put(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
 {
 {
+	if (!_initialized)
+		return;
 	if (len > (ZT_IF_MTU))
 	if (len > (ZT_IF_MTU))
 		return;
 		return;
 
 
@@ -1398,6 +1406,9 @@ std::string EthernetTap::persistentId() const
 
 
 bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
 bool EthernetTap::updateMulticastGroups(std::set<MulticastGroup> &groups)
 {
 {
+	if (!_initialized)
+		return false;
+
 	std::set<MulticastGroup> newGroups;
 	std::set<MulticastGroup> newGroups;
 
 
 	// Ensure that groups are added for each IP... this handles the MAC:ADI
 	// Ensure that groups are added for each IP... this handles the MAC:ADI
@@ -1456,7 +1467,7 @@ void EthernetTap::threadMain()
 	HANDLE wait4[3];
 	HANDLE wait4[3];
 	wait4[0] = _injectSemaphore;
 	wait4[0] = _injectSemaphore;
 	wait4[1] = _tapOvlRead.hEvent;
 	wait4[1] = _tapOvlRead.hEvent;
-	wait4[2] = _tapOvlWrite.hEvent;
+	wait4[2] = _tapOvlWrite.hEvent; // only included if writeInProgress is true
 
 
 	ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead);
 	ReadFile(_tap,_tapReadBuf,sizeof(_tapReadBuf),NULL,&_tapOvlRead);
 	bool writeInProgress = false;
 	bool writeInProgress = false;

+ 1 - 5
node/EthernetTap.hpp

@@ -94,11 +94,6 @@ public:
 	 */
 	 */
 	~EthernetTap();
 	~EthernetTap();
 
 
-	/**
-	 * Perform OS dependent actions on network configuration change detection
-	 */
-	void whack();
-
 	/**
 	/**
 	 * Set the user display name for this connection
 	 * Set the user display name for this connection
 	 *
 	 *
@@ -245,6 +240,7 @@ private:
 	std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
 	std::queue< std::pair< Array<char,ZT_IF_MTU + 32>,unsigned int > > _injectPending;
 	Mutex _injectPending_m;
 	Mutex _injectPending_m;
 	volatile bool _run;
 	volatile bool _run;
+	volatile bool _initialized;
 #endif
 #endif
 };
 };
 
 

+ 61 - 21
node/Network.cpp

@@ -46,6 +46,7 @@ const char *Network::statusString(const Status s)
 	throw()
 	throw()
 {
 {
 	switch(s) {
 	switch(s) {
+		case NETWORK_INITIALIZING: return "INITIALIZING";
 		case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF";
 		case NETWORK_WAITING_FOR_FIRST_AUTOCONF: return "WAITING_FOR_FIRST_AUTOCONF";
 		case NETWORK_OK: return "OK";
 		case NETWORK_OK: return "OK";
 		case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED";
 		case NETWORK_ACCESS_DENIED: return "ACCESS_DENIED";
@@ -56,6 +57,8 @@ const char *Network::statusString(const Status s)
 
 
 Network::~Network()
 Network::~Network()
 {
 {
+	Thread::join(_setupThread);
+
 	std::string devPersistentId(_tap->persistentId());
 	std::string devPersistentId(_tap->persistentId());
 	delete _tap;
 	delete _tap;
 
 
@@ -73,46 +76,50 @@ Network::~Network()
 
 
 SharedPtr<Network> Network::newInstance(const RuntimeEnvironment *renv,uint64_t id)
 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());
 	SharedPtr<Network> nw(new Network());
 	nw->_id = id;
 	nw->_id = id;
-	nw->_ready = false; // disable handling of Ethernet frames during construct
+	nw->_mac = renv->identity.address().toMAC();
 	nw->_r = renv;
 	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->_lastConfigUpdate = 0;
-	nw->_status = NETWORK_WAITING_FOR_FIRST_AUTOCONF;
 	nw->_destroyOnDelete = false;
 	nw->_destroyOnDelete = false;
+	nw->_netconfFailure = NETCONF_FAILURE_NONE;
+
 	if (nw->controller() == renv->identity.address()) // netconf masters can't really join networks
 	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");
 		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;
 	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 {
 	try {
 		SharedPtr<NetworkConfig> newConfig(new NetworkConfig(conf));
 		SharedPtr<NetworkConfig> newConfig(new NetworkConfig(conf));
 		if ((newConfig->networkId() == _id)&&(newConfig->issuedTo() == _r->identity.address())) {
 		if ((newConfig->networkId() == _id)&&(newConfig->issuedTo() == _r->identity.address())) {
-			Mutex::Lock _l(_lock);
 			_config = newConfig;
 			_config = newConfig;
 
 
 			if (newConfig->staticIps().size())
 			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();
 			_lastConfigUpdate = Utils::now();
-			_status = NETWORK_OK;
+			_netconfFailure = NETCONF_FAILURE_NONE;
 
 
 			if (saveToDisk) {
 			if (saveToDisk) {
 				std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + idString() + ".conf");
 				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);
 					Utils::lockDownFile(confPath.c_str(),false);
 				}
 				}
 			}
 			}
+
+			return true;
 		} else {
 		} else {
 			LOG("ignored invalid configuration for network %.16llx (configuration contains mismatched network ID or issued-to address)",(unsigned long long)_id);
 			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 ( ... ) {
 	} catch ( ... ) {
 		LOG("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
 		LOG("ignored invalid configuration for network %.16llx (unknown exception)",(unsigned long long)_id);
 	}
 	}
+
+	return false;
 }
 }
 
 
 void Network::requestConfiguration()
 void Network::requestConfiguration()
 {
 {
+	if (!_tap)
+		return; // don't bother requesting until we are initialized
+
 	if (controller() == _r->identity.address()) {
 	if (controller() == _r->identity.address()) {
 		// 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);
 		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()
 void Network::clean()
 {
 {
 	Mutex::Lock _l(_lock);
 	Mutex::Lock _l(_lock);
+
 	if ((_config)&&(_config->isOpen())) {
 	if ((_config)&&(_config->isOpen())) {
 		// Open (public) networks do not track certs or cert pushes at all.
 		// Open (public) networks do not track certs or cert pushes at all.
 		_membershipCertificates.clear();
 		_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)
 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;
 		return;
 
 
 	const RuntimeEnvironment *_r = ((Network *)arg)->_r;
 	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()
 void Network::_restoreState()
 {
 {
 	if (!_id)
 	if (!_id)

+ 126 - 37
node/Network.hpp

@@ -53,6 +53,7 @@
 #include "BandwidthAccount.hpp"
 #include "BandwidthAccount.hpp"
 #include "NetworkConfig.hpp"
 #include "NetworkConfig.hpp"
 #include "CertificateOfMembership.hpp"
 #include "CertificateOfMembership.hpp"
+#include "Thread.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -91,6 +92,9 @@ private:
 	 * If there is no saved state, a dummy .conf is created on disk to remember
 	 * If there is no saved state, a dummy .conf is created on disk to remember
 	 * this network across restarts.
 	 * this network across restarts.
 	 *
 	 *
+	 * This can be a time consuming operation on some platforms (cough Windows
+	 * cough).
+	 *
 	 * @param renv Runtime environment
 	 * @param renv Runtime environment
 	 * @param id Network ID
 	 * @param id Network ID
 	 * @return Reference counted pointer to new network
 	 * @return Reference counted pointer to new network
@@ -109,10 +113,12 @@ public:
 	 */
 	 */
 	enum Status
 	enum Status
 	{
 	{
+		NETWORK_INITIALIZING,
 		NETWORK_WAITING_FOR_FIRST_AUTOCONF,
 		NETWORK_WAITING_FOR_FIRST_AUTOCONF,
 		NETWORK_OK,
 		NETWORK_OK,
 		NETWORK_ACCESS_DENIED,
 		NETWORK_ACCESS_DENIED,
-		NETWORK_NOT_FOUND
+		NETWORK_NOT_FOUND,
+		NETWORK_INITIALIZATION_FAILED
 	};
 	};
 
 
 	/**
 	/**
@@ -127,11 +133,6 @@ public:
 	 */
 	 */
 	inline uint64_t id() const throw() { return _id; }
 	inline uint64_t id() const throw() { return _id; }
 
 
-	/**
-	 * @return Ethernet tap
-	 */
-	inline EthernetTap &tap() throw() { return *_tap; }
-
 	/**
 	/**
 	 * @return Address of network's controlling node
 	 * @return Address of network's controlling node
 	 */
 	 */
@@ -155,7 +156,10 @@ public:
 	inline bool updateMulticastGroups()
 	inline bool updateMulticastGroups()
 	{
 	{
 		Mutex::Lock _l(_lock);
 		Mutex::Lock _l(_lock);
-		return _tap->updateMulticastGroups(_multicastGroups);
+		EthernetTap *t = _tap;
+		if (t)
+			return _tap->updateMulticastGroups(_multicastGroups);
+		return false;
 	}
 	}
 
 
 	/**
 	/**
@@ -173,10 +177,34 @@ public:
 	 * This is called by PacketDecoder when an update comes over the wire, or
 	 * This is called by PacketDecoder when an update comes over the wire, or
 	 * internally when an old config is reloaded from disk.
 	 * internally when an old config is reloaded from disk.
 	 *
 	 *
+	 * This also cancels any netconf failure flags.
+	 *
+	 * The network can't accept configuration when in INITIALIZATION state,
+	 * and so in that state this will just return false.
+	 *
 	 * @param conf Configuration in key/value dictionary form
 	 * @param conf Configuration in key/value dictionary form
 	 * @param saveToDisk IF true (default), write config to disk
 	 * @param saveToDisk IF true (default), write config to disk
+	 * @return True if configuration was accepted
+	 */
+	bool setConfiguration(const Dictionary &conf,bool saveToDisk = true);
+
+	/**
+	 * Set netconf failure to 'access denied'.
 	 */
 	 */
-	void setConfiguration(const Dictionary &conf,bool saveToDisk = true);
+	inline void setAccessDenied()
+	{
+		Mutex::Lock _l(_lock);
+		_netconfFailure = NETCONF_FAILURE_ACCESS_DENIED;
+	}
+
+	/**
+	 * Set netconf failure to 'not found'.
+	 */
+	inline void setNotFound()
+	{
+		Mutex::Lock _l(_lock);
+		_netconfFailure = NETCONF_FAILURE_NOT_FOUND;
+	}
 
 
 	/**
 	/**
 	 * Causes this network to request an updated configuration from its master node now
 	 * Causes this network to request an updated configuration from its master node now
@@ -223,16 +251,6 @@ public:
 	 */
 	 */
 	inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
 	inline uint64_t lastConfigUpdate() const throw() { return _lastConfigUpdate; }
 
 
-	/** 
-	 * Force this network's status to a particular state based on config reply
-	 */
-	inline void forceStatusTo(const Status s)
-		throw()
-	{
-		Mutex::Lock _l(_lock);
-		_status = s;
-	}
-
 	/**
 	/**
 	 * @return Status of this network
 	 * @return Status of this network
 	 */
 	 */
@@ -240,17 +258,20 @@ public:
 		throw()
 		throw()
 	{
 	{
 		Mutex::Lock _l(_lock);
 		Mutex::Lock _l(_lock);
-		return _status;
-	}
-
-	/**
-	 * @return True if this network is in "OK" status and can accept traffic from us
-	 */
-	inline bool isUp() const
-		throw()
-	{
-		Mutex::Lock _l(_lock);
-		return ((_config)&&(_status == NETWORK_OK)&&(_ready));
+		if (_tap) {
+			switch(_netconfFailure) {
+				case NETCONF_FAILURE_ACCESS_DENIED:
+					return NETWORK_ACCESS_DENIED;
+				case NETCONF_FAILURE_NOT_FOUND:
+					return NETWORK_NOT_FOUND;
+				case NETCONF_FAILURE_NONE:
+					if (_lastConfigUpdate > 0)
+						return NETWORK_OK;
+					else return NETWORK_WAITING_FOR_FIRST_AUTOCONF;
+			}
+		} else if (_netconfFailure == NETCONF_FAILURE_INIT_FAILED)
+			return NETWORK_INITIALIZATION_FAILED;
+		else return NETWORK_INITIALIZING;
 	}
 	}
 
 
 	/**
 	/**
@@ -307,6 +328,73 @@ public:
 		return _config;
 		return _config;
 	}
 	}
 
 
+	/**
+	 * Thread main method; do not call elsewhere
+	 */
+	void threadMain()
+		throw();
+
+	/**
+	 * Inject a frame into tap (if it's created)
+	 *
+	 * @param from Origin MAC
+	 * @param to Destination MC
+	 * @param etherType Ethernet frame type
+	 * @param data Frame data
+	 * @param len Frame length
+	 */
+	inline void tapPut(const MAC &from,const MAC &to,unsigned int etherType,const void *data,unsigned int len)
+	{
+		EthernetTap *t = _tap;
+		if (t)
+			t->put(from,to,etherType,data,len);
+	}
+
+	/**
+	 * Inject a frame into tap with local MAC as destination MAC (if it's created)
+	 *
+	 * @param from Origin MAC
+	 * @param etherType Ethernet frame type
+	 * @param data Frame data
+	 * @param len Frame length
+	 */
+	inline void tapPut(const MAC &from,unsigned int etherType,const void *data,unsigned int len)
+	{
+		EthernetTap *t = _tap;
+		if (t)
+			t->put(from,t->mac(),etherType,data,len);
+	}
+
+	/**
+	 * @return Tap device name or empty string if still initializing
+	 */
+	inline std::string tapDeviceName() const
+	{
+		EthernetTap *t = _tap;
+		if (t)
+			return t->deviceName();
+		else return std::string();
+	}
+
+	/**
+	 * @return Ethernet MAC address for this network's local interface
+	 */
+	inline const MAC &mac() const
+	{
+		return _mac;
+	}
+
+	/**
+	 * @return Set of currently assigned IP addresses
+	 */
+	inline std::set<InetAddress> ips() const
+	{
+		EthernetTap *t = _tap;
+		if (t)
+			return t->ips();
+		return std::set<InetAddress>();
+	}
+
 private:
 private:
 	static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
 	static void _CBhandleTapData(void *arg,const MAC &from,const MAC &to,unsigned int etherType,const Buffer<4096> &data);
 
 
@@ -315,22 +403,23 @@ private:
 	void _dumpMulticastCerts();
 	void _dumpMulticastCerts();
 
 
 	uint64_t _id;
 	uint64_t _id;
-
+	MAC _mac;
 	const RuntimeEnvironment *_r;
 	const RuntimeEnvironment *_r;
-
-	EthernetTap *_tap;
+	EthernetTap *volatile _tap;
 	std::set<MulticastGroup> _multicastGroups;
 	std::set<MulticastGroup> _multicastGroups;
-
 	std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts;
 	std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts;
 	std::map<Address,CertificateOfMembership> _membershipCertificates;
 	std::map<Address,CertificateOfMembership> _membershipCertificates;
 	std::map<Address,uint64_t> _lastPushedMembershipCertificate;
 	std::map<Address,uint64_t> _lastPushedMembershipCertificate;
 	SharedPtr<NetworkConfig> _config;
 	SharedPtr<NetworkConfig> _config;
-
 	volatile uint64_t _lastConfigUpdate;
 	volatile uint64_t _lastConfigUpdate;
-	volatile Status _status;
 	volatile bool _destroyOnDelete;
 	volatile bool _destroyOnDelete;
-	volatile bool _ready;
-
+	volatile enum {
+		NETCONF_FAILURE_NONE,
+		NETCONF_FAILURE_ACCESS_DENIED,
+		NETCONF_FAILURE_NOT_FOUND,
+		NETCONF_FAILURE_INIT_FAILED
+	} _netconfFailure;
+	Thread _setupThread;
 	Mutex _lock;
 	Mutex _lock;
 
 
 	AtomicCounter __refCount;
 	AtomicCounter __refCount;

+ 0 - 1
node/Node.cpp

@@ -545,7 +545,6 @@ Node::ReasonForTermination Node::run()
 					LOG("netconf fingerprint change: %.16llx != %.16llx, resyncing with network",networkConfigurationFingerprint,fp);
 					LOG("netconf fingerprint change: %.16llx != %.16llx, resyncing with network",networkConfigurationFingerprint,fp);
 					networkConfigurationFingerprint = fp;
 					networkConfigurationFingerprint = fp;
 					resynchronize = true;
 					resynchronize = true;
-					_r->nc->whackAllTaps(); // call whack() on all tap devices -- hack, might go away
 				}
 				}
 			}
 			}
 
 

+ 3 - 10
node/NodeConfig.cpp

@@ -115,14 +115,6 @@ NodeConfig::~NodeConfig()
 {
 {
 }
 }
 
 
-void NodeConfig::whackAllTaps()
-{
-	std::vector< SharedPtr<Network> > nwlist;
-	Mutex::Lock _l(_networks_m);
-	for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
-		n->second->tap().whack();
-}
-
 void NodeConfig::clean()
 void NodeConfig::clean()
 {
 {
 	Mutex::Lock _l(_networks_m);
 	Mutex::Lock _l(_networks_m);
@@ -205,7 +197,7 @@ std::vector<std::string> NodeConfig::execute(const char *command)
 		_P("200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>");
 		_P("200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>");
 		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) {
 		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator nw(_networks.begin());nw!=_networks.end();++nw) {
 			std::string tmp;
 			std::string tmp;
-			std::set<InetAddress> ips(nw->second->tap().ips());
+			std::set<InetAddress> ips(nw->second->ips());
 			for(std::set<InetAddress>::iterator i(ips.begin());i!=ips.end();++i) {
 			for(std::set<InetAddress>::iterator i(ips.begin());i!=ips.end();++i) {
 				if (tmp.length())
 				if (tmp.length())
 					tmp.push_back(',');
 					tmp.push_back(',');
@@ -219,13 +211,14 @@ std::vector<std::string> NodeConfig::execute(const char *command)
 				age = 0;
 				age = 0;
 			age /= 1000;
 			age /= 1000;
 
 
+			std::string dn(nw->second->tapDeviceName());
 			_P("200 listnetworks %.16llx %s %s %lld %s %s %s",
 			_P("200 listnetworks %.16llx %s %s %lld %s %s %s",
 				(unsigned long long)nw->first,
 				(unsigned long long)nw->first,
 				((nconf) ? nconf->name().c_str() : "?"),
 				((nconf) ? nconf->name().c_str() : "?"),
 				Network::statusString(nw->second->status()),
 				Network::statusString(nw->second->status()),
 				age,
 				age,
 				((nconf) ? (nconf->isOpen() ? "public" : "private") : "?"),
 				((nconf) ? (nconf->isOpen() ? "public" : "private") : "?"),
-				nw->second->tap().deviceName().c_str(),
+				(dn.length() > 0) ? dn.c_str() : "?",
 				((tmp.length() > 0) ? tmp.c_str() : "-"));
 				((tmp.length() > 0) ? tmp.c_str() : "-"));
 		}
 		}
 	} else if (cmd[0] == "join") {
 	} else if (cmd[0] == "join") {

+ 5 - 7
node/NodeConfig.hpp

@@ -90,11 +90,6 @@ public:
 		return nwlist;
 		return nwlist;
 	}
 	}
 
 
-	/**
-	 * Call whack() on all networks' tap devices
-	 */
-	void whackAllTaps();
-
 	/**
 	/**
 	 * Perform cleanup and possibly update saved state
 	 * Perform cleanup and possibly update saved state
 	 */
 	 */
@@ -117,8 +112,11 @@ public:
 	{
 	{
 		std::set<std::string> tapDevs;
 		std::set<std::string> tapDevs;
 		Mutex::Lock _l(_networks_m);
 		Mutex::Lock _l(_networks_m);
-		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
-			tapDevs.insert(n->second->tap().deviceName());
+		for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
+			std::string dn(n->second->tapDeviceName());
+			if (dn.length())
+				tapDevs.insert(dn);
+		}
 		return tapDevs;
 		return tapDevs;
 	}
 	}
 
 

+ 4 - 4
node/PacketDecoder.cpp

@@ -139,7 +139,7 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 				} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
 				} else if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
 					SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 					SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 					if ((network)&&(network->controller() == source()))
 					if ((network)&&(network->controller() == source()))
-						network->forceStatusTo(Network::NETWORK_NOT_FOUND);
+						network->setNotFound();
 				}
 				}
 				break;
 				break;
 			case Packet::ERROR_IDENTITY_COLLISION:
 			case Packet::ERROR_IDENTITY_COLLISION:
@@ -154,7 +154,7 @@ bool PacketDecoder::_doERROR(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 			case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
 			case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
 				SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				SharedPtr<Network> network(_r->nc->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				if ((network)&&(network->controller() == source()))
 				if ((network)&&(network->controller() == source()))
-					network->forceStatusTo(Network::NETWORK_ACCESS_DENIED);
+					network->setAccessDenied();
 			}	break;
 			}	break;
 			default:
 			default:
 				break;
 				break;
@@ -416,7 +416,7 @@ bool PacketDecoder::_doFRAME(const RuntimeEnvironment *_r,const SharedPtr<Peer>
 				unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
 				unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
 				if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
 				if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
 					if (network->config()->permitsEtherType(etherType)) {
 					if (network->config()->permitsEtherType(etherType)) {
-						network->tap().put(source().toMAC(),network->tap().mac(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
+						network->tapPut(source().toMAC(),etherType,data() + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD,size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD);
 					} else {
 					} else {
 						TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id());
 						TRACE("dropped FRAME from %s: ethernet type %u not allowed on network %.16llx",source().toString().c_str(),etherType,(unsigned long long)network->id());
 						return true;
 						return true;
@@ -677,7 +677,7 @@ bool PacketDecoder::_doMULTICAST_FRAME(const RuntimeEnvironment *_r,const Shared
 						TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str());
 						TRACE("dropped MULTICAST_FRAME from %s(%s): rate limits exceeded for sender %s",source().toString().c_str(),_remoteAddress.toString().c_str(),origin.toString().c_str());
 						return true;
 						return true;
 					} else {
 					} else {
-						network->tap().put(sourceMac,dest.mac(),etherType,frame,frameLen);
+						network->tapPut(sourceMac,dest.mac(),etherType,frame,frameLen);
 					}
 					}
 				}
 				}
 			}
 			}

+ 3 - 4
node/Switch.cpp

@@ -86,12 +86,11 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	if (!nconf)
 	if (!nconf)
 		return;
 		return;
 
 
-	if (to == network->tap().mac()) {
-		LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tap().deviceName().c_str());
+	if (to == network->mac()) {
+		LOG("%s: frame received from self, ignoring (bridge loop? OS bug?)",network->tapDeviceName().c_str());
 		return;
 		return;
 	}
 	}
-
-	if (from != network->tap().mac()) {
+	if (from != network->mac()) {
 		LOG("ignored tap: %s -> %s %s (bridging not supported)",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 		LOG("ignored tap: %s -> %s %s (bridging not supported)",from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 		return;
 		return;
 	}
 	}