diff options
Diffstat (limited to 'node/Network.cpp')
| -rw-r--r-- | node/Network.cpp | 167 |
1 files changed, 119 insertions, 48 deletions
diff --git a/node/Network.cpp b/node/Network.cpp index 487766a7..e24e3e16 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -569,12 +569,14 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) : _lastAnnouncedMulticastGroupsUpstream(0), _mac(renv->identity.address(),nwid), _portInitialized(false), - _inboundConfigPacketId(0), _lastConfigUpdate(0), _destroyed(false), _netconfFailure(NETCONF_FAILURE_NONE), _portError(0) { + for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) + _incomingConfigChunks[i].ts = 0; + char confn[128]; Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id); @@ -875,54 +877,133 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg) _myMulticastGroups.erase(i); } -void Network::handleInboundConfigChunk(const uint64_t inRePacketId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize) +uint64_t Network::handleConfigChunk(const Packet &chunk,unsigned int ptr) { - std::string newConfig; - if ((_inboundConfigPacketId == inRePacketId)&&(totalSize < ZT_NETWORKCONFIG_DICT_CAPACITY)&&((chunkIndex + chunkSize) <= totalSize)) { - Mutex::Lock _l(_lock); - - _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(); + const unsigned int start = ptr; + + ptr += 8; // skip network ID, which is already obviously known + const uint16_t chunkLen = chunk.at<uint16_t>(ptr); ptr += 2; + const void *chunkData = chunk.field(ptr,chunkLen); ptr += chunkLen; + + Mutex::Lock _l(_lock); + + _IncomingConfigChunk *c = (_IncomingConfigChunk *)0; + uint64_t chunkId = 0; + uint64_t configUpdateId; + unsigned long totalLength,chunkIndex; + if (ptr < chunk.size()) { + const bool fastPropagate = ((chunk[ptr++] & 0x01) != 0); + configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8; + totalLength = chunk.at<uint32_t>(ptr); ptr += 4; + chunkIndex = chunk.at<uint32_t>(ptr); ptr += 4; + + if (((chunkIndex + chunkLen) > totalLength)||(totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY)) { // >= since we need room for a null at the end + TRACE("discarded chunk from %s: invalid length or length overflow",chunk.source().toString().c_str()); + return 0; + } + + if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN)) { + TRACE("discarded chunk from %s: unrecognized signature type",chunk.source().toString().c_str()); + return 0; + } + const uint8_t *sig = reinterpret_cast<const uint8_t *>(chunk.field(ptr + 3,ZT_C25519_SIGNATURE_LEN)); + + // We can use the signature, which is unique per chunk, to get a per-chunk ID for local deduplication use + for(unsigned int i=0;i<16;++i) + reinterpret_cast<uint8_t *>(&chunkId)[i & 7] ^= sig[i]; + + // Find existing or new slot for this update and check if this is a duplicate chunk + for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) { + if (_incomingConfigChunks[i].updateId == configUpdateId) { + c = &(_incomingConfigChunks[i]); + + for(unsigned long j=0;j<c->haveChunks;++j) { + if (c->haveChunkIds[j] == chunkId) + return 0; + } + + break; + } else if ((!c)||(_incomingConfigChunks[i].ts < c->ts)) { + c = &(_incomingConfigChunks[i]); + } + } + + // If it's not a duplicate, check chunk signature + const Identity controllerId(RR->topology->getIdentity(controller())); + if (!controllerId) { // we should always have the controller identity by now, otherwise how would we have queried it the first time? + TRACE("unable to verify chunk from %s: don't have controller identity",chunk.source().toString().c_str()); + return 0; + } + if (!controllerId.verify(chunk.field(start,ptr - start),ptr - start,sig,ZT_C25519_SIGNATURE_LEN)) { + TRACE("discarded chunk from %s: signature check failed",chunk.source().toString().c_str()); + return 0; + } + + // New properly verified chunks can be flooded "virally" through the network + if (fastPropagate) { + Address *a = (Address *)0; + Membership *m = (Membership *)0; + Hashtable<Address,Membership>::Iterator i(_memberships); + while (i.next(a,m)) { + if ((*a != chunk.source())&&(*a != controller())) { + Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CONFIG); + outp.append(reinterpret_cast<const uint8_t *>(chunk.data()) + start,chunk.size() - start); + RR->sw->send(outp,true); + } + } + } + } else if (chunk.source() == controller()) { + // Legacy support for OK(NETWORK_CONFIG_REQUEST) from older controllers + chunkId = chunk.packetId(); + configUpdateId = chunkId; + totalLength = chunkLen; + chunkIndex = 0; + + if (totalLength >= ZT_NETWORKCONFIG_DICT_CAPACITY) + return 0; + + // Find oldest slot for this udpate to use buffer space + for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i) { + if ((!c)||(_incomingConfigChunks[i].ts < c->ts)) + c = &(_incomingConfigChunks[i]); } } else { - return; + TRACE("discarded single-chunk unsigned legacy config: this is only allowed if the sender is the controller itself"); + return 0; + } + + ++c->ts; // newer is higher, that's all we need + + if (c->updateId != configUpdateId) { + c->updateId = configUpdateId; + for(int i=0;i<ZT_NETWORK_MAX_UPDATE_CHUNKS;++i) + c->haveChunkIds[i] = 0; + c->haveChunks = 0; + c->haveBytes = 0; } + if (c->haveChunks >= ZT_NETWORK_MAX_UPDATE_CHUNKS) + return false; + c->haveChunkIds[c->haveChunks++] = chunkId; + + memcpy(c->data.unsafeData() + chunkIndex,chunkData,chunkLen); + c->haveBytes += chunkLen; - if ((newConfig.length() > 0)&&(newConfig.length() < ZT_NETWORKCONFIG_DICT_CAPACITY)) { - Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(newConfig.c_str()); - NetworkConfig *nc = new NetworkConfig(); + if (c->haveBytes == totalLength) { + c->data.unsafeData()[c->haveBytes] = (char)0; // ensure null terminated + + NetworkConfig *const nc = new NetworkConfig(); try { - Identity controllerId(RR->topology->getIdentity(this->controller())); - if (controllerId) { - if (nc->fromDictionary(*dict)) { - Mutex::Lock _l(_lock); - this->_setConfiguration(*nc,true); - } else { - TRACE("error parsing new config with length %u: deserialization of NetworkConfig failed (certificate error?)",(unsigned int)newConfig.length()); - } + if (nc->fromDictionary(c->data)) { + this->_setConfiguration(*nc,true); + return configUpdateId; } delete nc; - delete dict; } catch ( ... ) { - TRACE("error parsing new config with length %u: unexpected exception",(unsigned int)newConfig.length()); delete nc; - delete dict; - throw; } } + + return 0; } void Network::requestConfiguration() @@ -980,10 +1061,7 @@ void Network::requestConfiguration() } else { outp.append((unsigned char)0,16); } - - RR->node->expectReplyTo(_inboundConfigPacketId = outp.packetId()); - _inboundConfigChunks.clear(); - + RR->node->expectReplyTo(outp.packetId()); outp.compress(); RR->sw->send(outp,true); } @@ -1127,13 +1205,6 @@ Membership::AddCredentialResult Network::addCredential(const Address &sentFrom,c const Membership::AddCredentialResult result = m.addCredential(RR,_config,rev); if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) { - /* Fast propagation is done by using a very aggressive rumor mill - * propagation algorithm. When we see a Revocation that we haven't - * seen before we blast it to every known member. This leads to - * a huge number of redundant messages, but eventually everybody - * will get it. This helps revocation speed and also helps in cases - * where the controller is under attack. It need only get one - * revocation out and the rest is history. */ Address *a = (Address *)0; Membership *m = (Membership *)0; Hashtable<Address,Membership>::Iterator i(_memberships); |
