Browse Source

Trusted path support, and version bump to 1.1.9

Adam Ierymenko 9 years ago
parent
commit
765082fdb6
8 changed files with 164 additions and 27 deletions
  1. 28 0
      include/ZeroTierOne.h
  2. 21 5
      node/IncomingPacket.cpp
  3. 12 0
      node/Node.cpp
  4. 1 12
      node/Node.hpp
  5. 42 8
      node/Packet.hpp
  6. 6 1
      node/Switch.cpp
  7. 53 0
      node/Topology.hpp
  8. 1 1
      version.h

+ 28 - 0
include/ZeroTierOne.h

@@ -116,6 +116,11 @@ extern "C" {
  */
 #define ZT_MAX_PEER_NETWORK_PATHS 4
 
+/**
+ * Maximum number of trusted physical network paths
+ */
+#define ZT_MAX_TRUSTED_PATHS 16
+
 /**
  * Maximum number of hops in a ZeroTier circuit test
  *
@@ -1837,6 +1842,29 @@ void ZT_Node_clusterHandleIncomingMessage(ZT_Node *node,const void *msg,unsigned
  */
 void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs);
 
+/**
+ * Set trusted paths
+ *
+ * A trusted path is a physical network (network/bits) over which both
+ * encryption and authentication can be skipped to improve performance.
+ * Each trusted path must have a non-zero unique ID that is the same across
+ * all participating nodes.
+ *
+ * We don't recommend using trusted paths at all unless you really *need*
+ * near-bare-metal performance. Even on a LAN authentication and encryption
+ * are never a bad thing, and anything that introduces an "escape hatch"
+ * for encryption should be treated with the utmost care.
+ *
+ * Calling with NULL pointers for networks and ids and a count of zero clears
+ * all trusted paths.
+ *
+ * @param node Node instance
+ * @param networks Array of [count] networks
+ * @param ids Array of [count] corresponding non-zero path IDs (zero path IDs are ignored)
+ * @param count Number of trusted paths-- values greater than ZT_MAX_TRUSTED_PATHS are clipped
+ */
+void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
+
 /**
  * Do things in the background until Node dies
  *

+ 21 - 5
node/IncomingPacket.cpp

@@ -42,9 +42,21 @@ namespace ZeroTier {
 
 bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 {
-	const Address sourceAddress(source());
 	try {
-		if ((cipher() == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
+		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
+		const unsigned int c = cipher();
+		bool trusted = false;
+		if (c == ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH) {
+			// If this is marked as a packet via a trusted path, check source address and path ID.
+			// Obviously if no trusted paths are configured this always returns false and such
+			// packets are dropped on the floor.
+			if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
+				trusted = true;
+			} else {
+				TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %.16llx@%s is not trusted!",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
+				return true;
+			}
+		} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
 			// Unencrypted HELLOs require some potentially expensive verification, so
 			// do this in the background if background processing is enabled.
 			if ((RR->dpEnabled > 0)&&(!deferred)) {
@@ -59,12 +71,16 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 			}
 		}
 
+		const Address sourceAddress(source());
 		SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
 		if (peer) {
-			if (!dearmor(peer->key())) {
-				TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size());
-				return true;
+			if (!trusted) {
+				if (!dearmor(peer->key())) {
+					TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),size());
+					return true;
+				}
 			}
+
 			if (!uncompress()) {
 				TRACE("dropped packet from %s(%s), compressed data invalid",peer->address().toString().c_str(),_remoteAddress.toString().c_str());
 				return true;

+ 12 - 0
node/Node.cpp

@@ -745,6 +745,11 @@ void Node::postCircuitTestReport(const ZT_CircuitTestReport *report)
 		(reinterpret_cast<void (*)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *)>((*i)->_internalPtr))(reinterpret_cast<ZT_Node *>(this),*i,report);
 }
 
+void Node::setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+{
+	RR->topology->setTrustedPaths(reinterpret_cast<const InetAddress *>(networks),ids,count);
+}
+
 } // namespace ZeroTier
 
 /****************************************************************************/
@@ -1014,6 +1019,13 @@ void ZT_Node_clusterStatus(ZT_Node *node,ZT_ClusterStatus *cs)
 	} catch ( ... ) {}
 }
 
+void ZT_Node_setTrustedPaths(ZT_Node *node,const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count)
+{
+	try {
+		reinterpret_cast<ZeroTier::Node *>(node)->setTrustedPaths(networks,ids,count);
+	} catch ( ... ) {}
+}
+
 void ZT_Node_backgroundThreadMain(ZT_Node *node)
 {
 	try {

+ 1 - 12
node/Node.hpp

@@ -248,26 +248,15 @@ public:
 	 */
 	inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
 
-	/**
-	 * @return True if we appear to be online
-	 */
 	inline bool online() const throw() { return _online; }
 
 #ifdef ZT_TRACE
 	void postTrace(const char *module,unsigned int line,const char *fmt,...);
 #endif
 
-	/**
-	 * @return Next 64-bit random number (not for cryptographic use)
-	 */
 	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);
+	void setTrustedPaths(const struct sockaddr_storage *networks,const uint64_t *ids,unsigned int count);
 
 private:
 	inline SharedPtr<Network> _network(uint64_t nwid) const

+ 42 - 8
node/Packet.hpp

@@ -57,11 +57,13 @@
  *   + Supports in-band world (root server definition) updates
  *   + Clustering! (Though this will work with protocol v4 clients.)
  *   + Otherwise backward compatible with protocol v4
- * 6 - 1.1.5 ... CURRENT
+ * 6 - 1.1.5 ... 1.1.10
  *   + Deprecate old dictionary-based network config format
  *   + Introduce new binary serialized network config and meta-data
+ * 7 - 1.1.10 -- CURRENT
+ *   + Introduce trusted paths for local SDN use
  */
-#define ZT_PROTO_VERSION 6
+#define ZT_PROTO_VERSION 7
 
 /**
  * Minimum supported protocol version
@@ -100,10 +102,21 @@
 #define ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012 1
 
 /**
- * DEPRECATED payload encrypted flag, will be removed for re-use soon.
+ * Cipher suite: NONE
  *
- * This has been replaced by the three-bit cipher suite selection field where
- * a value of 0 indicates unencrypted (but authenticated) messages.
+ * This differs from POLY1305/NONE in that *no* crypto is done, not even
+ * authentication. This is for trusted local LAN interconnects for internal
+ * SDN use within a data center.
+ *
+ * For this mode the MAC field becomes a trusted path ID and must match the
+ * configured ID of a trusted path or the packet is discarded.
+ */
+#define ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH 2
+
+/**
+ * DEPRECATED payload encrypted flag, may be re-used in the future.
+ *
+ * This has been replaced by the three-bit cipher suite selection field.
  */
 #define ZT_PROTO_FLAG_ENCRYPTED 0x80
 
@@ -337,7 +350,7 @@ namespace ZeroTier {
  *   <[5] destination ZT address>
  *   <[5] source ZT address>
  *   <[1] flags/cipher/hops>
- *   <[8] 64-bit MAC>
+ *   <[8] 64-bit MAC (or trusted path ID in trusted path mode)>
  *   [... -- begin encryption envelope -- ...]
  *   <[1] encrypted flags (MS 3 bits) and verb (LS 5 bits)>
  *   [... verb-specific payload ...]
@@ -1218,7 +1231,6 @@ public:
 	 */
 	inline unsigned int cipher() const
 	{
-		// Note: this uses the new cipher spec field, which is incompatible with <1.0.0 peers
 		return (((unsigned int)(*this)[ZT_PACKET_IDX_FLAGS] & 0x38) >> 3);
 	}
 
@@ -1229,12 +1241,30 @@ public:
 	{
 		unsigned char &b = (*this)[ZT_PACKET_IDX_FLAGS];
 		b = (b & 0xc7) | (unsigned char)((c << 3) & 0x38); // bits: FFCCCHHH
-		// DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
+		// Set DEPRECATED "encrypted" flag -- used by pre-1.0.3 peers
 		if (c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_SALSA2012)
 			b |= ZT_PROTO_FLAG_ENCRYPTED;
 		else b &= (~ZT_PROTO_FLAG_ENCRYPTED);
 	}
 
+	/**
+	 * Get the trusted path ID for this packet (only meaningful if cipher is trusted path)
+	 *
+	 * @return Trusted path ID (from MAC field)
+	 */
+	inline uint64_t trustedPathId() const { return at<uint64_t>(ZT_PACKET_IDX_MAC); }
+
+	/**
+	 * Set this packet's trusted path ID and set the cipher spec to trusted path
+	 *
+	 * @param tpid Trusted path ID
+	 */
+	inline void setTrusted(const uint64_t tpid)
+	{
+		setCipher(ZT_PROTO_CIPHER_SUITE__NO_CRYPTO_TRUSTED_PATH);
+		setAt(ZT_PACKET_IDX_MAC,tpid);
+	}
+
 	/**
 	 * Get this packet's unique ID (the IV field interpreted as uint64_t)
 	 *
@@ -1278,6 +1308,10 @@ public:
 	/**
 	 * Verify and (if encrypted) decrypt packet
 	 *
+	 * This does not handle trusted path mode packets and will return false
+	 * for these. These are handled in IncomingPacket if the sending physical
+	 * address and MAC field match a trusted path.
+	 *
 	 * @param key 32-byte key
 	 * @return False if packet is invalid or failed MAC authenticity check
 	 */

+ 6 - 1
node/Switch.cpp

@@ -849,7 +849,12 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
 		unsigned int chunkSize = std::min(tmp.size(),(unsigned int)ZT_UDP_DEFAULT_PAYLOAD_MTU);
 		tmp.setFragmented(chunkSize < tmp.size());
 
-		tmp.armor(peer->key(),encrypt);
+		const uint64_t trustedPathId = RR->topology->getOutboundPathTrust(viaPath->address());
+		if (trustedPathId) {
+			tmp.setTrusted(trustedPathId);
+		} else {
+			tmp.armor(peer->key(),encrypt);
+		}
 
 		if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
 			if (chunkSize < tmp.size()) {

+ 53 - 0
node/Topology.hpp

@@ -28,6 +28,7 @@
 #include <utility>
 
 #include "Constants.hpp"
+#include "../include/ZeroTierOne.h"
 
 #include "Address.hpp"
 #include "Identity.hpp"
@@ -252,12 +253,64 @@ public:
 	 */
 	inline bool amRoot() const throw() { return _amRoot; }
 
+	/**
+	 * Get the outbound trusted path ID for a physical address, or 0 if none
+	 *
+	 * @param physicalAddress Physical address to which we are sending the packet
+	 * @return Trusted path ID or 0 if none (0 is not a valid trusted path ID)
+	 */
+	inline uint64_t getOutboundPathTrust(const InetAddress &physicalAddress)
+	{
+		for(unsigned int i=0;i<_trustedPathCount;++i) {
+			if (_trustedPathNetworks[i].containsAddress(physicalAddress))
+				return _trustedPathIds[i];
+		}
+		return 0;
+	}
+
+	/**
+	 * Check whether in incoming trusted path marked packet is valid
+	 *
+	 * @param physicalAddress Originating physical address
+	 * @param trustedPathId Trusted path ID from packet (from MAC field)
+	 */
+	inline bool shouldInboundPathBeTrusted(const InetAddress &physicalAddress,const uint64_t trustedPathId)
+	{
+		for(unsigned int i=0;i<_trustedPathCount;++i) {
+			if ((_trustedPathIds[i] == trustedPathId)&&(_trustedPathNetworks[i].containsAddress(physicalAddress)))
+				return true;
+		}
+		return false;
+	}
+
+	/**
+	 * Set trusted paths in this topology
+	 *
+	 * @param networks Array of networks (prefix/netmask bits)
+	 * @param ids Array of trusted path IDs
+	 * @param count Number of trusted paths (if larger than ZT_MAX_TRUSTED_PATHS overflow is ignored)
+	 */
+	inline void setTrustedPaths(const InetAddress *networks,const uint64_t *ids,unsigned int count)
+	{
+		if (count > ZT_MAX_TRUSTED_PATHS)
+			count = ZT_MAX_TRUSTED_PATHS;
+		Mutex::Lock _l(_lock);
+		for(unsigned int i=0;i<count;++i) {
+			_trustedPathIds[i] = ids[i];
+			_trustedPathNetworks[i] = networks[i];
+		}
+		_trustedPathCount = count;
+	}
+
 private:
 	Identity _getIdentity(const Address &zta);
 	void _setWorld(const World &newWorld);
 
 	const RuntimeEnvironment *const RR;
 
+	uint64_t _trustedPathIds[ZT_MAX_TRUSTED_PATHS];
+	InetAddress _trustedPathNetworks[ZT_MAX_TRUSTED_PATHS];
+	unsigned int _trustedPathCount;
 	World _world;
 	Hashtable< Address,SharedPtr<Peer> > _peers;
 	std::vector< Address > _rootAddresses;

+ 1 - 1
version.h

@@ -32,6 +32,6 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 8
+#define ZEROTIER_ONE_VERSION_REVISION 9
 
 #endif