From 5341afcdcd7d2d1ae5546ae44024d039b03ccad3 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 6 Oct 2015 11:47:16 -0700 Subject: [PATCH] Handling of CIRCUIT_TEST, should be ready to test. --- node/Buffer.hpp | 17 +++++ node/IncomingPacket.cpp | 152 ++++++++++++++++++++++++++++++++++------ node/Packet.hpp | 44 ++++-------- 3 files changed, 162 insertions(+), 51 deletions(-) diff --git a/node/Buffer.hpp b/node/Buffer.hpp index 789b835a5..46924c144 100644 --- a/node/Buffer.hpp +++ b/node/Buffer.hpp @@ -391,6 +391,23 @@ public: ::memmove(_b,_b + at,_l -= at); } + /** + * Erase something from the middle of the buffer + * + * @param start Starting position + * @param length Length of block to erase + * @throw std::out_of_range Position plus length is beyond size of buffer + */ + inline void erase(const unsigned int at,const unsigned int length) + throw(std::out_of_range) + { + const unsigned int endr = at + length; + if (endr > _l) + throw std::out_of_range("Buffer: erase() range beyond end of buffer"); + ::memmove(_b + at,_b + endr,_l - endr); + _l -= length; + } + /** * Set buffer data length to zero */ diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp index e892edc21..49a5981bb 100644 --- a/node/IncomingPacket.cpp +++ b/node/IncomingPacket.cpp @@ -933,38 +933,146 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt const uint64_t timestamp = at(ZT_PACKET_IDX_PAYLOAD + 7); const uint64_t testId = at(ZT_PACKET_IDX_PAYLOAD + 15); - unsigned int vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); // variable length field length - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { - case 0x01: { // 64-bit network ID, originator must be controller - } break; - default: break; + // Tracks total length of variable length fields, initialized to originator credential length below + unsigned int vlf; + + // Originator credentials + const unsigned int originatorCredentialLength = vlf = at(ZT_PACKET_IDX_PAYLOAD + 23); + uint64_t originatorCredentialNetworkId = 0; + if (originatorCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 25]) { + case 0x01: { // 64-bit network ID, originator must be controller + if (originatorCredentialLength >= 9) + originatorCredentialNetworkId = at(ZT_PACKET_IDX_PAYLOAD + 26); + } break; + default: break; + } } - vlf += at(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused + // Add length of "additional fields," which are currently unused + vlf += at(ZT_PACKET_IDX_PAYLOAD + 25 + vlf); - const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 28 + vlf); - if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,28 + vlf),28 + vlf,field(30 + vlf,signatureLength),signatureLength)) { + // Verify signature -- only tests signed by their originators are allowed + const unsigned int signatureLength = at(ZT_PACKET_IDX_PAYLOAD + 27 + vlf); + if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) { TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); return true; } vlf += signatureLength; - vlf += at(ZT_PACKET_IDX_PAYLOAD + 30 + vlf); - switch((*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]) { - case 0x01: { // network certificate of membership for previous hop - } break; - default: break; + // Save this length so we can copy the immutable parts of this test + // into the one we send along to next hops. + const unsigned int lengthOfSignedPortionAndSignature = 29 + vlf; + + // Get previous hop's credential, if any + const unsigned int previousHopCredentialLength = at(ZT_PACKET_IDX_PAYLOAD + 29 + vlf); + CertificateOfMembership previousHopCom; + if (previousHopCredentialLength >= 1) { + switch((*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf]) { + case 0x01: { // network certificate of membership for previous hop + if (previousHopCom.deserialize(*this,ZT_PACKET_IDX_PAYLOAD + 32 + vlf) != (previousHopCredentialLength - 1)) { + TRACE("dropped CIRCUIT_TEST from %s(%s): previous hop COM invalid",source().toString().c_str(),_remoteAddress.toString().c_str()); + return true; + } + } break; + default: break; + } + } + vlf += previousHopCredentialLength; + + // Check credentials (signature already verified) + SharedPtr originatorCredentialNetworkConfig; + if (originatorCredentialNetworkId) { + if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) { + SharedPtr nw(RR->node->network(originatorCredentialNetworkId)); + if (nw) { + originatorCredentialNetworkConfig = nw->config2(); + if ( (originatorCredentialNetworkConfig) && (originatorCredentialNetworkConfig->isPublic()||((originatorCredentialNetworkConfig->com())&&(previousHopCom)&&(originatorCredentialNetworkConfig->com().agreesWith(previousHopCom)))) ) { + TRACE("CIRCUIT_TEST %.16llx received from hop %s(%s) and originator %s with valid network ID credential %.16llx (verified from originator and next hop)",testId,source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and previous hop %s did not supply a valid COM",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId,peer->address().toString().c_str()); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we are not a member",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID as credential, is not controller for %.16llx",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId); + return true; + } + } else { + TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_remoteAddress.toString().c_str(),originatorAddress.toString().c_str()); + return true; } - if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) { - const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf]; - Address nextHops[255]; - SharedPtr nextHopPeers[255]; - unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf; - for(unsigned int h=0;((h256 but be safe anyway - nextHops[h].setTo(field(hptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH); - hptr += ZT_ADDRESS_LENGTH; - nextHopPeers[h] = RR->topology->getPeer(nextHops[h]); + const uint64_t now = RR->node->now(); + + unsigned int breadth = 0; + Address nextHop[256]; // breadth is a uin8_t, so this is the max + InetAddress nextHopBestPathAddress[256]; + unsigned int remainingHopsPtr = ZT_PACKET_IDX_PAYLOAD + 33 + vlf; + if ((ZT_PACKET_IDX_PAYLOAD + 31 + vlf) < size()) { + // unsigned int nextHopFlags = (*this)[ZT_PACKET_IDX_PAYLOAD + 31 + vlf] + breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 32 + vlf]; + for(unsigned int h=0;h nhp(RR->topology->getPeer(nextHop[h])); + if (nhp) { + RemotePath *const rp = nhp->getBestPath(now); + if (rp) + nextHopBestPathAddress[h] = rp->address(); + } + } + } + + // Report back to originator, depending on flags and whether we are last hop + if ( ((flags & 0x01) != 0) || ((breadth == 0)&&((flags & 0x02) != 0)) ) { + Packet outp(originatorAddress,RR->identity.address(),Packet::VERB_CIRCUIT_TEST_REPORT); + outp.append((uint64_t)timestamp); + outp.append((uint64_t)testId); + outp.append((uint64_t)now); + outp.append((uint8_t)0); // vendor ID, currently unused + outp.append((uint8_t)ZT_PROTO_VERSION); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MAJOR); + outp.append((uint8_t)ZEROTIER_ONE_VERSION_MINOR); + outp.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED); + outp.append((uint16_t)CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED); + outp.append((uint16_t)0); // error code, currently unused + outp.append((uint64_t)0); // flags, currently unused + outp.append((uint64_t)packetId()); + outp.append((uint8_t)hops()); + _localAddress.serialize(outp); + _remoteAddress.serialize(outp); + outp.append((uint16_t)0); // no additional fields + outp.append((uint8_t)breadth); + for(unsigned int h=0;hsw->send(outp,true,0); + } + + // If there are next hops, forward the test along through the graph + if (breadth > 0) { + Packet outp(Address(),RR->identity.address(),Packet::VERB_CIRCUIT_TEST); + outp.append(field(ZT_PACKET_IDX_PAYLOAD,lengthOfSignedPortionAndSignature),lengthOfSignedPortionAndSignature); + const unsigned int previousHopCredentialPos = outp.size(); + outp.append((uint16_t)0); // no previous hop credentials: default + if ((originatorCredentialNetworkConfig)&&(!originatorCredentialNetworkConfig->isPublic())&&(originatorCredentialNetworkConfig->com())) { + outp.append((uint8_t)0x01); // COM + originatorCredentialNetworkConfig->com().serialize(outp); + outp.setAt(previousHopCredentialPos,(uint16_t)(size() - previousHopCredentialPos)); + } + if (remainingHopsPtr < size()) + outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr); + + for(unsigned int h=0;hsw->send(outp,true,originatorCredentialNetworkId); } } } catch (std::exception &exc) { diff --git a/node/Packet.hpp b/node/Packet.hpp index 71fa6e569..eaffb922d 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -911,39 +911,34 @@ public: * <[2] 16-bit flags> * <[8] 64-bit timestamp> * <[8] 64-bit test ID (arbitrary, set by tester)> - * <[2] 16-bit originator credential length> - * <[1] originator credential type (for authorizing test)> - * <[...] credential> + * <[2] 16-bit originator credential length (includes type)> + * [[1] originator credential type (for authorizing test)] + * [[...] originator credential] * <[2] 16-bit length of additional fields> - * <[...] additional fields> + * [[...] additional fields] * [ ... end of signed portion of request ... ] * <[2] 16-bit length of signature of request> * <[...] signature of request by originator> - * <[2] 16-bit previous hop credential length> - * <[1] previous hop credential type> - * <[...] previous hop credential> + * <[2] 16-bit previous hop credential length (including type)> + * [[1] previous hop credential type] + * [[...] previous hop credential] * <[...] next hop(s) in path> * * Flags: - * 0x01 - Report back to originator at each hop + * 0x01 - Report back to originator at middle hops * 0x02 - Report back to originator at last hop * * Originator credential types: - * 0x00 - No credentials included * 0x01 - 64-bit network ID for which originator is controller * * Previous hop credential types: - * 0x00 - No credentials included * 0x01 - Certificate of network membership * * Path record format: - * <[1] 8-bit flags> + * <[1] 8-bit flags (unused, must be zero)> * <[1] 8-bit breadth (number of next hops)> * <[...] one or more ZeroTier addresses of next hops> * - * Path record flags (in each path record): - * (unused, must be zero) - * * The circuit test allows a device to send a message that will traverse * the network along a specified path, with each hop optionally reporting * back to the tester via VERB_CIRCUIT_TEST_REPORT. @@ -1001,28 +996,19 @@ public: * <[2] 16-bit reporter OS/platform> * <[2] 16-bit reporter architecture> * <[2] 16-bit error code (set to 0, currently unused)> - * <[8] 64-bit report flags> + * <[8] 64-bit report flags (set to 0, currently unused)> * <[8] 64-bit source packet ID> - * <[1] 8-bit source packet hop count> - * <[1] 8-bit source address type> - * [<[...] source address>] - * <[2] 16-bit length of network information> - * <[...] network information> + * <[1] 8-bit source packet hop count (ZeroTier hop count)> + * <[...] local wire address on which packet was received> + * <[...] remote wire address from which packet was received> * <[2] 16-bit length of additional fields> * <[...] additional fields> - * <[2] 16-bit number of next hops to which something is being sent> + * <[1] 8-bit number of next hops (breadth)> * <[...] next hop information> * - * Circuit test report flags: - * (currently none, must be zero) - * * Next hop information record format: * <[5] ZeroTier address of next hop> - * <[1] 8-bit destination wire address type> - * <[...] destination wire address> - * - * See enums below for OS/platform and architecture. Source address format - * is the same as specified in HELLO. + * <[...] current best direct path address, if any, 0 if none> * * Circuit test reports can be sent by hops in a circuit test to report * back results. They should include information about the sender as well