Browse Source

Plumbing through circuit test stuff.

Adam Ierymenko 9 years ago
parent
commit
d3f29d09e8
5 changed files with 339 additions and 41 deletions
  1. 271 0
      include/ZeroTierOne.h
  2. 4 3
      node/IncomingPacket.cpp
  3. 52 0
      node/Node.cpp
  4. 12 0
      node/Node.hpp
  5. 0 38
      node/Packet.hpp

+ 271 - 0
include/ZeroTierOne.h

@@ -115,6 +115,19 @@ extern "C" {
  */
 #define ZT_FEATURE_FLAG_FIPS 0x00000002
 
+/**
+ * Maximum number of hops in a ZeroTier circuit test
+ *
+ * This is more or less the max that can be fit in a given packet (with
+ * fragmentation) and only one address per hop.
+ */
+#define ZT_CIRCUIT_TEST_MAX_HOPS 512
+
+/**
+ * Maximum number of addresses per hop in a circuit test
+ */
+#define ZT_CIRCUIT_TEST_MAX_HOP_BREADTH 256
+
 /**
  * A null/empty sockaddr (all zero) to signify an unspecified socket address
  */
@@ -631,6 +644,231 @@ typedef enum {
 	ZT_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 20
 } ZT_LocalInterfaceAddressTrust;
 
+/**
+ * Vendor ID
+ */
+typedef enum {
+	ZT_VENDOR_UNSPECIFIED = 0,
+	ZT_VENDOR_ZEROTIER = 1
+} ZT_Vendor;
+
+/**
+ * Platform type
+ */
+typedef enum {
+	ZT_PLATFORM_UNSPECIFIED = 0,
+	ZT_PLATFORM_LINUX = 1,
+	ZT_PLATFORM_WINDOWS = 2,
+	ZT_PLATFORM_MACOS = 3,
+	ZT_PLATFORM_ANDROID = 4,
+	ZT_PLATFORM_IOS = 5,
+	ZT_PLATFORM_SOLARIS_SMARTOS = 6,
+	ZT_PLATFORM_FREEBSD = 7,
+	ZT_PLATFORM_NETBSD = 8,
+	ZT_PLATFORM_OPENBSD = 9,
+	ZT_PLATFORM_RISCOS = 10,
+	ZT_PLATFORM_VXWORKS = 11,
+	ZT_PLATFORM_FREERTOS = 12,
+	ZT_PLATFORM_SYSBIOS = 13,
+	ZT_PLATFORM_HURD = 14
+} ZT_Platform;
+
+/**
+ * Architecture type
+ */
+typedef enum {
+	ZT_ARCHITECTURE_UNSPECIFIED = 0,
+	ZT_ARCHITECTURE_X86 = 1,
+	ZT_ARCHITECTURE_X64 = 2,
+	ZT_ARCHITECTURE_ARM32 = 3,
+	ZT_ARCHITECTURE_ARM64 = 4,
+	ZT_ARCHITECTURE_MIPS32 = 5,
+	ZT_ARCHITECTURE_MIPS64 = 6,
+	ZT_ARCHITECTURE_POWER32 = 7,
+	ZT_ARCHITECTURE_POWER64 = 8
+} ZT_Architecture;
+
+/**
+ * ZeroTier circuit test configuration and path
+ */
+typedef struct {
+	/**
+	 * Test ID -- an arbitrary 64-bit identifier
+	 */
+	uint64_t testId;
+
+	/**
+	 * Timestamp -- sent with test and echoed back by each reporter
+	 */
+	uint64_t timestamp;
+
+	/**
+	 * Originator credential: network ID
+	 *
+	 * If this is nonzero, a network ID will be set for this test and
+	 * the originator must be its primary network controller. This is
+	 * currently the only authorization method available, so it must
+	 * be set to run a test.
+	 */
+	uint64_t credentialNetworkId;
+
+	/**
+	 * Hops in circuit test (a.k.a. FIFO for graph traversal)
+	 */
+	struct {
+		/**
+		 * Hop flags (currently unused, must be zero)
+		 */
+		unsigned int flags;
+
+		/**
+		 * Number of addresses in this hop (max: ZT_CIRCUIT_TEST_MAX_HOP_BREADTH)
+		 */
+		unsigned int breadth;
+
+		/**
+		 * 40-bit ZeroTier addresses (most significant 24 bits ignored)
+		 */
+		uint64_t addresses[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
+	} hops[ZT_CIRCUIT_TEST_MAX_HOPS];
+
+	/**
+	 * Number of hops (max: ZT_CIRCUIT_TEST_MAX_HOPS)
+	 */
+	unsigned int hopCount;
+
+	/**
+	 * If non-zero, circuit test will report back at every hop
+	 */
+	int reportAtEveryHop;
+
+	/**
+	 * An arbitrary user-settable pointer
+	 */
+	void *ptr;
+
+	/**
+	 * Pointer for internal use -- initialize to zero and do not modify
+	 */
+	void *_internalPtr;
+} ZT_CircuitTest;
+
+/**
+ * Circuit test result report
+ */
+typedef struct {
+	/**
+	 * 64-bit test ID
+	 */
+	uint64_t testId;
+
+	/**
+	 * Timestamp from original test (echoed back at each hop)
+	 */
+	uint64_t timestamp;
+
+	/**
+	 * Timestamp on remote device
+	 */
+	uint64_t remoteTimestamp;
+
+	/**
+	 * 64-bit packet ID of packet received by the reporting device
+	 */
+	uint64_t sourcePacketId;
+
+	/**
+	 * Flags (currently unused, will be zero)
+	 */
+	uint64_t flags;
+
+	/**
+	 * ZeroTier protocol-level hop count of packet received by reporting device (>0 indicates relayed)
+	 */
+	unsigned int sourcePacketHopCount;
+
+	/**
+	 * Error code (currently unused, will be zero)
+	 */
+	unsigned int errorCode;
+
+	/**
+	 * Remote device vendor ID
+	 */
+	ZT_Vendor vendor;
+
+	/**
+	 * Remote device protocol compliance version
+	 */
+	unsigned int protocolVersion;
+
+	/**
+	 * Software major version
+	 */
+	unsigned int majorVersion;
+
+	/**
+	 * Software minor version
+	 */
+	unsigned int minorVersion;
+
+	/**
+	 * Software revision
+	 */
+	unsigned int revision;
+
+	/**
+	 * Platform / OS
+	 */
+	ZT_Platform platform;
+
+	/**
+	 * System architecture
+	 */
+	ZT_Architecture architecture;
+
+	/**
+	 * Local device address on which packet was received by reporting device
+	 *
+	 * This may have ss_family equal to zero (null address) if unspecified.
+	 */
+	struct sockaddr_storage receivedOnLocalAddress;
+
+	/**
+	 * Remote address from which reporter received the test packet
+	 *
+	 * This may have ss_family set to zero (null address) if unspecified.
+	 */
+	struct sockaddr_storage receivedFromAddress;
+
+	/**
+	 * Next hops to which packets are being or will be sent by the reporter
+	 *
+	 * In addition to reporting back, the reporter may send the test on if
+	 * there are more recipients in the FIFO. If it does this, it can report
+	 * back the address(es) that make up the next hop and the physical address
+	 * for each if it has one. The physical address being null/unspecified
+	 * typically indicates that no direct path exists and the next packet
+	 * will be relayed.
+	 */
+	struct {
+		/**
+		 * 40-bit ZeroTier address
+		 */
+		uint64_t address;
+
+		/**
+		 * Physical address or null address (ss_family == 0) if unspecified or unknown
+		 */
+		struct sockaddr_storage physicalAddress;
+	} nextHops[ZT_CIRCUIT_TEST_MAX_HOP_BREADTH];
+
+	/**
+	 * Number of next hops reported in nextHops[]
+	 */
+	unsigned int nextHopCount;
+} ZT_CircuitTestReport;
+
 /**
  * An instance of a ZeroTier One node (opaque)
  */
@@ -1061,6 +1299,39 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
  */
 void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
 
+/**
+ * Initiate a VL1 circuit test
+ *
+ * This sends an initial VERB_CIRCUIT_TEST and reports results back to the
+ * supplied callback until circuitTestEnd() is called. The supplied
+ * ZT_CircuitTest structure should be initially zeroed and then filled
+ * in with settings and hops.
+ *
+ * It is the caller's responsibility to call circuitTestEnd() and then
+ * to dispose of the test structure. Otherwise this node will listen
+ * for results forever.
+ *
+ * @param node Node instance
+ * @param test Test configuration
+ * @param reportCallback Function to call each time a report is received
+ * @return OK or error if, for example, test is too big for a packet or support isn't compiled in
+ */
+ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
+
+/**
+ * Stop listening for results to a given circuit test
+ *
+ * This does not free the 'test' structure. The caller may do that
+ * after calling this method to unregister it.
+ *
+ * Any reports that are received for a given test ID after it is
+ * terminated are ignored.
+ *
+ * @param node Node instance
+ * @param test Test configuration to unregister
+ */
+void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test);
+
 /**
  * Get ZeroTier One version
  *

+ 4 - 3
node/IncomingPacket.cpp

@@ -30,6 +30,7 @@
 #include <stdlib.h>
 
 #include "../version.h"
+#include "../include/ZeroTierOne.h"
 
 #include "Constants.hpp"
 #include "Defaults.hpp"
@@ -1033,13 +1034,13 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 			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_VENDOR_ZEROTIER);
 			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)ZT_PLATFORM_UNSPECIFIED);
+			outp.append((uint16_t)ZT_ARCHITECTURE_UNSPECIFIED);
 			outp.append((uint16_t)0); // error code, currently unused
 			outp.append((uint64_t)0); // flags, currently unused
 			outp.append((uint64_t)packetId());

+ 52 - 0
node/Node.cpp

@@ -464,6 +464,28 @@ void Node::setNetconfMaster(void *networkControllerInstance)
 	RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
 }
 
+ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+{
+	{
+		test->_internalPtr = reinterpret_cast<void *>(reportCallback);
+		Mutex::Lock _l(_circuitTests_m);
+		if (std::find(_circuitTests.begin(),_circuitTests.end(),test) == _circuitTests.end())
+			_circuitTests.push_back(test);
+	}
+	return ZT_RESULT_OK;
+}
+
+void Node::circuitTestEnd(ZT_CircuitTest *test)
+{
+	Mutex::Lock _l(_circuitTests_m);
+	for(;;) {
+		std::vector< ZT_CircuitTest * >::iterator ct(std::find(_circuitTests.begin(),_circuitTests.end(),test));
+		if (ct == _circuitTests.end())
+			break;
+		else _circuitTests.erase(ct);
+	}
+}
+
 /****************************************************************************/
 /* Node methods used only within node/                                      */
 /****************************************************************************/
@@ -533,6 +555,20 @@ uint64_t Node::prng()
 	return _prngStream[p];
 }
 
+void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
+{
+	std::vector< ZT_CircuitTest * > toNotify;
+	{
+		Mutex::Lock _l(_circuitTests_m);
+		for(std::vector< ZT_CircuitTest * >::iterator i(_circuitTests.begin());i!=_circuitTests.end();++i) {
+			if ((*i)->testId == report->testId)
+				toNotify.push_back(*i);
+		}
+	}
+	for(std::vector< ZT_CircuitTest * >::iterator i(toNotify.begin());i!=toNotify.end();++i)
+		(reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
+}
+
 } // namespace ZeroTier
 
 /****************************************************************************/
@@ -721,6 +757,22 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
 	} catch ( ... ) {}
 }
 
+ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+{
+	try {
+		return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(test,reportCallback);
+	} catch ( ... ) {
+		return ZT_RESULT_FATAL_ERROR_INTERNAL;
+	}
+}
+
+void ZT_Node_circuitTestEnd(ZT_Node *node,ZT_CircuitTest *test)
+{
+	try {
+		reinterpret_cast<ZeroTier::Node *>(node)->circuitTestEnd(test);
+	} catch ( ... ) {}
+}
+
 int ZT_Node_addLocalInterfaceAddress(ZT_Node *node,const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust)
 {
 	try {

+ 12 - 0
node/Node.hpp

@@ -109,6 +109,8 @@ public:
 	int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT_LocalInterfaceAddressTrust trust);
 	void clearLocalInterfaceAddresses();
 	void setNetconfMaster(void *networkControllerInstance);
+	ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
+	void circuitTestEnd(ZT_CircuitTest *test);
 
 	// Internal functions ------------------------------------------------------
 
@@ -238,6 +240,13 @@ public:
 	 */
 	uint64_t prng();
 
+	/**
+	 * Post a circuit test report to any listeners for a given test ID
+	 *
+	 * @param report Report (includes test ID)
+	 */
+	void postCircuitTestReport(const ZT_CircuitTestReport *report);
+
 private:
 	inline SharedPtr<Network> _network(uint64_t nwid) const
 	{
@@ -264,6 +273,9 @@ private:
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
 	Mutex _networks_m;
 
+	std::vector< ZT_CircuitTest * > _circuitTests;
+	Mutex _circuitTests_m;
+
 	std::vector<Path> _directPaths;
 	Mutex _directPaths_m;
 

+ 0 - 38
node/Packet.hpp

@@ -1020,44 +1020,6 @@ public:
 		VERB_CIRCUIT_TEST_REPORT = 18
 	};
 
-	/**
-	 * Platforms reported in circuit tests
-	 */
-	enum CircuitTestReportPlatform
-	{
-		CIRCUIT_TEST_REPORT_PLATFORM_UNSPECIFIED = 0,
-		CIRCUIT_TEST_REPORT_PLATFORM_LINUX = 1,
-		CIRCUIT_TEST_REPORT_PLATFORM_WINDOWS = 2,
-		CIRCUIT_TEST_REPORT_PLATFORM_MACOS = 3,
-		CIRCUIT_TEST_REPORT_PLATFORM_ANDROID = 4,
-		CIRCUIT_TEST_REPORT_PLATFORM_IOS = 5,
-		CIRCUIT_TEST_REPORT_PLATFORM_SOLARIS_SMARTOS = 6,
-		CIRCUIT_TEST_REPORT_PLATFORM_FREEBSD = 7,
-		CIRCUIT_TEST_REPORT_PLATFORM_NETBSD = 8,
-		CIRCUIT_TEST_REPORT_PLATFORM_OPENBSD = 9,
-		CIRCUIT_TEST_REPORT_PLATFORM_RISCOS = 10,
-		CIRCUIT_TEST_REPORT_PLATFORM_VXWORKS = 11,
-		CIRCUIT_TEST_REPORT_PLATFORM_FREERTOS = 12,
-		CIRCUIT_TEST_REPORT_PLATFORM_SYSBIOS = 13,
-		CIRCUIT_TEST_REPORT_PLATFORM_HURD = 14
-	};
-
-	/**
-	 * Architectures reported in circuit tests
-	 */
-	enum CircuitTestReportArchitecture
-	{
-		CIRCUIT_TEST_REPORT_ARCH_UNSPECIFIED = 0,
-		CIRCUIT_TEST_REPORT_ARCH_X86 = 1,
-		CIRCUIT_TEST_REPORT_ARCH_X64 = 2,
-		CIRCUIT_TEST_REPORT_ARCH_ARM32 = 3,
-		CIRCUIT_TEST_REPORT_ARCH_ARM64 = 4,
-		CIRCUIT_TEST_REPORT_ARCH_MIPS32 = 5,
-		CIRCUIT_TEST_REPORT_ARCH_MIPS64 = 6,
-		CIRCUIT_TEST_REPORT_ARCH_POWER32 = 7,
-		CIRCUIT_TEST_REPORT_ARCH_POWER64 = 8
-	};
-
 	/**
 	 * Error codes for VERB_ERROR
 	 */