mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-05-31 22:40:48 +00:00
Refactored network config chunking to sign every chunk to prevent stupid DOS attack potential, and implement network config fast propagate (though we probably will not use this for a bit).
This commit is contained in:
parent
236fdb450c
commit
15c07c58b6
@ -23,7 +23,6 @@
|
|||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Buffer.hpp"
|
#include "Buffer.hpp"
|
||||||
#include "Address.hpp"
|
#include "Address.hpp"
|
||||||
#include "C25519.hpp"
|
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
@ -444,49 +443,14 @@ public:
|
|||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sign this Dictionary, replacing any previous signature
|
|
||||||
*
|
|
||||||
* @param sigKey Key to use for signature in dictionary
|
|
||||||
* @param kp Key pair to sign with
|
|
||||||
*/
|
|
||||||
inline void wrapWithSignature(const char *sigKey,const C25519::Pair &kp)
|
|
||||||
{
|
|
||||||
this->erase(sigKey);
|
|
||||||
C25519::Signature sig(C25519::sign(kp,this->data(),this->sizeBytes()));
|
|
||||||
this->add(sigKey,reinterpret_cast<const char *>(sig.data),ZT_C25519_SIGNATURE_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify signature (and erase signature key)
|
|
||||||
*
|
|
||||||
* This erases this Dictionary's signature key (if present) and verifies
|
|
||||||
* the signature. The key is erased to render the Dictionary into the
|
|
||||||
* original unsigned form it was signed in for verification purposes.
|
|
||||||
*
|
|
||||||
* @param sigKey Key to use for signature in dictionary
|
|
||||||
* @param pk Public key to check against
|
|
||||||
* @return True if signature was present and valid
|
|
||||||
*/
|
|
||||||
inline bool unwrapAndVerify(const char *sigKey,const C25519::Public &pk)
|
|
||||||
{
|
|
||||||
char sig[ZT_C25519_SIGNATURE_LEN+1];
|
|
||||||
if (this->get(sigKey,sig,sizeof(sig)) != ZT_C25519_SIGNATURE_LEN)
|
|
||||||
return false;
|
|
||||||
this->erase(sigKey);
|
|
||||||
return C25519::verify(pk,this->data(),this->sizeBytes(),sig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Dictionary data as a 0-terminated C-string
|
|
||||||
*/
|
|
||||||
inline const char *data() const { return _d; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Value of C template parameter
|
* @return Value of C template parameter
|
||||||
*/
|
*/
|
||||||
inline unsigned int capacity() const { return C; }
|
inline unsigned int capacity() const { return C; }
|
||||||
|
|
||||||
|
inline const char *data() const { return _d; }
|
||||||
|
inline char *unsafeData() { return _d; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
char _d[C];
|
char _d[C];
|
||||||
};
|
};
|
||||||
|
@ -433,21 +433,9 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
|
|||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Packet::VERB_NETWORK_CONFIG_REQUEST: {
|
case Packet::VERB_NETWORK_CONFIG_REQUEST: {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST__OK__IDX_NETWORK_ID);
|
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD)));
|
||||||
const SharedPtr<Network> network(RR->node->network(nwid));
|
if (network)
|
||||||
if ((network)&&(network->controller() == peer->address())) {
|
network->handleConfigChunk(*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
|
||||||
trustEstablished = true;
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
TRACE("%s(%s): OK(NETWORK_CONFIG_REQUEST) chunkLen==%u chunkIndex==%u totalSize==%u",source().toString().c_str(),_path->address().toString().c_str(),chunkLen,chunkIndex,totalSize);
|
|
||||||
network->handleInboundConfigChunk(inRePacketId,chunkData,chunkLen,chunkIndex,totalSize);
|
|
||||||
}
|
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
//case Packet::VERB_ECHO: {
|
//case Packet::VERB_ECHO: {
|
||||||
@ -894,20 +882,31 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
|
|||||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
|
||||||
try {
|
try {
|
||||||
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
|
if (netconf->toDictionary(*dconf,metaData.getUI(ZT_NETWORKCONFIG_REQUEST_METADATA_KEY_VERSION,0) < 6)) {
|
||||||
dconf->wrapWithSignature(ZT_NETWORKCONFIG_DICT_KEY_SIGNATURE,RR->identity.privateKeyPair());
|
uint64_t configUpdateId = RR->node->prng();
|
||||||
|
if (!configUpdateId) ++configUpdateId;
|
||||||
const unsigned int totalSize = dconf->sizeBytes();
|
const unsigned int totalSize = dconf->sizeBytes();
|
||||||
unsigned int chunkIndex = 0;
|
unsigned int chunkIndex = 0;
|
||||||
while (chunkIndex < totalSize) {
|
while (chunkIndex < totalSize) {
|
||||||
const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_PROTO_MAX_PACKET_LENGTH - (ZT_PACKET_IDX_PAYLOAD + 32)));
|
const unsigned int chunkLen = std::min(totalSize - chunkIndex,(unsigned int)(ZT_UDP_DEFAULT_PAYLOAD_MTU - (ZT_PACKET_IDX_PAYLOAD + 256)));
|
||||||
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
|
||||||
outp.append(requestPacketId);
|
outp.append(requestPacketId);
|
||||||
|
|
||||||
|
const unsigned int sigStart = outp.size();
|
||||||
outp.append(nwid);
|
outp.append(nwid);
|
||||||
outp.append((uint16_t)chunkLen);
|
outp.append((uint16_t)chunkLen);
|
||||||
outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
|
outp.append((const void *)(dconf->data() + chunkIndex),chunkLen);
|
||||||
|
|
||||||
|
outp.append((uint8_t)0); // no flags
|
||||||
|
outp.append((uint64_t)configUpdateId);
|
||||||
outp.append((uint32_t)totalSize);
|
outp.append((uint32_t)totalSize);
|
||||||
outp.append((uint32_t)chunkIndex);
|
outp.append((uint32_t)chunkIndex);
|
||||||
|
|
||||||
|
C25519::Signature sig(RR->identity.sign(reinterpret_cast<const uint8_t *>(outp.data()) + sigStart,outp.size() - sigStart));
|
||||||
|
outp.append((uint8_t)1);
|
||||||
|
outp.append((uint16_t)ZT_C25519_SIGNATURE_LEN);
|
||||||
|
outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
|
||||||
|
|
||||||
outp.compress();
|
outp.compress();
|
||||||
RR->sw->send(outp,true);
|
RR->sw->send(outp,true);
|
||||||
chunkIndex += chunkLen;
|
chunkIndex += chunkLen;
|
||||||
@ -977,12 +976,21 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
|
|||||||
bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
|
const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
|
||||||
bool trustEstablished = false;
|
if (network) {
|
||||||
|
const uint64_t configUpdateId = network->handleConfigChunk(*this,ZT_PACKET_IDX_PAYLOAD);
|
||||||
|
if (configUpdateId) {
|
||||||
|
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
|
||||||
|
outp.append((uint8_t)Packet::VERB_ECHO);
|
||||||
|
outp.append((uint64_t)packetId());
|
||||||
|
outp.append((uint64_t)network->id());
|
||||||
|
outp.append((uint64_t)configUpdateId);
|
||||||
|
outp.armor(peer->key(),true);
|
||||||
|
_path->send(RR,outp.data(),outp.size(),RR->node->now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
|
||||||
|
|
||||||
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,trustEstablished);
|
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
|
||||||
}
|
}
|
||||||
|
165
node/Network.cpp
165
node/Network.cpp
@ -569,12 +569,14 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
|
|||||||
_lastAnnouncedMulticastGroupsUpstream(0),
|
_lastAnnouncedMulticastGroupsUpstream(0),
|
||||||
_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),
|
||||||
_portError(0)
|
_portError(0)
|
||||||
{
|
{
|
||||||
|
for(int i=0;i<ZT_NETWORK_MAX_INCOMING_UPDATES;++i)
|
||||||
|
_incomingConfigChunks[i].ts = 0;
|
||||||
|
|
||||||
char confn[128];
|
char confn[128];
|
||||||
Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
|
Utils::snprintf(confn,sizeof(confn),"networks.d/%.16llx.conf",_id);
|
||||||
|
|
||||||
@ -875,54 +877,133 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg)
|
|||||||
_myMulticastGroups.erase(i);
|
_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;
|
const unsigned int start = ptr;
|
||||||
if ((_inboundConfigPacketId == inRePacketId)&&(totalSize < ZT_NETWORKCONFIG_DICT_CAPACITY)&&((chunkIndex + chunkSize) <= totalSize)) {
|
|
||||||
Mutex::Lock _l(_lock);
|
|
||||||
|
|
||||||
_inboundConfigChunks[chunkIndex].append((const char *)data,chunkSize);
|
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;
|
||||||
|
|
||||||
unsigned int totalWeHave = 0;
|
Mutex::Lock _l(_lock);
|
||||||
for(std::map<unsigned int,std::string>::iterator c(_inboundConfigChunks.begin());c!=_inboundConfigChunks.end();++c)
|
|
||||||
totalWeHave += (unsigned int)c->second.length();
|
|
||||||
|
|
||||||
if (totalWeHave == totalSize) {
|
_IncomingConfigChunk *c = (_IncomingConfigChunk *)0;
|
||||||
TRACE("have all chunks for network config request %.16llx, assembling...",inRePacketId);
|
uint64_t chunkId = 0;
|
||||||
for(std::map<unsigned int,std::string>::iterator c(_inboundConfigChunks.begin());c!=_inboundConfigChunks.end();++c)
|
uint64_t configUpdateId;
|
||||||
newConfig.append(c->second);
|
unsigned long totalLength,chunkIndex;
|
||||||
_inboundConfigPacketId = 0;
|
if (ptr < chunk.size()) {
|
||||||
_inboundConfigChunks.clear();
|
const bool fastPropagate = ((chunk[ptr++] & 0x01) != 0);
|
||||||
} else if (totalWeHave > totalSize) {
|
configUpdateId = chunk.at<uint64_t>(ptr); ptr += 8;
|
||||||
_inboundConfigPacketId = 0;
|
totalLength = chunk.at<uint32_t>(ptr); ptr += 4;
|
||||||
_inboundConfigChunks.clear();
|
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;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((newConfig.length() > 0)&&(newConfig.length() < ZT_NETWORKCONFIG_DICT_CAPACITY)) {
|
if ((chunk[ptr] != 1)||(chunk.at<uint16_t>(ptr + 1) != ZT_C25519_SIGNATURE_LEN)) {
|
||||||
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(newConfig.c_str());
|
TRACE("discarded chunk from %s: unrecognized signature type",chunk.source().toString().c_str());
|
||||||
NetworkConfig *nc = new NetworkConfig();
|
return 0;
|
||||||
try {
|
}
|
||||||
Identity controllerId(RR->topology->getIdentity(this->controller()));
|
const uint8_t *sig = reinterpret_cast<const uint8_t *>(chunk.field(ptr + 3,ZT_C25519_SIGNATURE_LEN));
|
||||||
if (controllerId) {
|
|
||||||
if (nc->fromDictionary(*dict)) {
|
// We can use the signature, which is unique per chunk, to get a per-chunk ID for local deduplication use
|
||||||
Mutex::Lock _l(_lock);
|
for(unsigned int i=0;i<16;++i)
|
||||||
this->_setConfiguration(*nc,true);
|
reinterpret_cast<uint8_t *>(&chunkId)[i & 7] ^= sig[i];
|
||||||
} else {
|
|
||||||
TRACE("error parsing new config with length %u: deserialization of NetworkConfig failed (certificate error?)",(unsigned int)newConfig.length());
|
// 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 {
|
||||||
|
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 (c->haveBytes == totalLength) {
|
||||||
|
c->data.unsafeData()[c->haveBytes] = (char)0; // ensure null terminated
|
||||||
|
|
||||||
|
NetworkConfig *const nc = new NetworkConfig();
|
||||||
|
try {
|
||||||
|
if (nc->fromDictionary(c->data)) {
|
||||||
|
this->_setConfiguration(*nc,true);
|
||||||
|
return configUpdateId;
|
||||||
|
}
|
||||||
delete nc;
|
delete nc;
|
||||||
delete dict;
|
|
||||||
} catch ( ... ) {
|
} catch ( ... ) {
|
||||||
TRACE("error parsing new config with length %u: unexpected exception",(unsigned int)newConfig.length());
|
|
||||||
delete nc;
|
delete nc;
|
||||||
delete dict;
|
|
||||||
throw;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Network::requestConfiguration()
|
void Network::requestConfiguration()
|
||||||
@ -980,10 +1061,7 @@ void Network::requestConfiguration()
|
|||||||
} else {
|
} else {
|
||||||
outp.append((unsigned char)0,16);
|
outp.append((unsigned char)0,16);
|
||||||
}
|
}
|
||||||
|
RR->node->expectReplyTo(outp.packetId());
|
||||||
RR->node->expectReplyTo(_inboundConfigPacketId = outp.packetId());
|
|
||||||
_inboundConfigChunks.clear();
|
|
||||||
|
|
||||||
outp.compress();
|
outp.compress();
|
||||||
RR->sw->send(outp,true);
|
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);
|
const Membership::AddCredentialResult result = m.addCredential(RR,_config,rev);
|
||||||
|
|
||||||
if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
|
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;
|
Address *a = (Address *)0;
|
||||||
Membership *m = (Membership *)0;
|
Membership *m = (Membership *)0;
|
||||||
Hashtable<Address,Membership>::Iterator i(_memberships);
|
Hashtable<Address,Membership>::Iterator i(_memberships);
|
||||||
|
@ -44,6 +44,9 @@
|
|||||||
#include "NetworkConfig.hpp"
|
#include "NetworkConfig.hpp"
|
||||||
#include "CertificateOfMembership.hpp"
|
#include "CertificateOfMembership.hpp"
|
||||||
|
|
||||||
|
#define ZT_NETWORK_MAX_INCOMING_UPDATES 3
|
||||||
|
#define ZT_NETWORK_MAX_UPDATE_CHUNKS ((ZT_NETWORKCONFIG_DICT_CAPACITY / 1024) + 1)
|
||||||
|
|
||||||
namespace ZeroTier {
|
namespace ZeroTier {
|
||||||
|
|
||||||
class RuntimeEnvironment;
|
class RuntimeEnvironment;
|
||||||
@ -174,16 +177,15 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Handle an inbound network config chunk
|
* Handle an inbound network config chunk
|
||||||
*
|
*
|
||||||
* This is called from IncomingPacket when we receive a chunk from a network
|
* This is called from IncomingPacket to handle incoming network config
|
||||||
* controller.
|
* chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
|
||||||
|
* each chunk and once assembled applies the configuration.
|
||||||
*
|
*
|
||||||
* @param requestId An ID for grouping chunks, e.g. in-re packet ID for OK(NETWORK_CONFIG_REQUEST)
|
* @param chunk Packet containing chunk
|
||||||
* @param data Chunk data
|
* @param ptr Index of chunk and related fields in packet
|
||||||
* @param chunkSize Size of data[]
|
* @return Update ID if update was fully assembled and accepted or 0 otherwise
|
||||||
* @param chunkIndex Index of chunk in full config
|
|
||||||
* @param totalSize Total size of network config
|
|
||||||
*/
|
*/
|
||||||
void handleInboundConfigChunk(const uint64_t requestId,const void *data,unsigned int chunkSize,unsigned int chunkIndex,unsigned int totalSize);
|
uint64_t handleConfigChunk(const Packet &chunk,unsigned int ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
@ -353,19 +355,27 @@ private:
|
|||||||
const uint64_t _id;
|
const uint64_t _id;
|
||||||
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
uint64_t _lastAnnouncedMulticastGroupsUpstream;
|
||||||
MAC _mac; // local MAC address
|
MAC _mac; // local MAC address
|
||||||
volatile bool _portInitialized;
|
bool _portInitialized;
|
||||||
|
|
||||||
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
std::vector< MulticastGroup > _myMulticastGroups; // multicast groups that we belong to (according to tap)
|
||||||
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;
|
uint64_t _lastConfigUpdate;
|
||||||
|
|
||||||
volatile bool _destroyed;
|
struct _IncomingConfigChunk
|
||||||
|
{
|
||||||
|
uint64_t ts;
|
||||||
|
uint64_t updateId;
|
||||||
|
uint64_t haveChunkIds[ZT_NETWORK_MAX_UPDATE_CHUNKS];
|
||||||
|
unsigned long haveChunks;
|
||||||
|
unsigned long haveBytes;
|
||||||
|
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> data;
|
||||||
|
};
|
||||||
|
_IncomingConfigChunk _incomingConfigChunks[ZT_NETWORK_MAX_INCOMING_UPDATES];
|
||||||
|
|
||||||
|
bool _destroyed;
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
NETCONF_FAILURE_NONE,
|
NETCONF_FAILURE_NONE,
|
||||||
@ -373,7 +383,7 @@ private:
|
|||||||
NETCONF_FAILURE_NOT_FOUND,
|
NETCONF_FAILURE_NOT_FOUND,
|
||||||
NETCONF_FAILURE_INIT_FAILED
|
NETCONF_FAILURE_INIT_FAILED
|
||||||
} _netconfFailure;
|
} _netconfFailure;
|
||||||
volatile int _portError; // return value from port config callback
|
int _portError; // return value from port config callback
|
||||||
|
|
||||||
Hashtable<Address,Membership> _memberships;
|
Hashtable<Address,Membership> _memberships;
|
||||||
|
|
||||||
|
@ -755,8 +755,26 @@ public:
|
|||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[2] 16-bit length of network configuration dictionary chunk>
|
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||||
* <[...] network configuration dictionary (may be incomplete)>
|
* <[...] network configuration dictionary (may be incomplete)>
|
||||||
|
* [ ... end of legacy single chunk response ... ]
|
||||||
|
* <[1] 8-bit flags>
|
||||||
|
* <[8] 64-bit config update ID (should never be 0)>
|
||||||
* <[4] 32-bit total length of assembled dictionary>
|
* <[4] 32-bit total length of assembled dictionary>
|
||||||
* <[4] 32-bit index of chunk in this reply>
|
* <[4] 32-bit index of chunk>
|
||||||
|
* [ ... end signed portion ... ]
|
||||||
|
* <[1] 8-bit chunk signature type>
|
||||||
|
* <[2] 16-bit length of chunk signature>
|
||||||
|
* <[...] chunk signature>
|
||||||
|
*
|
||||||
|
* The chunk signature signs the entire payload of the OK response.
|
||||||
|
* Currently only one signature type is supported: ed25519 (1).
|
||||||
|
*
|
||||||
|
* Each config chunk is signed to prevent memory exhaustion or
|
||||||
|
* traffic crowding DOS attacks against config fragment assembly.
|
||||||
|
*
|
||||||
|
* If the packet is from the network controller it is permitted to end
|
||||||
|
* before the config update ID or other chunking related or signature
|
||||||
|
* fields. This is to support older controllers that don't include
|
||||||
|
* these fields and may be removed in the future.
|
||||||
*
|
*
|
||||||
* ERROR response payload:
|
* ERROR response payload:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
@ -766,25 +784,30 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Network configuration data push:
|
* Network configuration data push:
|
||||||
* <[8] 64-bit network ID>
|
* <[8] 64-bit network ID>
|
||||||
* <[8] 64-bit config update ID (token to identify this update)>
|
|
||||||
* <[1] flags>
|
|
||||||
* <[2] 16-bit length of network configuration dictionary chunk>
|
* <[2] 16-bit length of network configuration dictionary chunk>
|
||||||
* <[...] network configuration dictionary (may be incomplete)>
|
* <[...] network configuration dictionary (may be incomplete)>
|
||||||
|
* <[1] 8-bit flags>
|
||||||
|
* <[8] 64-bit config update ID (should never be 0)>
|
||||||
* <[4] 32-bit total length of assembled dictionary>
|
* <[4] 32-bit total length of assembled dictionary>
|
||||||
* <[4] 32-bit index of chunk in this reply>
|
* <[4] 32-bit index of chunk>
|
||||||
|
* [ ... end signed portion ... ]
|
||||||
|
* <[1] 8-bit chunk signature type>
|
||||||
|
* <[2] 16-bit length of chunk signature>
|
||||||
|
* <[...] chunk signature>
|
||||||
*
|
*
|
||||||
* This is a direct push variant for network config updates. It otherwise
|
* This is a direct push variant for network config updates. It otherwise
|
||||||
* carries the same payload as OK(NETWORK_CONFIG_REQUEST). There is an
|
* carries the same payload as OK(NETWORK_CONFIG_REQUEST) and has the same
|
||||||
* extra number after network ID in this version that is used in place of
|
* semantics.
|
||||||
* the in-re packet ID sent with OKs to group chunks together.
|
|
||||||
*
|
|
||||||
* Unlike OK(NETWORK_CONFIG_REQUEST) this can be sent by peers other than
|
|
||||||
* network controllers. In that case the certificate inside the Dictionary
|
|
||||||
* is used for verification purposes.
|
|
||||||
*
|
*
|
||||||
* Flags:
|
* Flags:
|
||||||
* 0x01 - Patch, not whole config
|
* 0x01 - Use fast propagation
|
||||||
* 0x02 - Use fast P2P propagation
|
*
|
||||||
|
* An OK should be sent if the config is successfully received and
|
||||||
|
* accepted.
|
||||||
|
*
|
||||||
|
* OK payload:
|
||||||
|
* <[8] 64-bit network ID>
|
||||||
|
* <[8] 64-bit config update ID>
|
||||||
*/
|
*/
|
||||||
VERB_NETWORK_CONFIG = 0x0c,
|
VERB_NETWORK_CONFIG = 0x0c,
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user