Browse Source

Handling of multi-part chunked network configs on the inbound side.

Adam Ierymenko 9 years ago
parent
commit
4d498b3765
6 changed files with 93 additions and 12 deletions
  1. 5 0
      node/Identity.hpp
  2. 8 8
      node/IncomingPacket.cpp
  3. 47 1
      node/Network.cpp
  4. 18 0
      node/Network.hpp
  5. 11 1
      node/NetworkConfig.cpp
  6. 4 2
      node/NetworkConfig.hpp

+ 5 - 0
node/Identity.hpp

@@ -282,6 +282,11 @@ public:
 	bool fromString(const char *str);
 	bool fromString(const char *str);
 	inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
 	inline bool fromString(const std::string &str) { return fromString(str.c_str()); }
 
 
+	/**
+	 * @return C25519 public key
+	 */
+	inline const C25519::Public &publicKey() const { return _publicKey; }
+
 	/**
 	/**
 	 * @return True if this identity contains something
 	 * @return True if this identity contains something
 	 */
 	 */

+ 8 - 8
node/IncomingPacket.cpp

@@ -402,15 +402,15 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 			case Packet::VERB_NETWORK_CONFIG_REQUEST: {
 			case Packet::VERB_NETWORK_CONFIG_REQUEST: {
 				const SharedPtr<Network> nw(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID)));
 				const SharedPtr<Network> nw(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID)));
 				if ((nw)&&(nw->controller() == peer->address())) {
 				if ((nw)&&(nw->controller() == peer->address())) {
-					const unsigned int nclen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
-					if (nclen) {
-						Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> dconf((const char *)field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,nclen),nclen);
-						NetworkConfig nconf;
-						if (nconf.fromDictionary(dconf)) {
-							nw->setConfiguration(nconf,true);
-							TRACE("got network configuration for network %.16llx from %s",(unsigned long long)nw->id(),source().toString().c_str());
-						}
+					const unsigned int chunkLen = at<uint16_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT_LEN);
+					const void *chunkData = field(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT,chunkLen);
+					unsigned int chunkIndex = 0;
+					unsigned int totalSize = chunkLen;
+					if ((ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT + chunkLen) < size()) {
+						totalSize = at<uint32_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT + chunkLen);
+						chunkIndex = at<uint32_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_DICT + chunkLen + 4);
 					}
 					}
+					nw->handleInboundConfigChunk(inRePacketId,chunkData,chunkLen,chunkIndex,totalSize);
 				}
 				}
 			}	break;
 			}	break;
 
 

+ 47 - 1
node/Network.cpp

@@ -343,6 +343,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
 	_id(nwid),
 	_id(nwid),
 	_mac(renv->identity.address(),nwid),
 	_mac(renv->identity.address(),nwid),
 	_portInitialized(false),
 	_portInitialized(false),
+	_inboundConfigPacketId(0),
 	_lastConfigUpdate(0),
 	_lastConfigUpdate(0),
 	_destroyed(false),
 	_destroyed(false),
 	_netconfFailure(NETCONF_FAILURE_NONE),
 	_netconfFailure(NETCONF_FAILURE_NONE),
@@ -364,7 +365,7 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
 			std::string conf(RR->node->dataStoreGet(confn));
 			std::string conf(RR->node->dataStoreGet(confn));
 			if (conf.length()) {
 			if (conf.length()) {
 				dconf->load(conf.c_str());
 				dconf->load(conf.c_str());
-				if (nconf->fromDictionary(*dconf)) {
+				if (nconf->fromDictionary(Identity(),*dconf)) {
 					this->setConfiguration(*nconf,false);
 					this->setConfiguration(*nconf,false);
 					_lastConfigUpdate = 0; // we still want to re-request a new config from the network
 					_lastConfigUpdate = 0; // we still want to re-request a new config from the network
 					gotConf = true;
 					gotConf = true;
@@ -589,6 +590,47 @@ int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
 	return 0;
 	return 0;
 }
 }
 
 
+void Network::handleInboundConfigChunk(const uint64_t inRePacketId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize)
+{
+	std::string newConfig;
+	if ((_inboundConfigPacketId == inRePacketId)&&(totalSize < ZT_NETWORKCONFIG_DICT_CAPACITY)&&((chunkIndex + chunkSize) < totalSize)) {
+		Mutex::Lock _l(_lock);
+		TRACE("got %u bytes at position %u of network config request %.16llx, total expected length %u",chunkSize,chunkIndex,inRePacketId,totalSize);
+		_inboundConfigChunks[chunkIndex].append((const char *)data,chunkSize);
+		unsigned int totalWeHave = 0;
+		for(std::map<unsigned int,std::string>::iterator c(_inboundConfigChunks.begin());c!=_inboundConfigChunks.end();++c)
+			totalWeHave += (unsigned int)c->second.length();
+		if (totalWeHave == totalSize) {
+			TRACE("have all chunks for network config request %.16llx, assembling...",inRePacketId);
+			for(std::map<unsigned int,std::string>::iterator c(_inboundConfigChunks.begin());c!=_inboundConfigChunks.end();++c)
+				newConfig.append(c->second);
+			_inboundConfigPacketId = 0;
+			_inboundConfigChunks.clear();
+		} else if (totalWeHave > totalSize) {
+			_inboundConfigPacketId = 0;
+			_inboundConfigChunks.clear();
+		}
+	}
+
+	if (newConfig.length() > 0) {
+		if (newConfig.length() < ZT_NETWORKCONFIG_DICT_CAPACITY) {
+			Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(newConfig.c_str());
+			try {
+				Identity controllerId(RR->topology->getIdentity(this->controller()));
+				if (controllerId) {
+					NetworkConfig nc;
+					if (nc.fromDictionary(controllerId,*dict))
+						this->setConfiguration(nc,true);
+				}
+				delete dict;
+			} catch ( ... ) {
+				delete dict;
+				throw;
+			}
+		}
+	}
+}
+
 void Network::requestConfiguration()
 void Network::requestConfiguration()
 {
 {
 	if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config
 	if (_id == ZT_TEST_NETWORK_ID) // pseudo-network-ID, uses locally generated static config
@@ -637,6 +679,10 @@ void Network::requestConfiguration()
 	outp.append((_config) ? (uint64_t)_config.revision : (uint64_t)0);
 	outp.append((_config) ? (uint64_t)_config.revision : (uint64_t)0);
 	outp.compress();
 	outp.compress();
 	RR->sw->send(outp,true,0);
 	RR->sw->send(outp,true,0);
+
+	// Expect replies with this in-re packet ID
+	_inboundConfigPacketId = outp.packetId();
+	_inboundConfigChunks.clear();
 }
 }
 
 
 void Network::clean()
 void Network::clean()

+ 18 - 0
node/Network.hpp

@@ -214,6 +214,21 @@ public:
 	 */
 	 */
 	int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
 	int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
 
 
+	/**
+	 * Handle an inbound network config chunk
+	 *
+	 * Only chunks whose inRePacketId matches the packet ID of the last request
+	 * are handled. If this chunk completes the config, it is decoded and
+	 * setConfiguration() is called.
+	 *
+	 * @param inRePacketId In-re packet ID from OK(NETWORK_CONFIG_REQUEST)
+	 * @param data Chunk data
+	 * @param chunkSize Size of data[]
+	 * @param chunkIndex Index of chunk in full config
+	 * @param totalSize Total size of network config
+	 */
+	void handleInboundConfigChunk(const uint64_t inRePacketId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize);
+
 	/**
 	/**
 	 * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
 	 * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
 	 */
 	 */
@@ -411,6 +426,9 @@ private:
 	Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
 	Hashtable< MulticastGroup,uint64_t > _multicastGroupsBehindMe; // multicast groups that seem to be behind us and when we last saw them (if we are a bridge)
 	Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
 	Hashtable< MAC,Address > _remoteBridgeRoutes; // remote addresses where given MACs are reachable (for tracking devices behind remote bridges)
 
 
+	uint64_t _inboundConfigPacketId;
+	std::map<unsigned int,std::string> _inboundConfigChunks;
+
 	NetworkConfig _config;
 	NetworkConfig _config;
 	volatile uint64_t _lastConfigUpdate;
 	volatile uint64_t _lastConfigUpdate;
 
 

+ 11 - 1
node/NetworkConfig.cpp

@@ -178,8 +178,18 @@ bool NetworkConfig::toDictionary(Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d,b
 	return true;
 	return true;
 }
 }
 
 
-bool NetworkConfig::fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
+bool NetworkConfig::fromDictionary(const Identity &controllerId,Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d)
 {
 {
+	if ((d.contains(ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE))&&(controllerId)) {
+		// FIXME: right now signature are optional since network configs are only
+		// accepted directly from the controller and the protocol already guarantees
+		// the sender. In the future these might be made non-optional once old
+		// controllers that do not sign are gone and if we ever support peer caching
+		// of network configs.
+		if (!d.unwrapAndVerify(ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE,controllerId.publicKey()))
+			return false;
+	}
+
 	Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
 	Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY> *tmp = new Buffer<ZT_NETWORKCONFIG_DICT_CAPACITY>();
 
 
 	try {
 	try {

+ 4 - 2
node/NetworkConfig.hpp

@@ -38,6 +38,7 @@
 #include "Capability.hpp"
 #include "Capability.hpp"
 #include "Tag.hpp"
 #include "Tag.hpp"
 #include "Dictionary.hpp"
 #include "Dictionary.hpp"
+#include "Identity.hpp"
 
 
 /**
 /**
  * Flag: allow passive bridging (experimental)
  * Flag: allow passive bridging (experimental)
@@ -239,10 +240,11 @@ public:
 	/**
 	/**
 	 * Read this network config from a dictionary
 	 * Read this network config from a dictionary
 	 *
 	 *
-	 * @param d Dictionary
+	 * @param controllerId Controller identity for verification of any signature or NULL identity to skip
+	 * @param d Dictionary (non-const since it might be modified during parse, should not be used after call)
 	 * @return True if dictionary was valid and network config successfully initialized
 	 * @return True if dictionary was valid and network config successfully initialized
 	 */
 	 */
-	bool fromDictionary(const Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
+	bool fromDictionary(const Identity &controllerId,Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> &d);
 
 
 	/**
 	/**
 	 * @return True if passive bridging is allowed (experimental)
 	 * @return True if passive bridging is allowed (experimental)