From f86e9b7df28c287f7111f92007bed0d273416da3 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 8 Jul 2016 13:57:16 -0700
Subject: [PATCH 01/20] Bump version in RPM spec and DEB.

---
 debian/changelog  | 6 ++++++
 zerotier-one.spec | 5 ++++-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/debian/changelog b/debian/changelog
index c6e9eb90f..10c524d67 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+zerotier-one (1.1.8) unstable; urgency=low
+
+  * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com>  Fri, 08 Jul 2016 01:56:00 -0700
+
 zerotier-one (1.1.6) unstable; urgency=medium
 
   * First Debian release on ZeroTier, Inc. private apt repository.
diff --git a/zerotier-one.spec b/zerotier-one.spec
index beda4cca8..3b9d2a0cd 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,5 +1,5 @@
 Name:           zerotier-one
-Version:        1.1.6
+Version:        1.1.8
 Release:        0.1%{?dist}
 Summary:        ZeroTier One network virtualization service
 
@@ -149,6 +149,9 @@ esac
 %endif
 
 %changelog
+* Fri Jul 08 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.8-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
 * Sat Jun 25 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.6-0.1
 - now builds on CentOS 6 as well as newer distros, and some cleanup
 

From 2d598dd433167912b0036ba51dc4dc9b7c2e6b39 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 8 Jul 2016 14:25:25 -0700
Subject: [PATCH 02/20] Automatically add symlinks to redhat distro.

---
 linux-build-farm/make-rpm-repos.sh | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/linux-build-farm/make-rpm-repos.sh b/linux-build-farm/make-rpm-repos.sh
index d669501b4..0ed1cfe4c 100755
--- a/linux-build-farm/make-rpm-repos.sh
+++ b/linux-build-farm/make-rpm-repos.sh
@@ -31,5 +31,34 @@ for db in `find /tmp/zt-rpm-repo -mindepth 2 -maxdepth 2 -type d`; do
 	createrepo --database $db
 done
 
+# Stupid RHEL stuff
+cd /tmp/zt-rpm-repo/el
+ln -sf 6 6Client
+ln -sf 6 6Workstation
+ln -sf 6 6Server
+ln -sf 6 6.0
+ln -sf 6 6.1
+ln -sf 6 6.2
+ln -sf 6 6.3
+ln -sf 6 6.4
+ln -sf 6 6.5
+ln -sf 6 6.6
+ln -sf 6 6.7
+ln -sf 6 6.8
+ln -sf 6 6.9
+ln -sf 7 7Client
+ln -sf 7 7Workstation
+ln -sf 7 7Server
+ln -sf 7 7.0
+ln -sf 7 7.1
+ln -sf 7 7.2
+ln -sf 7 7.3
+ln -sf 7 7.4
+ln -sf 7 7.5
+ln -sf 7 7.6
+ln -sf 7 7.7
+ln -sf 7 7.8
+ln -sf 7 7.9
+
 echo
 echo Repo created in /tmp/zt-rpm-repo

From aff62e9e10c164c005f7b4f9bc78b50c2c6fcca1 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Fri, 8 Jul 2016 14:28:34 -0700
Subject: [PATCH 03/20] Version bump in Mac pkg.

---
 ext/installfiles/mac/ZeroTier One.pkgproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index e090c5747..863046f7a 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj	
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj	
@@ -759,7 +759,7 @@
 			<key>OVERWRITE_PERMISSIONS</key>
 			<false/>
 			<key>VERSION</key>
-			<string>1.1.6</string>
+			<string>1.1.8</string>
 		</dict>
 		<key>PROJECT_COMMENTS</key>
 		<dict>

From 765082fdb68d8847cbd53cb442cbed5006b28d5f Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 08:29:50 -0700
Subject: [PATCH 04/20] Trusted path support, and version bump to 1.1.9

---
 include/ZeroTierOne.h   | 28 ++++++++++++++++++++++
 node/IncomingPacket.cpp | 26 ++++++++++++++++----
 node/Node.cpp           | 12 ++++++++++
 node/Node.hpp           | 13 +---------
 node/Packet.hpp         | 50 +++++++++++++++++++++++++++++++-------
 node/Switch.cpp         |  7 +++++-
 node/Topology.hpp       | 53 +++++++++++++++++++++++++++++++++++++++++
 version.h               |  2 +-
 8 files changed, 164 insertions(+), 27 deletions(-)

diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index d46c64b8d..f55234618 100644
--- a/include/ZeroTierOne.h
+++ b/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
  *
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 871297f75..fb4562ab6 100644
--- a/node/IncomingPacket.cpp
+++ b/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;
diff --git a/node/Node.cpp b/node/Node.cpp
index bedbba946..058df32d2 100644
--- a/node/Node.cpp
+++ b/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 {
diff --git a/node/Node.hpp b/node/Node.hpp
index 6ac23ca0f..0a39d1eed 100644
--- a/node/Node.hpp
+++ b/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
diff --git a/node/Packet.hpp b/node/Packet.hpp
index 79fff344d..3d95b0baa 100644
--- a/node/Packet.hpp
+++ b/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
 	 */
diff --git a/node/Switch.cpp b/node/Switch.cpp
index b134cc693..bf3afe33c 100644
--- a/node/Switch.cpp
+++ b/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()) {
diff --git a/node/Topology.hpp b/node/Topology.hpp
index 86fbb011f..03c491e50 100644
--- a/node/Topology.hpp
+++ b/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;
diff --git a/version.h b/version.h
index 012660dd4..b0df04f8f 100644
--- a/version.h
+++ b/version.h
@@ -32,6 +32,6 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 8
+#define ZEROTIER_ONE_VERSION_REVISION 9
 
 #endif

From 653573025598c982aa1785cc859e08336c7174ec Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 08:42:36 -0700
Subject: [PATCH 05/20] GitHub issue #352

---
 service/OneService.cpp | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/service/OneService.cpp b/service/OneService.cpp
index c1b240506..89d9501d0 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1041,13 +1041,13 @@ public:
 	// Begin private implementation methods
 
 	// Checks if a managed IP or route target is allowed
-	bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &addr)
+	bool checkIfManagedIsAllowed(const NetworkState &n,const InetAddress &target)
 	{
 		if (!n.settings.allowManaged)
 			return false;
-		if (addr.isDefaultRoute())
+		if (target.isDefaultRoute())
 			return n.settings.allowDefault;
-		switch(addr.ipScope()) {
+		switch(target.ipScope()) {
 			case InetAddress::IP_SCOPE_NONE:
 			case InetAddress::IP_SCOPE_MULTICAST:
 			case InetAddress::IP_SCOPE_LOOPBACK:
@@ -1099,10 +1099,12 @@ public:
 			Utils::scopy(tapdev,sizeof(tapdev),n.tap->deviceName().c_str());
 #endif
 
+			std::vector<InetAddress> myIps(n.tap->ips());
+
 			// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
 			for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
 				bool haveRoute = false;
-				if (checkIfManagedIsAllowed(n,mr->target())) {
+				if ( (checkIfManagedIsAllowed(n,mr->target())) && ((!mr->via())||(std::find(myIps.begin(),myIps.end(),mr->via()) == myIps.end())) ) {
 					for(unsigned int i=0;i<n.config.routeCount;++i) {
 						const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 						const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
@@ -1124,7 +1126,7 @@ public:
 				const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 				const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
 
-				if (!checkIfManagedIsAllowed(n,*target))
+				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(std::find(myIps.begin(),myIps.end(),*via) != myIps.end())) )
 					continue;
 
 				bool haveRoute = false;

From 3f2b21ce71be81ae960a4a6197555a92158a74e3 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 08:55:57 -0700
Subject: [PATCH 06/20] Do not shadow our own route twice on Mac if there is no
 default route. (Fix for issue found during IPv6 default route override
 testing.)

---
 osdep/ManagedRoute.cpp | 4 +++-
 osdep/ManagedRoute.hpp | 4 ++--
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
index 549901224..88fd944a4 100644
--- a/osdep/ManagedRoute.cpp
+++ b/osdep/ManagedRoute.cpp
@@ -57,6 +57,8 @@
 #define ZT_LINUX_IP_COMMAND "/sbin/ip"
 #define ZT_LINUX_IP_COMMAND_2 "/usr/sbin/ip"
 
+// NOTE: BSD is mostly tested on Apple/Mac but is likely to work on other BSD too
+
 namespace ZeroTier {
 
 namespace {
@@ -413,7 +415,7 @@ bool ManagedRoute::sync()
 		// Shadow system route if it exists, also delete any obsolete shadows
 		// and replace them with the new state. sync() is called periodically to
 		// allow us to do that if underlying connectivity changes.
-		if ((_systemVia != newSystemVia)||(!strcmp(_systemDevice,newSystemDevice))) {
+		if ( ((_systemVia != newSystemVia)||(!strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
 			if ((_systemVia)&&(_systemDevice[0])) {
 				_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
 				_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
diff --git a/osdep/ManagedRoute.hpp b/osdep/ManagedRoute.hpp
index 5dde12ad2..63310f24b 100644
--- a/osdep/ManagedRoute.hpp
+++ b/osdep/ManagedRoute.hpp
@@ -54,8 +54,8 @@ public:
 	 * "device name."
 	 *
 	 * @param target Route target (e.g. 0.0.0.0/0 for default)
-	 * @param via Route next L3 hop or NULL InetAddress if local
-	 * @param device Device name/ID if 'via' is null and route is local, otherwise ignored
+	 * @param via Route next L3 hop or NULL InetAddress if local in which case it will be routed via device
+	 * @param device Name or hex LUID of ZeroTier device (e.g. zt#)
 	 * @return True if route was successfully set
 	 */
 	inline bool set(const InetAddress &target,const InetAddress &via,const char *device)

From 23391ff9dad8c82858cee8f1bb19ca10bf613bcb Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 09:22:04 -0700
Subject: [PATCH 07/20] More OSX IPv6 fixes.

---
 osdep/ManagedRoute.cpp   | 2 +-
 osdep/OSXEthernetTap.cpp | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/osdep/ManagedRoute.cpp b/osdep/ManagedRoute.cpp
index 88fd944a4..ae2f8943c 100644
--- a/osdep/ManagedRoute.cpp
+++ b/osdep/ManagedRoute.cpp
@@ -415,7 +415,7 @@ bool ManagedRoute::sync()
 		// Shadow system route if it exists, also delete any obsolete shadows
 		// and replace them with the new state. sync() is called periodically to
 		// allow us to do that if underlying connectivity changes.
-		if ( ((_systemVia != newSystemVia)||(!strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
+		if ( ((_systemVia != newSystemVia)||(strcmp(_systemDevice,newSystemDevice))) && (strcmp(_device,newSystemDevice)) ) {
 			if ((_systemVia)&&(_systemDevice[0])) {
 				_routeCmd("delete",leftt,_systemVia,_systemDevice,(const char *)0);
 				_routeCmd("delete",rightt,_systemVia,_systemDevice,(const char *)0);
diff --git a/osdep/OSXEthernetTap.cpp b/osdep/OSXEthernetTap.cpp
index e8c5c1eac..56934c964 100644
--- a/osdep/OSXEthernetTap.cpp
+++ b/osdep/OSXEthernetTap.cpp
@@ -474,7 +474,7 @@ bool OSXEthernetTap::addIp(const InetAddress &ip)
 
 	long cpid = (long)vfork();
 	if (cpid == 0) {
-		::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),ip.isV4() ? "inet" : "inet6",ip.toString().c_str(),"alias",(const char *)0);
+		::execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toString().c_str(),"alias",(const char *)0);
 		::_exit(-1);
 	} else if (cpid > 0) {
 		int exitcode = -1;
@@ -494,7 +494,7 @@ bool OSXEthernetTap::removeIp(const InetAddress &ip)
 		if (*i == ip) {
 			long cpid = (long)vfork();
 			if (cpid == 0) {
-				execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),"inet",ip.toIpString().c_str(),"-alias",(const char *)0);
+				execl("/sbin/ifconfig","/sbin/ifconfig",_dev.c_str(),(ip.ss_family == AF_INET6) ? "inet6" : "inet",ip.toIpString().c_str(),"-alias",(const char *)0);
 				_exit(-1);
 			} else if (cpid > 0) {
 				int exitcode = -1;

From cdb5ceac7b5a368d777df7f7de4fadb2555fc00a Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 09:43:12 -0700
Subject: [PATCH 08/20] Should remove old IPs before trying to add new ones.

---
 service/OneService.cpp | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/service/OneService.cpp b/service/OneService.cpp
index 89d9501d0..c19b266aa 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1075,18 +1075,18 @@ public:
 			std::sort(newManagedIps.begin(),newManagedIps.end());
 			newManagedIps.erase(std::unique(newManagedIps.begin(),newManagedIps.end()),newManagedIps.end());
 
-			for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
-				if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
-					if (!n.tap->addIp(*ip))
-						fprintf(stderr,"ERROR: unable to add ip address %s"ZT_EOL_S, ip->toString().c_str());
-				}
-			}
 			for(std::vector<InetAddress>::iterator ip(n.managedIps.begin());ip!=n.managedIps.end();++ip) {
 				if (std::find(newManagedIps.begin(),newManagedIps.end(),*ip) == newManagedIps.end()) {
 					if (!n.tap->removeIp(*ip))
 						fprintf(stderr,"ERROR: unable to remove ip address %s"ZT_EOL_S, ip->toString().c_str());
 				}
 			}
+			for(std::vector<InetAddress>::iterator ip(newManagedIps.begin());ip!=newManagedIps.end();++ip) {
+				if (std::find(n.managedIps.begin(),n.managedIps.end(),*ip) == n.managedIps.end()) {
+					if (!n.tap->addIp(*ip))
+						fprintf(stderr,"ERROR: unable to add ip address %s"ZT_EOL_S, ip->toString().c_str());
+				}
+			}
 
 			n.managedIps.swap(newManagedIps);
 		}

From 5d7174b162055340eb7622dd2e1e556c5a2dcd42 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 10:11:00 -0700
Subject: [PATCH 09/20] Go back to bundling libhttp-parser on Debian, at least
 for our own internally-created packages, since this library has a massively
 unstable ABI and causes nothing but problems if referenced from the distro.

---
 debian/control                                 | 4 ++--
 linux-build-farm/debian-jessie/x64/Dockerfile  | 2 ++
 linux-build-farm/debian-jessie/x86/Dockerfile  | 2 ++
 linux-build-farm/debian-stretch/x64/Dockerfile | 2 ++
 linux-build-farm/debian-stretch/x86/Dockerfile | 2 ++
 linux-build-farm/debian-wheezy/x64/Dockerfile  | 2 ++
 linux-build-farm/debian-wheezy/x86/Dockerfile  | 2 ++
 linux-build-farm/ubuntu-trusty/x64/Dockerfile  | 2 ++
 linux-build-farm/ubuntu-trusty/x86/Dockerfile  | 2 ++
 linux-build-farm/ubuntu-wily/x64/Dockerfile    | 2 ++
 linux-build-farm/ubuntu-wily/x86/Dockerfile    | 2 ++
 linux-build-farm/ubuntu-xenial/x64/Dockerfile  | 2 ++
 linux-build-farm/ubuntu-xenial/x86/Dockerfile  | 2 ++
 13 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/debian/control b/debian/control
index cfe506824..46b8307ff 100644
--- a/debian/control
+++ b/debian/control
@@ -3,14 +3,14 @@ Maintainer: Adam Ierymenko <adam.ierymenko@zerotier.com>
 Section: net
 Priority: optional
 Standards-Version: 3.9.6
-Build-Depends: debhelper (>= 9), libhttp-parser-dev (>= 2.1), liblz4-dev, libnatpmp-dev, dh-systemd, ruby-ronn
+Build-Depends: debhelper (>= 9), liblz4-dev, libnatpmp-dev, dh-systemd, ruby-ronn
 Vcs-Git: git://github.com/zerotier/ZeroTierOne
 Vcs-Browser: https://github.com/zerotier/ZeroTierOne
 Homepage: https://www.zerotier.com/
 
 Package: zerotier-one
 Architecture: any
-Depends:  ${shlibs:Depends}, ${misc:Depends}, libhttp-parser2.1, liblz4-1, libnatpmp1, iproute2
+Depends:  ${shlibs:Depends}, ${misc:Depends}, liblz4-1, libnatpmp1, iproute2
 Homepage: https://www.zerotier.com/
 Description: ZeroTier network virtualization service
  ZeroTier One lets you join ZeroTier virtual networks and
diff --git a/linux-build-farm/debian-jessie/x64/Dockerfile b/linux-build-farm/debian-jessie/x64/Dockerfile
index 9591eff66..316c1d83e 100644
--- a/linux-build-farm/debian-jessie/x64/Dockerfile
+++ b/linux-build-farm/debian-jessie/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-jessie/x86/Dockerfile b/linux-build-farm/debian-jessie/x86/Dockerfile
index 9ed826ff5..3ad83329f 100644
--- a/linux-build-farm/debian-jessie/x86/Dockerfile
+++ b/linux-build-farm/debian-jessie/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x64/Dockerfile b/linux-build-farm/debian-stretch/x64/Dockerfile
index c5fdc2a19..c973c2b7b 100644
--- a/linux-build-farm/debian-stretch/x64/Dockerfile
+++ b/linux-build-farm/debian-stretch/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 #RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
 #RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-stretch/x86/Dockerfile b/linux-build-farm/debian-stretch/x86/Dockerfile
index 159e83c76..bfc7a86fa 100644
--- a/linux-build-farm/debian-stretch/x86/Dockerfile
+++ b/linux-build-farm/debian-stretch/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 #RUN ln -sf /usr/bin/clang++-3.5 /usr/bin/clang++
 #RUN ln -sf /usr/bin/clang-3.5 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/debian-wheezy/x64/Dockerfile b/linux-build-farm/debian-wheezy/x64/Dockerfile
index 5663f1e17..77e1c3259 100644
--- a/linux-build-farm/debian-wheezy/x64/Dockerfile
+++ b/linux-build-farm/debian-wheezy/x64/Dockerfile
@@ -4,6 +4,8 @@ MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
 RUN apt-get update
 RUN apt-get install -y build-essential debhelper ruby-ronn g++ make devscripts
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
 
 RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
diff --git a/linux-build-farm/debian-wheezy/x86/Dockerfile b/linux-build-farm/debian-wheezy/x86/Dockerfile
index 7b7a632a8..1f0117d27 100644
--- a/linux-build-farm/debian-wheezy/x86/Dockerfile
+++ b/linux-build-farm/debian-wheezy/x86/Dockerfile
@@ -7,6 +7,8 @@
 FROM zerotier/zt1-build-debian-wheezy-x86-base
 MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
 
 RUN mv -f /ZeroTierOne/debian/control.wheezy /ZeroTierOne/debian/control
diff --git a/linux-build-farm/ubuntu-trusty/x64/Dockerfile b/linux-build-farm/ubuntu-trusty/x64/Dockerfile
index 6ec65d2ff..f84cc6e36 100644
--- a/linux-build-farm/ubuntu-trusty/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-trusty/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-trusty/x86/Dockerfile b/linux-build-farm/ubuntu-trusty/x86/Dockerfile
index 271c19bbe..6be3ae872 100644
--- a/linux-build-farm/ubuntu-trusty/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-trusty/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.6 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.6 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x64/Dockerfile b/linux-build-farm/ubuntu-wily/x64/Dockerfile
index f56344cfd..99b8d34cd 100644
--- a/linux-build-farm/ubuntu-wily/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-wily/x64/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-wily/x86/Dockerfile b/linux-build-farm/ubuntu-wily/x86/Dockerfile
index 24bb11169..86ad14f21 100644
--- a/linux-build-farm/ubuntu-wily/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-wily/x86/Dockerfile
@@ -7,4 +7,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 RUN ln -sf /usr/bin/clang++-3.7 /usr/bin/clang++
 RUN ln -sf /usr/bin/clang-3.7 /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x64/Dockerfile b/linux-build-farm/ubuntu-xenial/x64/Dockerfile
index 03a65c55e..fa665a0ac 100644
--- a/linux-build-farm/ubuntu-xenial/x64/Dockerfile
+++ b/linux-build-farm/ubuntu-xenial/x64/Dockerfile
@@ -9,4 +9,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 
 RUN rm -f /usr/bin/clang++ /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/ubuntu-xenial/x86/Dockerfile b/linux-build-farm/ubuntu-xenial/x86/Dockerfile
index 3d04a57df..d01eec9bb 100644
--- a/linux-build-farm/ubuntu-xenial/x86/Dockerfile
+++ b/linux-build-farm/ubuntu-xenial/x86/Dockerfile
@@ -9,4 +9,6 @@ RUN apt-get install -y build-essential debhelper libhttp-parser-dev liblz4-dev l
 
 RUN rm -f /usr/bin/clang++ /usr/bin/clang
 
+RUN dpkg --purge libhttp-parser-dev
+
 ADD zt1-src.tar.gz /

From 96576757552f1b0ce002df3e904419b7bfca62f8 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 11:30:22 -0700
Subject: [PATCH 10/20] Plumbing through trusted path stuff to OneService.

---
 include/ZeroTierOne.h    |  5 +++++
 node/IncomingPacket.cpp  | 10 ++++++----
 node/Node.cpp            |  1 +
 service/ControlPlane.cpp |  4 +++-
 service/OneService.cpp   | 32 ++++++++++++++++++++++++++++++++
 5 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/include/ZeroTierOne.h b/include/ZeroTierOne.h
index f55234618..2d7b007b4 100644
--- a/include/ZeroTierOne.h
+++ b/include/ZeroTierOne.h
@@ -892,6 +892,11 @@ typedef struct
 	 */
 	uint64_t lastReceive;
 
+	/**
+	 * Is this a trusted path? If so this will be its nonzero ID.
+	 */
+	uint64_t trustedPathId;
+
 	/**
 	 * Is path active?
 	 */
diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index fb4562ab6..6e1eb493f 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -43,6 +43,8 @@ namespace ZeroTier {
 bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 {
 	try {
+		const Address sourceAddress(source());
+
 		// Check for trusted paths or unencrypted HELLOs (HELLO is the only packet sent in the clear)
 		const unsigned int c = cipher();
 		bool trusted = false;
@@ -52,8 +54,9 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 			// packets are dropped on the floor.
 			if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
 				trusted = true;
+				printf("TRUSTED PATH packet from %s(%s), trusted path ID %llx\n",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
 			} 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());
+				TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.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)) {
@@ -71,18 +74,17 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 			}
 		}
 
-		const Address sourceAddress(source());
 		SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
 		if (peer) {
 			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());
+					TRACE("dropped packet from %s(%s), MAC authentication failed (size: %u)",sourceAddress.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());
+				TRACE("dropped packet from %s(%s), compressed data invalid",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str());
 				return true;
 			}
 
diff --git a/node/Node.cpp b/node/Node.cpp
index 058df32d2..130850286 100644
--- a/node/Node.cpp
+++ b/node/Node.cpp
@@ -447,6 +447,7 @@ ZT_PeerList *Node::peers() const
 			p->paths[p->pathCount].lastReceive = path->lastReceived();
 			p->paths[p->pathCount].active = path->active(_now) ? 1 : 0;
 			p->paths[p->pathCount].preferred = ((bestPath)&&(*path == *bestPath)) ? 1 : 0;
+			p->paths[p->pathCount].trustedPathId = RR->topology->getOutboundPathTrust(path->address());
 			++p->pathCount;
 		}
 	}
diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index 0e2b530d7..ced36e75f 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -190,13 +190,15 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *
 			"%s\t\"lastSend\": %llu,\n"
 			"%s\t\"lastReceive\": %llu,\n"
 			"%s\t\"active\": %s,\n"
-			"%s\t\"preferred\": %s\n"
+			"%s\t\"preferred\": %s,\n"
+			"%s\t\"trustedPathId\": %llx\n"
 			"%s}",
 			prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
 			prefix,pp[i].lastSend,
 			prefix,pp[i].lastReceive,
 			prefix,(pp[i].active == 0) ? "false" : "true",
 			prefix,(pp[i].preferred == 0) ? "false" : "true",
+			prefix,pp[i].trustedPathId,
 			prefix);
 		buf.append(json);
 	}
diff --git a/service/OneService.cpp b/service/OneService.cpp
index c19b266aa..bbd15965d 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -759,6 +759,38 @@ public:
 			for(int i=0;i<3;++i)
 				_portsBE[i] = Utils::hton((uint16_t)_ports[i]);
 
+			{
+				FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustpaths").c_str(),"r");
+				uint64_t ids[ZT_MAX_TRUSTED_PATHS];
+				InetAddress addresses[ZT_MAX_TRUSTED_PATHS];
+				if (trustpaths) {
+					char buf[1024];
+					unsigned int count = 0;
+					while ((fgets(buf,sizeof(buf),trustpaths))&&(count < ZT_MAX_TRUSTED_PATHS)) {
+						int fno = 0;
+						char *saveptr = (char *)0;
+						uint64_t trustedPathId = 0;
+						InetAddress trustedPathNetwork;
+						for(char *f=Utils::stok(buf,"=\r\n \t",&saveptr);(f);f=Utils::stok((char *)0,"=\r\n \t",&saveptr)) {
+							if (fno == 0) {
+								trustedPathId = Utils::hexStrToU64(f);
+							} else if (fno == 1) {
+								trustedPathNetwork = InetAddress(f);
+							} else break;
+							++fno;
+						}
+						if ( (trustedPathId != 0) && ((trustedPathNetwork.ss_family == AF_INET)||(trustedPathNetwork.ss_family == AF_INET6)) && (trustedPathNetwork.ipScope() != InetAddress::IP_SCOPE_GLOBAL) && (trustedPathNetwork.netmaskBits() > 0) ) {
+							ids[count] = trustedPathId;
+							addresses[count] = trustedPathNetwork;
+							++count;
+						}
+					}
+					fclose(trustpaths);
+					if (count)
+						_node->setTrustedPaths(reinterpret_cast<const struct sockaddr_storage *>(addresses),ids,count);
+				}
+			}
+
 #ifdef ZT_ENABLE_NETWORK_CONTROLLER
 			_controller = new SqliteNetworkController(_node,(_homePath + ZT_PATH_SEPARATOR_S + ZT_CONTROLLER_DB_PATH).c_str(),(_homePath + ZT_PATH_SEPARATOR_S + "circuitTestResults.d").c_str());
 			_node->setNetconfMaster((void *)_controller);

From 9785fde32aae379c68a1bf3c7a651b064239254f Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 11:40:45 -0700
Subject: [PATCH 11/20] Trusted paths work!

---
 node/IncomingPacket.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/node/IncomingPacket.cpp b/node/IncomingPacket.cpp
index 6e1eb493f..231f0d062 100644
--- a/node/IncomingPacket.cpp
+++ b/node/IncomingPacket.cpp
@@ -54,7 +54,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,bool deferred)
 			// packets are dropped on the floor.
 			if (RR->topology->shouldInboundPathBeTrusted(_remoteAddress,trustedPathId())) {
 				trusted = true;
-				printf("TRUSTED PATH packet from %s(%s), trusted path ID %llx\n",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
+				TRACE("TRUSTED PATH packet approved from %s(%s), trusted path ID %llx",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId());
 			} else {
 				TRACE("dropped packet from %s(%s), cipher set to trusted path mode but path %llx@%s is not trusted!",sourceAddress.toString().c_str(),_remoteAddress.toString().c_str(),trustedPathId(),_remoteAddress.toString().c_str());
 				return true;

From b5e9d3f6f413de4e28caf4c32024a2a1f7ab1d2f Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 11:41:19 -0700
Subject: [PATCH 12/20] Rename file to be intuitive.

---
 service/OneService.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/service/OneService.cpp b/service/OneService.cpp
index bbd15965d..71bf8e68d 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -760,7 +760,7 @@ public:
 				_portsBE[i] = Utils::hton((uint16_t)_ports[i]);
 
 			{
-				FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustpaths").c_str(),"r");
+				FILE *trustpaths = fopen((_homePath + ZT_PATH_SEPARATOR_S + "trustedpaths").c_str(),"r");
 				uint64_t ids[ZT_MAX_TRUSTED_PATHS];
 				InetAddress addresses[ZT_MAX_TRUSTED_PATHS];
 				if (trustpaths) {

From 1861f67fa7abdc3b1e92c9c8b34c257fc93e8c85 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 12:21:36 -0700
Subject: [PATCH 13/20] Version bump to 1.1.10

---
 version.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/version.h b/version.h
index b0df04f8f..2facbffcb 100644
--- a/version.h
+++ b/version.h
@@ -32,6 +32,6 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 9
+#define ZEROTIER_ONE_VERSION_REVISION 10
 
 #endif

From 34e7c8652a48b01b9e8ed993ed29606d159e4165 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 12:30:35 -0700
Subject: [PATCH 14/20] Version bumps.

---
 debian/changelog                          | 7 +++++++
 ext/installfiles/mac/ZeroTier One.pkgproj | 2 +-
 zerotier-one.spec                         | 5 ++++-
 3 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 10c524d67..46b2d04ee 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+zerotier-one (1.1.10) unstable; urgency=medium
+
+  * See https://github.com/zerotier/ZeroTierOne for release notes.
+  * ZeroTier Debian packages no longer depend on http-parser since its ABI is too unstable.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com>  Tue, 12 Jul 2016 12:29:00 -0700
+
 zerotier-one (1.1.8) unstable; urgency=low
 
   * See https://github.com/zerotier/ZeroTierOne for release notes.
diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index 863046f7a..34198ca0b 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj	
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj	
@@ -759,7 +759,7 @@
 			<key>OVERWRITE_PERMISSIONS</key>
 			<false/>
 			<key>VERSION</key>
-			<string>1.1.8</string>
+			<string>1.1.10</string>
 		</dict>
 		<key>PROJECT_COMMENTS</key>
 		<dict>
diff --git a/zerotier-one.spec b/zerotier-one.spec
index 3b9d2a0cd..aba188782 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,5 +1,5 @@
 Name:           zerotier-one
-Version:        1.1.8
+Version:        1.1.10
 Release:        0.1%{?dist}
 Summary:        ZeroTier One network virtualization service
 
@@ -149,6 +149,9 @@ esac
 %endif
 
 %changelog
+* Tue Jul 12 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.10-0.1
+- see https://github.com/zerotier/ZeroTierOne for release notes
+
 * Fri Jul 08 2016 Adam Ierymenko <adam.ierymenko@zerotier.com> - 1.1.8-0.1
 - see https://github.com/zerotier/ZeroTierOne for release notes
 

From a6d9ae3a36d2abbc3058da21518d5953c04fccbc Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 13:30:15 -0700
Subject: [PATCH 15/20] Man page fix on fedora-22

---
 linux-build-farm/fedora-22/x64/Dockerfile | 1 +
 linux-build-farm/fedora-22/x86/Dockerfile | 1 +
 2 files changed, 2 insertions(+)

diff --git a/linux-build-farm/fedora-22/x64/Dockerfile b/linux-build-farm/fedora-22/x64/Dockerfile
index 6874355c5..6da0a9213 100644
--- a/linux-build-farm/fedora-22/x64/Dockerfile
+++ b/linux-build-farm/fedora-22/x64/Dockerfile
@@ -5,5 +5,6 @@ RUN yum update -y
 RUN yum install -y make rpmdevtools gcc-c++ rubygem-ronn json-parser-devel lz4-devel http-parser-devel libnatpmp-devel
 
 RUN rpm --erase http-parser-devel
+RUN yum install -y rubygem-ronn ruby
 
 ADD zt1-src.tar.gz /
diff --git a/linux-build-farm/fedora-22/x86/Dockerfile b/linux-build-farm/fedora-22/x86/Dockerfile
index cb264482e..3c24b844b 100644
--- a/linux-build-farm/fedora-22/x86/Dockerfile
+++ b/linux-build-farm/fedora-22/x86/Dockerfile
@@ -14,5 +14,6 @@ MAINTAINER Adam Ierymenko <adam.ierymenko@zerotier.com>
 RUN echo 'i686-redhat-linux' >/etc/rpm/platform
 
 RUN rpm --erase http-parser-devel
+RUN yum install -y rubygem-ronn ruby
 
 ADD zt1-src.tar.gz /

From 4b5e43ab0df43e06f4aea3491fd05c0bbd86a236 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 13:37:38 -0700
Subject: [PATCH 16/20] VERSION 1.1.10: minor managed route fixes

Version 1.1.10 is a minor bug fix release. It fixes the following two issues:

 * Routes are no longer added 'via' IP addresses of the current node's ZT interface.
 * IPv6 default route is now fixed on Mac.
 * IP addresses are now deleted before new ones are added to allow changes to netmask
   on Mac and some other platforms.

This version also quietly introduces a feature called trusted paths. We'll be
documenting this in our community section soon. This is an experimental feature
for in-LAN SDN use.

From 6ec3464ee94dc9059477c6ef8f7c4d7e4045b914 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 13:58:15 -0700
Subject: [PATCH 17/20] JSON fix for trusted paths (does not affect normal op)

---
 service/ControlPlane.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/service/ControlPlane.cpp b/service/ControlPlane.cpp
index ced36e75f..a10697a97 100644
--- a/service/ControlPlane.cpp
+++ b/service/ControlPlane.cpp
@@ -191,7 +191,7 @@ static std::string _jsonEnumerate(unsigned int depth,const ZT_PeerPhysicalPath *
 			"%s\t\"lastReceive\": %llu,\n"
 			"%s\t\"active\": %s,\n"
 			"%s\t\"preferred\": %s,\n"
-			"%s\t\"trustedPathId\": %llx\n"
+			"%s\t\"trustedPathId\": %llu\n"
 			"%s}",
 			prefix,_jsonEscape(reinterpret_cast<const InetAddress *>(&(pp[i].address))->toString()).c_str(),
 			prefix,pp[i].lastSend,

From 89125150112fecca2372e5531bfefe6becd91129 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 15:00:30 -0700
Subject: [PATCH 18/20] Fix to fix to GitHub issue #352

---
 service/OneService.cpp | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/service/OneService.cpp b/service/OneService.cpp
index 71bf8e68d..13820f5ca 100644
--- a/service/OneService.cpp
+++ b/service/OneService.cpp
@@ -1092,6 +1092,16 @@ public:
 		}
 	}
 
+	// Match only an IP from a vector of IPs -- used in syncManagedStuff()
+	bool matchIpOnly(const std::vector<InetAddress> &ips,const InetAddress &ip) const
+	{
+		for(std::vector<InetAddress>::const_iterator i(ips.begin());i!=ips.end();++i) {
+			if (i->ipsEqual(ip))
+				return true;
+		}
+		return false;
+	}
+
 	// Apply or update managed IPs for a configured network (be sure n.tap exists)
 	void syncManagedStuff(NetworkState &n,bool syncIps,bool syncRoutes)
 	{
@@ -1136,7 +1146,7 @@ public:
 			// Nuke applied routes that are no longer in n.config.routes[] and/or are not allowed
 			for(std::list<ManagedRoute>::iterator mr(n.managedRoutes.begin());mr!=n.managedRoutes.end();) {
 				bool haveRoute = false;
-				if ( (checkIfManagedIsAllowed(n,mr->target())) && ((!mr->via())||(std::find(myIps.begin(),myIps.end(),mr->via()) == myIps.end())) ) {
+				if ( (checkIfManagedIsAllowed(n,mr->target())) && ((mr->via().ss_family != mr->target().ss_family)||(!matchIpOnly(myIps,mr->via()))) ) {
 					for(unsigned int i=0;i<n.config.routeCount;++i) {
 						const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 						const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
@@ -1158,7 +1168,7 @@ public:
 				const InetAddress *const target = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].target));
 				const InetAddress *const via = reinterpret_cast<const InetAddress *>(&(n.config.routes[i].via));
 
-				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(std::find(myIps.begin(),myIps.end(),*via) != myIps.end())) )
+				if ( (!checkIfManagedIsAllowed(n,*target)) || ((via->ss_family == target->ss_family)&&(matchIpOnly(myIps,*via))) )
 					continue;
 
 				bool haveRoute = false;

From 1a285e534298c498e5e458c30e812b3366134152 Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 15:03:06 -0700
Subject: [PATCH 19/20] Version bump again. Slack has updated their client
 twice in one day so its okay.

---
 debian/changelog                          | 6 ++++++
 ext/installfiles/mac/ZeroTier One.pkgproj | 2 +-
 version.h                                 | 2 +-
 zerotier-one.spec                         | 2 +-
 4 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 46b2d04ee..3b33c1e29 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+zerotier-one (1.1.12) unstable; urgency=medium
+
+  * See https://github.com/zerotier/ZeroTierOne for release notes.
+
+ -- Adam Ierymenko <adam.ierymenko@zerotier.com>  Tue, 12 Jul 2016 03:02:22 -0700
+
 zerotier-one (1.1.10) unstable; urgency=medium
 
   * See https://github.com/zerotier/ZeroTierOne for release notes.
diff --git a/ext/installfiles/mac/ZeroTier One.pkgproj b/ext/installfiles/mac/ZeroTier One.pkgproj
index 34198ca0b..acbc3cfbe 100755
--- a/ext/installfiles/mac/ZeroTier One.pkgproj	
+++ b/ext/installfiles/mac/ZeroTier One.pkgproj	
@@ -759,7 +759,7 @@
 			<key>OVERWRITE_PERMISSIONS</key>
 			<false/>
 			<key>VERSION</key>
-			<string>1.1.10</string>
+			<string>1.1.12</string>
 		</dict>
 		<key>PROJECT_COMMENTS</key>
 		<dict>
diff --git a/version.h b/version.h
index 2facbffcb..9ef01d6d5 100644
--- a/version.h
+++ b/version.h
@@ -32,6 +32,6 @@
 /**
  * Revision
  */
-#define ZEROTIER_ONE_VERSION_REVISION 10
+#define ZEROTIER_ONE_VERSION_REVISION 12
 
 #endif
diff --git a/zerotier-one.spec b/zerotier-one.spec
index aba188782..36856bd4a 100644
--- a/zerotier-one.spec
+++ b/zerotier-one.spec
@@ -1,5 +1,5 @@
 Name:           zerotier-one
-Version:        1.1.10
+Version:        1.1.12
 Release:        0.1%{?dist}
 Summary:        ZeroTier One network virtualization service
 

From bdc3b0834bb8abc621ee6dbc9a92769b57376bee Mon Sep 17 00:00:00 2001
From: Adam Ierymenko <adam.ierymenko@gmail.com>
Date: Tue, 12 Jul 2016 15:09:44 -0700
Subject: [PATCH 20/20] VERSION 1.1.12: yet another minor fix to managed routes

This version just contains another minor fix to managed routes for
GitHub issue #352.