summaryrefslogtreecommitdiff
path: root/node/IncomingPacket.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/IncomingPacket.cpp')
-rw-r--r--node/IncomingPacket.cpp383
1 files changed, 148 insertions, 235 deletions
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index b3925773..5afacd0e 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -39,6 +39,7 @@
#include "CertificateOfMembership.hpp"
#include "Capability.hpp"
#include "Tag.hpp"
+#include "Revocation.hpp"
namespace ZeroTier {
@@ -66,7 +67,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
return _doHELLO(RR,false);
}
- SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
+ const SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
if (peer) {
if (!trusted) {
if (!dearmor(peer->key())) {
@@ -99,13 +100,12 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
case Packet::VERB_MULTICAST_LIKE: return _doMULTICAST_LIKE(RR,peer);
case Packet::VERB_NETWORK_CREDENTIALS: return _doNETWORK_CREDENTIALS(RR,peer);
case Packet::VERB_NETWORK_CONFIG_REQUEST: return _doNETWORK_CONFIG_REQUEST(RR,peer);
- case Packet::VERB_NETWORK_CONFIG_REFRESH: return _doNETWORK_CONFIG_REFRESH(RR,peer);
+ case Packet::VERB_NETWORK_CONFIG: return _doNETWORK_CONFIG(RR,peer);
case Packet::VERB_MULTICAST_GATHER: return _doMULTICAST_GATHER(RR,peer);
case Packet::VERB_MULTICAST_FRAME: return _doMULTICAST_FRAME(RR,peer);
case Packet::VERB_PUSH_DIRECT_PATHS: return _doPUSH_DIRECT_PATHS(RR,peer);
case Packet::VERB_CIRCUIT_TEST: return _doCIRCUIT_TEST(RR,peer);
case Packet::VERB_CIRCUIT_TEST_REPORT: return _doCIRCUIT_TEST_REPORT(RR,peer);
- case Packet::VERB_REQUEST_PROOF_OF_WORK: return _doREQUEST_PROOF_OF_WORK(RR,peer);
case Packet::VERB_USER_MESSAGE:
return true;
}
@@ -130,12 +130,18 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
//TRACE("ERROR %s from %s(%s) in-re %s",Packet::errorString(errorCode),peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb));
+ /* Security note: we do not gate doERROR() with expectingReplyTo() to
+ * avoid having to log every outgoing packet ID. Instead we put the
+ * logic to determine whether we should consider an ERROR in each
+ * error handler. In most cases these are only trusted in specific
+ * circumstances. */
+
switch(errorCode) {
case Packet::ERROR_OBJ_NOT_FOUND:
// Object not found, currently only meaningful from network controllers.
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setNotFound();
}
@@ -146,7 +152,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
// consider it meaningful from network controllers. This would indicate
// that the queried node does not support acting as a controller.
if (inReVerb == Packet::VERB_NETWORK_CONFIG_REQUEST) {
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setNotFound();
}
@@ -160,20 +166,15 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
// Peers can send this in response to frames if they do not have a recent enough COM from us
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
const uint64_t now = RR->node->now();
- if ( (network) && (network->config().com) && (peer->rateGateComRequest(now)) ) {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
- network->config().com.serialize(outp);
- outp.append((uint8_t)0);
- outp.armor(peer->key(),true);
- _path->send(RR,outp.data(),outp.size(),now);
- }
+ if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
+ network->pushCredentialsNow(peer->address(),now);
} break;
case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
// Network controller: network access denied.
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
if ((network)&&(network->controller() == peer->address()))
network->setAccessDenied();
} break;
@@ -181,9 +182,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
case Packet::ERROR_UNWANTED_MULTICAST: {
// Members of networks can use this error to indicate that they no longer
// want to receive multicasts on a given channel.
- SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
- if ((network)&&(network->gate(peer,verb(),packetId()))) {
- MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
+ if ((network)&&(network->gate(peer))) {
+ const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
RR->mc->remove(network->id(),mg,peer->address());
}
@@ -373,9 +374,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
try {
const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
const uint64_t inRePacketId = at<uint64_t>(ZT_PROTO_VERB_OK_IDX_IN_RE_PACKET_ID);
- bool trustEstablished = false;
- // Don't parse OK packets that are not in response to a packet ID we sent
if (!RR->node->expectingReplyTo(inRePacketId)) {
TRACE("%s(%s): OK(%s) DROPPED: not expecting reply to %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),Packet::verbString(inReVerb),packetId());
return true;
@@ -424,39 +423,23 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
RR->sa->iam(peer->address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
} break;
- case Packet::VERB_WHOIS: {
+ case Packet::VERB_WHOIS:
if (RR->topology->isUpstream(peer->identity())) {
const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
}
- } break;
+ break;
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(nwid));
- if ((network)&&(network->controller() == peer->address())) {
- 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);
- }
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD)));
+ if (network)
+ network->handleConfigChunk(*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
} break;
- //case Packet::VERB_ECHO: {
- //} break;
-
case Packet::VERB_MULTICAST_GATHER: {
const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_NETWORK_ID);
- SharedPtr<Network> network(RR->node->network(nwid));
- if ((network)&&(network->gateMulticastGatherReply(peer,verb(),packetId()))) {
- trustEstablished = true;
+ const SharedPtr<Network> network(RR->node->network(nwid));
+ if (network) {
const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
@@ -471,7 +454,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
//TRACE("%s(%s): OK(MULTICAST_FRAME) %.16llx/%s flags %.2x",peer->address().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),flags);
- SharedPtr<Network> network(RR->node->network(nwid));
+ const SharedPtr<Network> network(RR->node->network(nwid));
if (network) {
unsigned int offset = 0;
@@ -482,15 +465,12 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
network->addCredential(com);
}
- if (network->gateMulticastGatherReply(peer,verb(),packetId())) {
- trustEstablished = true;
- if ((flags & 0x02) != 0) {
- // OK(MULTICAST_FRAME) includes implicit gather results
- offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
- unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
- unsigned int count = at<uint16_t>(offset); offset += 2;
- RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
- }
+ if ((flags & 0x02) != 0) {
+ // OK(MULTICAST_FRAME) includes implicit gather results
+ offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
+ unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
+ unsigned int count = at<uint16_t>(offset); offset += 2;
+ RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
}
}
} break;
@@ -498,7 +478,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
default: break;
}
- peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,trustEstablished);
+ peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
} catch ( ... ) {
TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
}
@@ -595,9 +575,7 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
const SharedPtr<Network> network(RR->node->network(nwid));
bool trustEstablished = false;
if (network) {
- if (!network->gate(peer,verb(),packetId())) {
- TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
- } else {
+ if (network->gate(peer)) {
trustEstablished = true;
if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
@@ -607,9 +585,13 @@ bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer>
if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
RR->node->putFrame(nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
}
+ } else {
+ TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
+ _sendErrorNeedCredentials(RR,peer,nwid);
}
} else {
TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+ _sendErrorNeedCredentials(RR,peer,nwid);
}
peer->received(_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
} catch ( ... ) {
@@ -634,8 +616,9 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
network->addCredential(com);
}
- if (!network->gate(peer,verb(),packetId())) {
+ if (!network->gate(peer)) {
TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
+ _sendErrorNeedCredentials(RR,peer,nwid);
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
return true;
}
@@ -681,11 +664,21 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
RR->node->putFrame(nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
break;
}
+ }
- peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
+ if ((flags & 0x10) != 0) {
+ Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
+ outp.append((uint8_t)Packet::VERB_EXT_FRAME);
+ outp.append((uint64_t)packetId());
+ outp.append((uint64_t)nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,outp.data(),outp.size(),RR->node->now());
}
+
+ peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
} else {
TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
+ _sendErrorNeedCredentials(RR,peer,nwid);
peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
}
} catch ( ... ) {
@@ -723,7 +716,7 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
try {
const uint64_t now = RR->node->now();
- uint64_t authOnNetwork[256];
+ uint64_t authOnNetwork[256]; // cache for approved network IDs
unsigned int authOnNetworkCount = 0;
SharedPtr<Network> network;
bool trustEstablished = false;
@@ -742,7 +735,9 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
if (!auth) {
if ((!network)||(network->id() != nwid))
network = RR->node->network(nwid);
- const bool authOnNet = ((network)&&(network->gate(peer,verb(),packetId())));
+ const bool authOnNet = ((network)&&(network->gate(peer)));
+ if (!authOnNet)
+ _sendErrorNeedCredentials(RR,peer,nwid);
trustEstablished |= authOnNet;
if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
auth = true;
@@ -775,32 +770,45 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
CertificateOfMembership com;
Capability cap;
Tag tag;
+ Revocation revocation;
bool trustEstablished = false;
unsigned int p = ZT_PACKET_IDX_PAYLOAD;
while ((p < size())&&((*this)[p])) {
p += com.deserialize(*this,p);
if (com) {
- SharedPtr<Network> network(RR->node->network(com.networkId()));
+ const SharedPtr<Network> network(RR->node->network(com.networkId()));
if (network) {
switch (network->addCredential(com)) {
- case 0: trustEstablished = true; break;
- case 1: return false; // wait for WHOIS
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
} else RR->mc->addCredential(com,false);
}
}
++p; // skip trailing 0 after COMs if present
- if (p < size()) { // check if new capabilities and tags fields are present
+ if (p < size()) { // older ZeroTier versions do not send capabilities, tags, or revocations
const unsigned int numCapabilities = at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numCapabilities;++i) {
p += cap.deserialize(*this,p);
- SharedPtr<Network> network(RR->node->network(cap.networkId()));
+ const SharedPtr<Network> network(RR->node->network(cap.networkId()));
if (network) {
switch (network->addCredential(cap)) {
- case 0: trustEstablished = true; break;
- case 1: return false; // wait for WHOIS
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
}
@@ -808,17 +816,43 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
const unsigned int numTags = at<uint16_t>(p); p += 2;
for(unsigned int i=0;i<numTags;++i) {
p += tag.deserialize(*this,p);
- SharedPtr<Network> network(RR->node->network(tag.networkId()));
+ const SharedPtr<Network> network(RR->node->network(tag.networkId()));
if (network) {
switch (network->addCredential(tag)) {
- case 0: trustEstablished = true; break;
- case 1: return false; // wait for WHOIS
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
+ }
+ }
+ }
+
+ const unsigned int numRevocations = at<uint16_t>(p); p += 2;
+ for(unsigned int i=0;i<numRevocations;++i) {
+ p += revocation.deserialize(*this,p);
+ const SharedPtr<Network> network(RR->node->network(revocation.networkId()));
+ if (network) {
+ switch(network->addCredential(peer->address(),revocation)) {
+ case Membership::ADD_REJECTED:
+ break;
+ case Membership::ADD_ACCEPTED_NEW:
+ case Membership::ADD_ACCEPTED_REDUNDANT:
+ trustEstablished = true;
+ break;
+ case Membership::ADD_DEFERRED_FOR_WHOIS:
+ return false;
}
}
}
}
peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
+ } catch (std::exception &exc) {
+ TRACE("dropped NETWORK_CREDENTIALS from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
} catch ( ... ) {
TRACE("dropped NETWORK_CREDENTIALS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
}
@@ -847,19 +881,31 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
try {
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();
unsigned int chunkIndex = 0;
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);
outp.append((unsigned char)Packet::VERB_NETWORK_CONFIG_REQUEST);
outp.append(requestPacketId);
+
+ const unsigned int sigStart = outp.size();
outp.append(nwid);
outp.append((uint16_t)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)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();
RR->sw->send(outp,true);
chunkIndex += chunkLen;
@@ -926,32 +972,23 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
return true;
}
-bool IncomingPacket::_doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
{
try {
- const uint64_t nwid = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
- bool trustEstablished = false;
-
- if (Network::controllerFor(nwid) == peer->address()) {
- SharedPtr<Network> network(RR->node->network(nwid));
- if (network) {
- network->requestConfiguration();
- trustEstablished = true;
- } else {
- TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): not a member of %.16llx",source().toString().c_str(),_path->address().toString().c_str(),nwid);
- peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG_REFRESH,0,Packet::VERB_NOP,false);
- return true;
- }
-
- const unsigned int blacklistCount = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 8);
- unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 10;
- for(unsigned int i=0;i<blacklistCount;++i) {
- network->blacklistBefore(Address(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH),at<uint64_t>(ptr + 5));
- ptr += 13;
+ const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
+ 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_REFRESH,0,Packet::VERB_NOP,trustEstablished);
+ peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
} catch ( ... ) {
TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
}
@@ -984,7 +1021,9 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
}
}
- const bool trustEstablished = ((network)&&(network->gate(peer,verb(),packetId())));
+ const bool trustEstablished = ((network)&&(network->gate(peer)));
+ if (!trustEstablished)
+ _sendErrorNeedCredentials(RR,peer,nwid);
if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
@@ -1031,8 +1070,9 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
network->addCredential(com);
}
- if (!network->gate(peer,verb(),packetId())) {
+ if (!network->gate(peer)) {
TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
+ _sendErrorNeedCredentials(RR,peer,nwid);
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
return true;
}
@@ -1107,6 +1147,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true);
} else {
+ _sendErrorNeedCredentials(RR,peer,nwid);
peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
}
} catch ( ... ) {
@@ -1252,7 +1293,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
return true;
}
- if (network->gate(peer,verb(),packetId()))
+ if (network->gate(peer))
reportFlags |= ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH;
} else {
TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
@@ -1379,146 +1420,18 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S
return true;
}
-bool IncomingPacket::_doREQUEST_PROOF_OF_WORK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid)
{
- try {
- // If this were allowed from anyone, it would itself be a DOS vector. Right
- // now we only allow it from roots and controllers of networks you have joined.
- bool allowed = RR->topology->isUpstream(peer->identity());
- if (!allowed) {
- std::vector< SharedPtr<Network> > allNetworks(RR->node->allNetworks());
- for(std::vector< SharedPtr<Network> >::const_iterator n(allNetworks.begin());n!=allNetworks.end();++n) {
- if (peer->address() == (*n)->controller()) {
- allowed = true;
- break;
- }
- }
- }
-
- if (allowed) {
- const uint64_t pid = packetId();
- const unsigned int difficulty = (*this)[ZT_PACKET_IDX_PAYLOAD + 1];
- const unsigned int challengeLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 2);
- if (challengeLength > ZT_PROTO_MAX_PACKET_LENGTH)
- return true; // sanity check, drop invalid size
- const unsigned char *challenge = field(ZT_PACKET_IDX_PAYLOAD + 4,challengeLength);
-
- switch((*this)[ZT_PACKET_IDX_PAYLOAD]) {
-
- // Salsa20/12+SHA512 hashcash
- case 0x01: {
- if (difficulty <= 14) {
- unsigned char result[16];
- computeSalsa2012Sha512ProofOfWork(difficulty,challenge,challengeLength,result);
- TRACE("PROOF_OF_WORK computed for %s: difficulty==%u, challengeLength==%u, result: %.16llx%.16llx",peer->address().toString().c_str(),difficulty,challengeLength,Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result))),Utils::ntoh(*(reinterpret_cast<const uint64_t *>(result + 8))));
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((uint16_t)sizeof(result));
- outp.append(result,sizeof(result));
- outp.armor(peer->key(),true);
- _path->send(RR,outp.data(),outp.size(),RR->node->now());
- } else {
- Packet outp(peer->address(),RR->identity.address(),Packet::VERB_ERROR);
- outp.append((unsigned char)Packet::VERB_REQUEST_PROOF_OF_WORK);
- outp.append(pid);
- outp.append((unsigned char)Packet::ERROR_INVALID_REQUEST);
- outp.armor(peer->key(),true);
- _path->send(RR,outp.data(),outp.size(),RR->node->now());
- }
- } break;
-
- default:
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unrecognized proof of work type",peer->address().toString().c_str(),_path->address().toString().c_str());
- break;
- }
-
- peer->received(_path,hops(),pid,Packet::VERB_REQUEST_PROOF_OF_WORK,0,Packet::VERB_NOP,false);
- } else {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): not trusted enough",peer->address().toString().c_str(),_path->address().toString().c_str());
- }
- } catch ( ... ) {
- TRACE("dropped REQUEST_PROOF_OF_WORK from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
- }
- return true;
-}
-
-void IncomingPacket::computeSalsa2012Sha512ProofOfWork(unsigned int difficulty,const void *challenge,unsigned int challengeLength,unsigned char result[16])
-{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidatebuf[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- char *const candidate = (char *)(( ((uintptr_t)&(candidatebuf[0])) | 0xf ) + 1); // align to 16-byte boundary to ensure that uint64_t type punning of initial nonce is okay
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- Utils::getSecureRandom(candidate,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
-try_salsa2012sha512_again:
- ++*(reinterpret_cast<volatile uint64_t *>(candidate));
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- goto try_salsa2012sha512_again;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- goto try_salsa2012sha512_again;
- }
-
- memcpy(result,candidate,16);
-}
-
-bool IncomingPacket::testSalsa2012Sha512ProofOfWorkResult(unsigned int difficulty,const void *challenge,unsigned int challengeLength,const unsigned char proposedResult[16])
-{
- unsigned char salsabuf[131072]; // 131072 == protocol constant, size of memory buffer for this proof of work function
- char candidate[ZT_PROTO_MAX_PACKET_LENGTH + 256];
- unsigned char shabuf[ZT_SHA512_DIGEST_LEN];
- const uint64_t s20iv = 0; // zero IV for Salsa20
- Salsa20 s20;
- unsigned int d;
- unsigned char *p;
-
- if (difficulty > 512)
- difficulty = 512; // sanity check
-
- memcpy(candidate,proposedResult,16);
- memcpy(candidate + 16,challenge,challengeLength);
-
- SHA512::hash(shabuf,candidate,16 + challengeLength);
- s20.init(shabuf,256,&s20iv);
- memset(salsabuf,0,sizeof(salsabuf));
- s20.encrypt12(salsabuf,salsabuf,sizeof(salsabuf));
- SHA512::hash(shabuf,salsabuf,sizeof(salsabuf));
-
- d = difficulty;
- p = shabuf;
- while (d >= 8) {
- if (*(p++))
- return false;
- d -= 8;
- }
- if (d > 0) {
- if ( ((((unsigned int)*p) << d) & 0xff00) != 0 )
- return false;
+ const uint64_t now = RR->node->now();
+ if (peer->rateGateOutgoingComRequest(now)) {
+ Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);
+ outp.append((uint8_t)verb());
+ outp.append(packetId());
+ outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
+ outp.append(nwid);
+ outp.armor(peer->key(),true);
+ _path->send(RR,outp.data(),outp.size(),now);
}
-
- return true;
}
} // namespace ZeroTier