Browse Source

Handling of CIRCUIT_TEST, should be ready to test.

Adam Ierymenko 9 years ago
parent
commit
5341afcdcd
3 changed files with 162 additions and 51 deletions
  1. 17 0
      node/Buffer.hpp
  2. 130 22
      node/IncomingPacket.cpp
  3. 15 29
      node/Packet.hpp

+ 17 - 0
node/Buffer.hpp

@@ -391,6 +391,23 @@ public:
 		::memmove(_b,_b + at,_l -= at);
 		::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
 	 * Set buffer data length to zero
 	 */
 	 */

+ 130 - 22
node/IncomingPacket.cpp

@@ -933,38 +933,146 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 		const uint64_t timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 7);
 		const uint64_t timestamp = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 7);
 		const uint64_t testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 15);
 		const uint64_t testId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 15);
 
 
-		unsigned int vlf = at<uint16_t>(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<uint16_t>(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<uint64_t>(ZT_PACKET_IDX_PAYLOAD + 26);
+				}	break;
+				default: break;
+			}
 		}
 		}
 
 
-		vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 26 + vlf); // length of additional fields, currently unused
+		// Add length of "additional fields," which are currently unused
+		vlf += at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 25 + vlf);
 
 
-		const unsigned int signatureLength = at<uint16_t>(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<uint16_t>(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());
 			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;
 			return true;
 		}
 		}
 		vlf += signatureLength;
 		vlf += signatureLength;
 
 
-		vlf += at<uint16_t>(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<uint16_t>(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<NetworkConfig> originatorCredentialNetworkConfig;
+		if (originatorCredentialNetworkId) {
+			if (Network::controllerFor(originatorCredentialNetworkId) == originatorAddress) {
+				SharedPtr<Network> 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;
+		}
+
+		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<breadth;++h) {
+				nextHop[h].setTo(field(remainingHopsPtr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
+				remainingHopsPtr += ZT_ADDRESS_LENGTH;
+				SharedPtr<Peer> 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;h<breadth;++h) {
+				nextHop[h].appendTo(outp);
+				nextHopBestPathAddress[h].serialize(outp); // appends 0 if null InetAddress
+			}
+			RR->sw->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<uint16_t>(previousHopCredentialPos,(uint16_t)(size() - previousHopCredentialPos));
+			}
+			if (remainingHopsPtr < size())
+				outp.append(field(remainingHopsPtr,size() - remainingHopsPtr),size() - remainingHopsPtr);
 
 
-		if ((ZT_PACKET_IDX_PAYLOAD + 33 + vlf) < size()) {
-			const unsigned int breadth = (*this)[ZT_PACKET_IDX_PAYLOAD + 33 + vlf];
-			Address nextHops[255];
-			SharedPtr<Peer> nextHopPeers[255];
-			unsigned int hptr = ZT_PACKET_IDX_PAYLOAD + 34 + vlf;
-			for(unsigned int h=0;((h<breadth)&&(h<255));++h) { // breadth can't actually be >256 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]);
+			for(unsigned int h=0;h<breadth;++h) {
+				outp.newInitializationVector();
+				outp.setDestination(nextHop[h]);
+				RR->sw->send(outp,true,originatorCredentialNetworkId);
 			}
 			}
 		}
 		}
 	} catch (std::exception &exc) {
 	} catch (std::exception &exc) {

+ 15 - 29
node/Packet.hpp

@@ -911,39 +911,34 @@ public:
 		 *   <[2] 16-bit flags>
 		 *   <[2] 16-bit flags>
 		 *   <[8] 64-bit timestamp>
 		 *   <[8] 64-bit timestamp>
 		 *   <[8] 64-bit test ID (arbitrary, set by tester)>
 		 *   <[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>
 		 *   <[2] 16-bit length of additional fields>
-		 *   <[...] additional fields>
+		 *   [[...] additional fields]
 		 *   [ ... end of signed portion of request ... ]
 		 *   [ ... end of signed portion of request ... ]
 		 *   <[2] 16-bit length of signature of request>
 		 *   <[2] 16-bit length of signature of request>
 		 *   <[...] signature of request by originator>
 		 *   <[...] 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>
 		 *   <[...] next hop(s) in path>
 		 *
 		 *
 		 * Flags:
 		 * 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
 		 *   0x02 - Report back to originator at last hop
 		 *
 		 *
 		 * Originator credential types:
 		 * Originator credential types:
-		 *   0x00 - No credentials included
 		 *   0x01 - 64-bit network ID for which originator is controller
 		 *   0x01 - 64-bit network ID for which originator is controller
 		 *
 		 *
 		 * Previous hop credential types:
 		 * Previous hop credential types:
-		 *   0x00 - No credentials included
 		 *   0x01 - Certificate of network membership
 		 *   0x01 - Certificate of network membership
 		 *
 		 *
 		 * Path record format:
 		 * Path record format:
-		 *   <[1] 8-bit flags>
+		 *   <[1] 8-bit flags (unused, must be zero)>
 		 *   <[1] 8-bit breadth (number of next hops)>
 		 *   <[1] 8-bit breadth (number of next hops)>
 		 *   <[...] one or more ZeroTier addresses 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 circuit test allows a device to send a message that will traverse
 		 * the network along a specified path, with each hop optionally reporting
 		 * the network along a specified path, with each hop optionally reporting
 		 * back to the tester via VERB_CIRCUIT_TEST_REPORT.
 		 * back to the tester via VERB_CIRCUIT_TEST_REPORT.
@@ -1001,28 +996,19 @@ public:
 		 *   <[2] 16-bit reporter OS/platform>
 		 *   <[2] 16-bit reporter OS/platform>
 		 *   <[2] 16-bit reporter architecture>
 		 *   <[2] 16-bit reporter architecture>
 		 *   <[2] 16-bit error code (set to 0, currently unused)>
 		 *   <[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>
 		 *   <[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>
 		 *   <[2] 16-bit length of additional fields>
 		 *   <[...] 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>
 		 *   <[...] next hop information>
 		 *
 		 *
-		 * Circuit test report flags:
-		 *   (currently none, must be zero)
-		 *
 		 * Next hop information record format:
 		 * Next hop information record format:
 		 *   <[5] ZeroTier address of next hop>
 		 *   <[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
 		 * 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
 		 * back results. They should include information about the sender as well