Browse Source

Whole bunch of stuff: netconf, bug fixes, tweaks to ping and firewall opener timing code.

Adam Ierymenko 12 years ago
parent
commit
e73c4cb68b
9 changed files with 149 additions and 22 deletions
  1. 1 1
      node/EthernetTap.cpp
  2. 1 1
      node/Multicaster.hpp
  3. 78 3
      node/Network.cpp
  4. 25 8
      node/Network.hpp
  5. 6 2
      node/Node.cpp
  6. 33 3
      node/NodeConfig.cpp
  7. 2 2
      node/NodeConfig.hpp
  8. 1 0
      node/PacketDecoder.cpp
  9. 2 2
      node/Topology.hpp

+ 1 - 1
node/EthernetTap.cpp

@@ -114,7 +114,7 @@ EthernetTap::EthernetTap(
 
 
 	_fd = ::open("/dev/net/tun",O_RDWR);
 	_fd = ::open("/dev/net/tun",O_RDWR);
 	if (_fd <= 0)
 	if (_fd <= 0)
-		throw std::runtime_error("could not open TUN/TAP device");
+		throw std::runtime_error(std::string("could not open TUN/TAP device: ") + strerror(errno));
 
 
 	struct ifreq ifr;
 	struct ifreq ifr;
 	memset(&ifr,0,sizeof(ifr));
 	memset(&ifr,0,sizeof(ifr));

+ 1 - 1
node/Multicaster.hpp

@@ -68,7 +68,7 @@ class Multicaster
 {
 {
 public:
 public:
 	/**
 	/**
-	 * 256-bit simple bloom filter included with multicast frame packets
+	 * Simple bit field bloom filter included with multicast frame packets
 	 */
 	 */
 	typedef BloomFilter<ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BITS> MulticastBloomFilter;
 	typedef BloomFilter<ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BITS> MulticastBloomFilter;
 
 

+ 78 - 3
node/Network.cpp

@@ -25,6 +25,8 @@
  * LLC. Start here: http://www.zerotier.com/
  * LLC. Start here: http://www.zerotier.com/
  */
  */
 
 
+#include <stdio.h>
+#include <string.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <math.h>
 #include <math.h>
 
 
@@ -35,6 +37,7 @@
 #include "Network.hpp"
 #include "Network.hpp"
 #include "Switch.hpp"
 #include "Switch.hpp"
 #include "Packet.hpp"
 #include "Packet.hpp"
+#include "Utils.hpp"
 
 
 namespace ZeroTier {
 namespace ZeroTier {
 
 
@@ -106,14 +109,44 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t id)
 	_r(renv),
 	_r(renv),
 	_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
 	_tap(renv,renv->identity.address().toMAC(),ZT_IF_MTU,&_CBhandleTapData,this),
 	_id(id),
 	_id(id),
-	_lastConfigUpdate(0)
+	_lastConfigUpdate(0),
+	_destroyOnDelete(false)
 {
 {
 	if (controller() == _r->identity.address())
 	if (controller() == _r->identity.address())
 		throw std::runtime_error("configuration error: cannot add a network for which I am the netconf master");
 		throw std::runtime_error("configuration error: cannot add a network for which I am the netconf master");
+
+	std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
+	std::string confs;
+	if (Utils::readFile(confPath.c_str(),confs)) {
+		try {
+			if (confs.length()) {
+				Config conf(confs);
+				if (conf.containsAllFields())
+					setConfiguration(Config(conf));
+			}
+		} catch ( ... ) {} // ignore invalid config on disk, we will re-request
+	} else {
+		// If the conf file isn't present, "touch" it so we'll remember
+		// the existence of this network.
+		FILE *tmp = fopen(confPath.c_str(),"w");
+		if (tmp)
+			fclose(tmp);
+	}
+
+	requestConfiguration();
 }
 }
 
 
 Network::~Network()
 Network::~Network()
 {
 {
+	if (_destroyOnDelete) {
+		std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
+		std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts");
+		unlink(confPath.c_str());
+		unlink(mcdbPath.c_str());
+	} else {
+		// Causes flush of membership certs to disk
+		clean();
+	}
 }
 }
 
 
 void Network::setConfiguration(const Network::Config &conf)
 void Network::setConfiguration(const Network::Config &conf)
@@ -124,6 +157,11 @@ void Network::setConfiguration(const Network::Config &conf)
 		_configuration = conf;
 		_configuration = conf;
 		_myCertificate = conf.certificateOfMembership();
 		_myCertificate = conf.certificateOfMembership();
 		_lastConfigUpdate = Utils::now();
 		_lastConfigUpdate = Utils::now();
+
+		std::string confPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".conf");
+		if (!Utils::writeFile(confPath.c_str(),conf.toString())) {
+			LOG("error: unable to write network configuration file at: %s",confPath.c_str());
+		}
 	}
 	}
 }
 }
 
 
@@ -136,9 +174,17 @@ void Network::requestConfiguration()
 	TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str());
 	TRACE("requesting netconf for network %.16llx from netconf master %s",(unsigned long long)_id,controller().toString().c_str());
 	Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
 	Packet outp(controller(),_r->identity.address(),Packet::VERB_NETWORK_CONFIG_REQUEST);
 	outp.append((uint64_t)_id);
 	outp.append((uint64_t)_id);
+	outp.append((uint16_t)0); // no meta-data
 	_r->sw->send(outp,true);
 	_r->sw->send(outp,true);
 }
 }
 
 
+void Network::addMembershipCertificate(const Address &peer,const Certificate &cert)
+{
+	Mutex::Lock _l(_lock);
+	if (!_configuration.isOpen())
+		_membershipCertificates[peer] = cert;
+}
+
 bool Network::isAllowed(const Address &peer) const
 bool Network::isAllowed(const Address &peer) const
 {
 {
 	// Exceptions can occur if we do not yet have *our* configuration.
 	// Exceptions can occur if we do not yet have *our* configuration.
@@ -164,10 +210,39 @@ void Network::clean()
 	if (_configuration.isOpen())
 	if (_configuration.isOpen())
 		_membershipCertificates.clear();
 		_membershipCertificates.clear();
 	else {
 	else {
+		std::string mcdbPath(_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d" + ZT_PATH_SEPARATOR_S + toString() + ".mcerts");
+		FILE *mcdb = fopen(mcdbPath.c_str(),"wb");
+		bool writeError = false;
+		if (!mcdb) {
+			LOG("error: unable to open membership cert database at: %s",mcdbPath.c_str());
+		} else {
+			if ((writeError)||(fwrite("MCDB0",5,1,mcdb) != 1)) // version
+				writeError = true;
+		}
+
 		for(std::map<Address,Certificate>::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) {
 		for(std::map<Address,Certificate>::iterator i=(_membershipCertificates.begin());i!=_membershipCertificates.end();) {
-			if (_myCertificate.qualifyMembership(i->second))
+			if (_myCertificate.qualifyMembership(i->second)) {
+				if ((!writeError)&&(mcdb)) {
+					char tmp[ZT_ADDRESS_LENGTH];
+					i->first.copyTo(tmp,ZT_ADDRESS_LENGTH);
+					if ((writeError)||(fwrite(tmp,ZT_ADDRESS_LENGTH,1,mcdb) != 1))
+						writeError = true;
+					std::string c(i->second.toString());
+					uint32_t cl = Utils::hton((uint32_t)c.length());
+					if ((writeError)||(fwrite(&cl,sizeof(cl),1,mcdb) != 1))
+						writeError = true;
+					if ((writeError)||(fwrite(c.data(),c.length(),1,mcdb) != 1))
+						writeError = true;
+				}
 				++i;
 				++i;
-			else _membershipCertificates.erase(i++);
+			} else _membershipCertificates.erase(i++);
+		}
+
+		if (mcdb)
+			fclose(mcdb);
+		if (writeError) {
+			unlink(mcdbPath.c_str());
+			LOG("error: unable to write to membership cert database at: %s",mcdbPath.c_str());
 		}
 		}
 	}
 	}
 }
 }

+ 25 - 8
node/Network.hpp

@@ -208,6 +208,11 @@ public:
 		{
 		{
 		}
 		}
 
 
+		inline bool containsAllFields() const
+		{
+			return (contains("nwid")&&contains("peer"));
+		}
+
 		inline std::string toString() const
 		inline std::string toString() const
 		{
 		{
 			return Dictionary::toString();
 			return Dictionary::toString();
@@ -241,7 +246,7 @@ public:
 		 */
 		 */
 		inline bool isOpen() const
 		inline bool isOpen() const
 		{
 		{
-			return (get("isOpen") == "1");
+			return (get("isOpen","0") == "1");
 		}
 		}
 
 
 		/**
 		/**
@@ -267,6 +272,14 @@ private:
 
 
 	~Network();
 	~Network();
 
 
+	/**
+	 * Causes all persistent disk presence to be erased on delete
+	 */
+	inline void destroyOnDelete()
+	{
+		_destroyOnDelete = true;
+	}
+
 public:
 public:
 	/**
 	/**
 	 * @return Network ID
 	 * @return Network ID
@@ -350,16 +363,16 @@ public:
 	 * @param peer Peer that owns certificate
 	 * @param peer Peer that owns certificate
 	 * @param cert Certificate itself
 	 * @param cert Certificate itself
 	 */
 	 */
-	inline void addMembershipCertificate(const Address &peer,const Certificate &cert)
-	{
-		Mutex::Lock _l(_lock);
-		_membershipCertificates[peer] = cert;
-	}
+	void addMembershipCertificate(const Address &peer,const Certificate &cert);
 
 
+	/**
+	 * @param peer Peer address to check
+	 * @return True if peer is allowed to communicate on this network
+	 */
 	bool isAllowed(const Address &peer) const;
 	bool isAllowed(const Address &peer) const;
 
 
 	/**
 	/**
-	 * Perform periodic database cleaning such as removing expired membership certificates
+	 * Perform cleanup and possibly save state
 	 */
 	 */
 	void clean();
 	void clean();
 
 
@@ -377,16 +390,20 @@ private:
 
 
 	const RuntimeEnvironment *_r;
 	const RuntimeEnvironment *_r;
 
 
+	// Tap and tap multicast memberships
 	EthernetTap _tap;
 	EthernetTap _tap;
-
 	std::set<MulticastGroup> _multicastGroups;
 	std::set<MulticastGroup> _multicastGroups;
+
+	// Membership certificates supplied by peers
 	std::map<Address,Certificate> _membershipCertificates;
 	std::map<Address,Certificate> _membershipCertificates;
 
 
+	// Configuration from network master node
 	Config _configuration;
 	Config _configuration;
 	Certificate _myCertificate;
 	Certificate _myCertificate;
 
 
 	uint64_t _id;
 	uint64_t _id;
 	volatile uint64_t _lastConfigUpdate;
 	volatile uint64_t _lastConfigUpdate;
+	volatile bool _destroyOnDelete;
 
 
 	Mutex _lock;
 	Mutex _lock;
 
 

+ 6 - 2
node/Node.cpp

@@ -44,6 +44,7 @@
 #include <unistd.h>
 #include <unistd.h>
 #include <signal.h>
 #include <signal.h>
 #include <sys/file.h>
 #include <sys/file.h>
+#include <sys/stat.h>
 #endif
 #endif
 
 
 #include "Condition.hpp"
 #include "Condition.hpp"
@@ -340,6 +341,9 @@ Node::ReasonForTermination Node::run()
 		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "status").c_str());
 		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "status").c_str());
 		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine").c_str());
 		unlink((_r->homePath + ZT_PATH_SEPARATOR_S + "thisdeviceismine").c_str());
 
 
+		// Make sure networks.d exists
+		mkdir((_r->homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str(),0700);
+
 		// Load or generate config authentication secret
 		// Load or generate config authentication secret
 		std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
 		std::string configAuthTokenPath(_r->homePath + ZT_PATH_SEPARATOR_S + "authtoken.secret");
 		std::string configAuthToken;
 		std::string configAuthToken;
@@ -504,7 +508,6 @@ Node::ReasonForTermination Node::run()
 							_r->topology->eachPeer(Topology::CollectPeersWithDirectPath(needPing));
 							_r->topology->eachPeer(Topology::CollectPeersWithDirectPath(needPing));
 						} else {
 						} else {
 							_r->topology->eachPeer(Topology::CollectPeersThatNeedPing(needPing));
 							_r->topology->eachPeer(Topology::CollectPeersThatNeedPing(needPing));
-							_r->topology->eachPeer(Topology::CollectPeersThatNeedFirewallOpener(needFirewallOpener));
 						}
 						}
 
 
 						for(std::vector< SharedPtr<Peer> >::iterator p(needPing.begin());p!=needPing.end();++p) {
 						for(std::vector< SharedPtr<Peer> >::iterator p(needPing.begin());p!=needPing.end();++p) {
@@ -517,6 +520,7 @@ Node::ReasonForTermination Node::run()
 							}
 							}
 						}
 						}
 
 
+						_r->topology->eachPeer(Topology::CollectPeersThatNeedFirewallOpener(needFirewallOpener));
 						for(std::vector< SharedPtr<Peer> >::iterator p(needFirewallOpener.begin());p!=needFirewallOpener.end();++p) {
 						for(std::vector< SharedPtr<Peer> >::iterator p(needFirewallOpener.begin());p!=needFirewallOpener.end();++p) {
 							try {
 							try {
 								(*p)->sendFirewallOpener(_r,now);
 								(*p)->sendFirewallOpener(_r,now);
@@ -537,7 +541,7 @@ Node::ReasonForTermination Node::run()
 			if ((now - lastClean) >= ZT_DB_CLEAN_PERIOD) {
 			if ((now - lastClean) >= ZT_DB_CLEAN_PERIOD) {
 				lastClean = now;
 				lastClean = now;
 				_r->topology->clean();
 				_r->topology->clean();
-				_r->nc->cleanAllNetworks();
+				_r->nc->clean();
 			}
 			}
 
 
 			try {
 			try {

+ 33 - 3
node/NodeConfig.cpp

@@ -72,7 +72,7 @@ void NodeConfig::whackAllTaps()
 		n->second->tap().whack();
 		n->second->tap().whack();
 }
 }
 
 
-void NodeConfig::cleanAllNetworks()
+void NodeConfig::clean()
 {
 {
 	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)
 	for(std::map< uint64_t,SharedPtr<Network> >::const_iterator n(_networks.begin());n!=_networks.end();++n)
@@ -145,9 +145,39 @@ std::vector<std::string> NodeConfig::execute(const char *command)
 				tmp.c_str());
 				tmp.c_str());
 		}
 		}
 	} else if (cmd[0] == "join") {
 	} else if (cmd[0] == "join") {
-		_P("404 join Not implemented yet.");
+		if (cmd.size() > 1) {
+			uint64_t nwid = strtoull(cmd[1].c_str(),(char **)0,16);
+			if (nwid > 0) {
+				Mutex::Lock _l(_networks_m);
+				try {
+					SharedPtr<Network> nw(new Network(_r,nwid));
+					_networks[nwid] = nw;
+					_P("200 join %.16llx OK",(unsigned long long)nwid);
+				} catch (std::exception &exc) {
+					_P("500 join %.16llx ERROR: %s",(unsigned long long)nwid,exc.what());
+				} catch ( ... ) {
+					_P("500 join %.16llx ERROR: (unknown exception)",(unsigned long long)nwid);
+				}
+			} else {
+				_P("400 join requires a network ID (>0) in hexadecimal format");
+			}
+		} else {
+			_P("400 join requires a network ID (>0) in hexadecimal format");
+		}
 	} else if (cmd[0] == "leave") {
 	} else if (cmd[0] == "leave") {
-		_P("404 leave Not implemented yet.");
+		if (cmd.size() > 1) {
+			Mutex::Lock _l(_networks_m);
+			uint64_t nwid = strtoull(cmd[1].c_str(),(char **)0,16);
+			std::map< uint64_t,SharedPtr<Network> >::iterator nw(_networks.find(nwid));
+			if (nw == _networks.end()) {
+				_P("404 leave %.16llx ERROR: not a member of that network",(unsigned long long)nwid);
+			} else {
+				nw->second->destroyOnDelete();
+				_networks.erase(nw);
+			}
+		} else {
+			_P("400 leave requires a network ID (>0) in hexadecimal format");
+		}
 	} else {
 	} else {
 		_P("404 %s No such command. Use 'help' for help.",cmd[0].c_str());
 		_P("404 %s No such command. Use 'help' for help.",cmd[0].c_str());
 	}
 	}

+ 2 - 2
node/NodeConfig.hpp

@@ -95,9 +95,9 @@ public:
 	void whackAllTaps();
 	void whackAllTaps();
 
 
 	/**
 	/**
-	 * Call clean() on all networks
+	 * Perform cleanup and possibly update saved state
 	 */
 	 */
-	void cleanAllNetworks();
+	void clean();
 
 
 	/**
 	/**
 	 * @param nwid Network ID
 	 * @param nwid Network ID

+ 1 - 0
node/PacketDecoder.cpp

@@ -610,6 +610,7 @@ bool PacketDecoder::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *_r,const
 	char tmp[128];
 	char tmp[128];
 	try {
 	try {
 		uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
 		uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
+		TRACE("NETWORK_CONFIG_REQUEST for %.16llx from %s",(unsigned long long)nwid,source().toString().c_str());
 #ifndef __WINDOWS__
 #ifndef __WINDOWS__
 		if (_r->netconfService) {
 		if (_r->netconfService) {
 			unsigned int dictLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);
 			unsigned int dictLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_DICT_LEN);

+ 2 - 2
node/Topology.hpp

@@ -200,7 +200,7 @@ public:
 
 
 		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 		{
 		{
-			if ((p->hasDirectPath())&&((_now - p->lastFirewallOpener()) >= ZT_FIREWALL_OPENER_DELAY))
+			if ((p->hasDirectPath())&&((_now - std::max(p->lastFirewallOpener(),p->lastDirectSend())) >= ZT_FIREWALL_OPENER_DELAY))
 				_v.push_back(p);
 				_v.push_back(p);
 		}
 		}
 
 
@@ -223,7 +223,7 @@ public:
 
 
 		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 		inline void operator()(Topology &t,const SharedPtr<Peer> &p)
 		{
 		{
-			if (((p->hasActiveDirectPath(_now))||(t.isSupernode(p->address())))&&((_now - p->lastDirectSend()) >= ZT_PEER_DIRECT_PING_DELAY))
+			if ( ((t.isSupernode(p->address()))&&((_now - p->lastDirectReceive()) >= ZT_PEER_DIRECT_PING_DELAY)) || ((p->hasActiveDirectPath(_now))&&((_now - p->lastDirectSend()) >= ZT_PEER_DIRECT_PING_DELAY)) )
 				_v.push_back(p);
 				_v.push_back(p);
 		}
 		}