Browse Source

Merge branch 'adamierymenko-dev' into android-jni

Grant Limberg 10 years ago
parent
commit
1ad2cfeedf

+ 39 - 0
include/ZeroTierOne.h

@@ -628,6 +628,15 @@ typedef struct
 	unsigned long peerCount;
 } ZT1_PeerList;
 
+/**
+ * Local interface trust levels
+ */
+typedef enum {
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL = 0,
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_PRIVACY = 1,
+	ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_ULTIMATE = 2
+} ZT1_LocalInterfaceAddressTrust;
+
 /**
  * An instance of a ZeroTier One node (opaque)
  */
@@ -958,6 +967,36 @@ ZT1_VirtualNetworkList *ZT1_Node_networks(ZT1_Node *node);
  */
 void ZT1_Node_freeQueryResult(ZT1_Node *node,void *qr);
 
+/**
+ * Add a local interface address
+ *
+ * Local interface addresses may be added if you want remote peers
+ * with whom you have a trust relatinship (e.g. common network membership)
+ * to receive information about these endpoints as potential endpoints for
+ * direct communication.
+ *
+ * Take care that these are never ZeroTier interface addresses, otherwise
+ * strange things might happen or they simply won't work.
+ *
+ * This returns a boolean indicating whether or not the address was
+ * accepted. ZeroTier will only communicate over certain address types
+ * and (for IP) address classes. Thus it's safe to just dump your OS's
+ * entire remote IP list (excluding ZeroTier interface IPs) into here
+ * and let ZeroTier determine which addresses it will use.
+ *
+ * @param addr Local interface address
+ * @param metric Local interface metric
+ * @param trust How much do you trust the local network under this interface?
+ * @param reliable If nonzero, this interface doesn't link to anything behind a NAT or stateful firewall
+ * @return Boolean: non-zero if address was accepted and added
+ */
+int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable);
+
+/**
+ * Clear local interface addresses
+ */
+void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node);
+
 /**
  * Set a network configuration master instance for this node
  *

+ 2 - 2
make-freebsd.mk

@@ -1,5 +1,5 @@
-CC?=cc
-CXX?=c++
+CC=cc
+CXX=c++
 
 INCLUDES=
 DEFS=

+ 4 - 4
make-linux.mk

@@ -22,7 +22,7 @@ CC?=$(shell if [ -e /usr/bin/clang ]; then echo clang; else echo gcc; fi)
 CXX?=$(shell if [ -e /usr/bin/clang++ ]; then echo clang++; else echo g++; fi)
 INCLUDES=
 DEFS=
-LIBS=
+LDLIBS?=
 
 include objects.mk
 OBJS+=osdep/LinuxEthernetTap.o 
@@ -35,7 +35,7 @@ endif
 # Build with ZT_ENABLE_NETWORK_CONTROLLER=1 to build with the Sqlite network controller
 ifeq ($(ZT_ENABLE_NETWORK_CONTROLLER),1)
         DEFS+=-DZT_ENABLE_NETWORK_CONTROLLER 
-        LIBS+=-L/usr/local/lib -lsqlite3
+        LDLIBS+=-L/usr/local/lib -lsqlite3
         OBJS+=controller/SqliteNetworkController.o 
 endif
 
@@ -67,13 +67,13 @@ endif
 all:	one
 
 one:	$(OBJS) one.o
-	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LIBS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-one $(OBJS) one.o $(LDLIBS)
 	$(STRIP) zerotier-one
 	ln -sf zerotier-one zerotier-idtool
 	ln -sf zerotier-one zerotier-cli
 
 selftest:	$(OBJS) selftest.o
-	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LIBS)
+	$(CXX) $(CXXFLAGS) $(LDFLAGS) -o zerotier-selftest selftest.o $(OBJS) $(LDLIBS)
 	$(STRIP) zerotier-selftest
 
 installer: one FORCE

+ 0 - 91
node/CMWC4096.hpp

@@ -1,91 +0,0 @@
-/*
- * ZeroTier One - Network Virtualization Everywhere
- * Copyright (C) 2011-2015  ZeroTier, Inc.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- *
- * --
- *
- * ZeroTier may be used and distributed under the terms of the GPLv3, which
- * are available at: http://www.gnu.org/licenses/gpl-3.0.html
- *
- * If you would like to embed ZeroTier into a commercial application or
- * redistribute it in a modified binary form, please contact ZeroTier Networks
- * LLC. Start here: http://www.zerotier.com/
- */
-
-#ifndef ZT_CMWC4096_HPP
-#define ZT_CMWC4096_HPP
-
-#include <stdint.h>
-#include "Utils.hpp"
-
-namespace ZeroTier {
-
-/** 
- * Complement Multiply With Carry random number generator
- *
- * Based on original code posted to Usenet in the public domain by
- * George Marsaglia. Period is approximately 2^131086.
- *
- * This is not used for cryptographic purposes but for a very fast
- * and high-quality PRNG elsewhere in the code.
- */
-class CMWC4096
-{
-public:
-	/**
-	 * Construct and initialize from secure random source
-	 */
-	CMWC4096()
-		throw()
-	{
-		Utils::getSecureRandom(Q,sizeof(Q));
-		Utils::getSecureRandom(&c,sizeof(c));
-		c %= 809430660;
-		i = 4095;
-	}
-
-	inline uint32_t next32()
-		throw()
-	{
-		uint32_t __i = ++i & 4095;
-		const uint64_t t = (18782ULL * (uint64_t)Q[__i]) + (uint64_t)c;
-		c = (uint32_t)(t >> 32);
-		uint32_t x = c + (uint32_t)t;
-		const uint32_t p = (uint32_t)(x < c); x += p; c += p;
-		return (Q[__i] = 0xfffffffe - x);
-	}
-
-	inline uint64_t next64()
-		throw()
-	{
-		return ((((uint64_t)next32()) << 32) ^ (uint64_t)next32());
-	}
-
-	inline double nextDouble()
-		throw()
-	{
-		return ((double)(next32()) / 4294967296.0);
-	}
-
-private:
-	uint32_t Q[4096];
-	uint32_t c;
-	uint32_t i;
-};
-
-} // namespace ZeroTier
-
-#endif

+ 4 - 9
node/Constants.hpp

@@ -305,19 +305,14 @@
 #define ZT_ANTIRECURSION_HISTORY_SIZE 16
 
 /**
- * How often to send LAN beacons
- */
-#define ZT_BEACON_INTERVAL 30000
-
-/**
- * Do not respond to any beacon more often than this
+ * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding)
  */
-#define ZT_MIN_BEACON_RESPONSE_INTERVAL 2500
+#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000
 
 /**
- * Minimum delay between attempts to confirm new paths to peers (to avoid HELLO flooding)
+ * Interval between direct path pushes in milliseconds
  */
-#define ZT_MIN_PATH_CONFIRMATION_INTERVAL 5000
+#define ZT_DIRECT_PATH_PUSH_INTERVAL 300000
 
 /**
  * Sanity limit on maximum bridge routes

+ 74 - 20
node/IncomingPacket.cpp

@@ -84,6 +84,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
 				case Packet::VERB_NETWORK_CONFIG_REFRESH:         return _doNETWORK_CONFIG_REFRESH(RR,peer);
 				case Packet::VERB_MULTICAST_GATHER:               return _doMULTICAST_GATHER(RR,peer);
 				case Packet::VERB_MULTICAST_FRAME:                return _doMULTICAST_FRAME(RR,peer);
+				case Packet::VERB_PUSH_DIRECT_PATHS:              return _doPUSH_DIRECT_PATHS(RR,peer);
 			}
 		} else {
 			RR->sw->requestWhois(source());
@@ -133,6 +134,9 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				break;
 
 			case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
+				/* Note: certificates are public so it's safe to push them to anyone
+				 * who asks. We won't communicate unless we also get a certificate
+				 * from the remote that agrees. */
 				SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				if (network) {
 					SharedPtr<NetworkConfig> nconf(network->config2());
@@ -152,7 +156,10 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 			}	break;
 
 			case Packet::ERROR_UNWANTED_MULTICAST: {
-				// TODO: unsubscribe
+				uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD);
+				MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
+				TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",nwid,peer->address().toString().c_str(),mg.toString().c_str());
+				RR->mc->remove(nwid,mg,peer->address());
 			}	break;
 
 			default: break;
@@ -169,8 +176,20 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 
 bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
 {
+	/* Note: this is the only packet ever sent in the clear, and it's also
+	 * the only packet that we authenticate via a different path. Authentication
+	 * occurs here and is based on the validity of the identity and the
+	 * integrity of the packet's MAC, but it must be done after we check
+	 * the identity since HELLO is a mechanism for learning new identities
+	 * in the first place. */
+
 	try {
 		const unsigned int protoVersion = (*this)[ZT_PROTO_VERB_HELLO_IDX_PROTOCOL_VERSION];
+		if (protoVersion < ZT_PROTO_VERSION_MIN) {
+			TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str());
+			return true;
+		}
+
 		const unsigned int vMajor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MAJOR_VERSION];
 		const unsigned int vMinor = (*this)[ZT_PROTO_VERB_HELLO_IDX_MINOR_VERSION];
 		const unsigned int vRevision = at<uint16_t>(ZT_PROTO_VERB_HELLO_IDX_REVISION);
@@ -178,6 +197,10 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
 
 		Identity id;
 		unsigned int destAddrPtr = id.deserialize(*this,ZT_PROTO_VERB_HELLO_IDX_IDENTITY) + ZT_PROTO_VERB_HELLO_IDX_IDENTITY;
+		if (source() != id.address()) {
+			TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str());
+			return true;
+		}
 
 		InetAddress destAddr;
 		if (destAddrPtr < size()) { // ZeroTier One < 1.0.3 did not include this field
@@ -192,16 +215,6 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
 			}
 		}
 
-		if (source() != id.address()) {
-			TRACE("dropped HELLO from %s(%s): identity not for sending address",source().toString().c_str(),_remoteAddress.toString().c_str());
-			return true;
-		}
-
-		if (protoVersion < ZT_PROTO_VERSION_MIN) {
-			TRACE("dropped HELLO from %s(%s): protocol version too old",id.address().toString().c_str(),_remoteAddress.toString().c_str());
-			return true;
-		}
-
 		SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
 		if (peer) {
 			// We already have an identity with this address -- check for collisions
@@ -244,12 +257,14 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR)
 		} else {
 			// We don't already have an identity with this address -- validate and learn it
 
+			// Check identity proof of work
 			if (!id.locallyValidate()) {
 				RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress);
 				TRACE("dropped HELLO from %s(%s): identity invalid",id.address().toString().c_str(),_remoteAddress.toString().c_str());
 				return true;
 			}
 
+			// Check packet integrity and authentication
 			SharedPtr<Peer> newPeer(new Peer(RR->identity,id));
 			if (!dearmor(newPeer->key())) {
 				RR->node->postEvent(ZT1_EVENT_AUTHENTICATION_FAILURE,(const void *)&_remoteAddress);
@@ -428,7 +443,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 					offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
 					SharedPtr<Network> network(RR->node->network(nwid));
 					if ((network)&&(com.hasRequiredFields()))
-						network->addMembershipCertificate(com,false);
+						network->validateAndAddMembershipCertificate(com);
 				}
 
 				if ((flags & 0x02) != 0) {
@@ -553,14 +568,17 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 				const unsigned int flags = (*this)[ZT_PROTO_VERB_EXT_FRAME_IDX_FLAGS];
 
 				unsigned int comLen = 0;
+				bool comFailed = false;
 				if ((flags & 0x01) != 0) {
 					CertificateOfMembership com;
 					comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
-					if (com.hasRequiredFields())
-						network->addMembershipCertificate(com,false);
+					if (com.hasRequiredFields()) {
+						if (!network->validateAndAddMembershipCertificate(com))
+							comFailed = true; // technically this check is redundant to isAllowed(), but do it anyway for thoroughness
+					}
 				}
 
-				if (!network->isAllowed(peer->address())) {
+				if ((comFailed)||(!network->isAllowed(peer->address()))) {
 					TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_remoteAddress.toString().c_str(),network->id());
 					_sendErrorNeedCertificate(RR,peer,network->id());
 					return true;
@@ -595,9 +613,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 						TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
 						return true;
 					}
-				}
-
-				if (to != network->mac()) {
+				} else if (to != network->mac()) {
 					if (!network->permitsBridging(RR->identity.address())) {
 						TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_remoteAddress.toString().c_str(),to.toString().c_str(),network->id());
 						return true;
@@ -649,7 +665,7 @@ bool IncomingPacket::_doNETWORK_MEMBERSHIP_CERTIFICATE(const RuntimeEnvironment
 			if (com.hasRequiredFields()) {
 				SharedPtr<Network> network(RR->node->network(com.networkId()));
 				if (network)
-					network->addMembershipCertificate(com,false);
+					network->validateAndAddMembershipCertificate(com);
 			}
 		}
 
@@ -807,7 +823,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 				CertificateOfMembership com;
 				offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
 				if (com.hasRequiredFields())
-					network->addMembershipCertificate(com,false);
+					network->validateAndAddMembershipCertificate(com);
 			}
 
 			// Check membership after we've read any included COM, since
@@ -884,6 +900,44 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 	return true;
 }
 
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+{
+	try {
+		unsigned int count = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD);
+		unsigned int ptr = ZT_PACKET_IDX_PAYLOAD + 2;
+
+		while (count) { // if ptr overflows Buffer will throw
+			// TODO: properly handle blacklisting, support other features... see Packet.hpp.
+
+			unsigned int flags = (*this)[ptr++];
+			/*int metric = (*this)[ptr++];*/ ++ptr;
+			unsigned int extLen = at<uint16_t>(ptr); ptr += 2;
+			ptr += extLen; // unused right now
+			unsigned int addrType = (*this)[ptr++];
+
+			unsigned int addrLen = (*this)[ptr++];
+			switch(addrType) {
+				case 4: {
+					InetAddress a(field(ptr,4),4,at<uint16_t>(ptr + 4));
+					if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) )
+						peer->attemptToContactAt(RR,a,RR->node->now());
+				}	break;
+				case 6: {
+					InetAddress a(field(ptr,16),16,at<uint16_t>(ptr + 16));
+					if ( ((flags & (0x01 | 0x02)) == 0) && (Path::isAddressValidForPath(a)) )
+						peer->attemptToContactAt(RR,a,RR->node->now());
+				}	break;
+			}
+			ptr += addrLen;
+		}
+	} catch (std::exception &exc) {
+		TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: %s",source().toString().c_str(),_remoteAddress.toString().c_str(),exc.what());
+	} catch ( ... ) {
+		TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception: (unknown)",source().toString().c_str(),_remoteAddress.toString().c_str());
+	}
+	return true;
+}
+
 void IncomingPacket::_sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid)
 {
 	Packet outp(source(),RR->identity.address(),Packet::VERB_ERROR);

+ 1 - 0
node/IncomingPacket.hpp

@@ -121,6 +121,7 @@ private:
 	bool _doNETWORK_CONFIG_REFRESH(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 	bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
+	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
 
 	// Send an ERROR_NEED_MEMBERSHIP_CERTIFICATE to a peer indicating that an updated cert is needed to join
 	void _sendErrorNeedCertificate(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,uint64_t nwid);

+ 7 - 1
node/InetAddress.cpp

@@ -91,7 +91,13 @@ InetAddress::IpScope InetAddress::ipScope() const
 			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
 			if ((ip[0] & 0xf0) == 0xf0) {
 				if (ip[0] == 0xff) return IP_SCOPE_MULTICAST;                              // ff00::/8
-				if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) return IP_SCOPE_LINK_LOCAL; // fe80::/10
+				if ((ip[0] == 0xfe)&&((ip[1] & 0xc0) == 0x80)) {
+					unsigned int k = 2;
+					while ((!ip[k])&&(k < 15)) ++k;
+					if ((k == 15)&&(ip[15] == 0x01))
+						return IP_SCOPE_LOOPBACK;                                              // fe80::1/128
+					else return IP_SCOPE_LINK_LOCAL;                                         // fe80::/10
+				}
 				if ((ip[0] & 0xfe) == 0xfc) return IP_SCOPE_PRIVATE;                       // fc00::/7
 			}
 			unsigned int k = 0;

+ 16 - 3
node/Multicaster.cpp

@@ -35,7 +35,6 @@
 #include "Switch.hpp"
 #include "Packet.hpp"
 #include "Peer.hpp"
-#include "CMWC4096.hpp"
 #include "C25519.hpp"
 #include "CertificateOfMembership.hpp"
 
@@ -62,6 +61,20 @@ void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &m
 	}
 }
 
+void Multicaster::remove(uint64_t nwid,const MulticastGroup &mg,const Address &member)
+{
+	Mutex::Lock _l(_groups_m);
+	std::map< std::pair<uint64_t,MulticastGroup>,MulticastGroupStatus >::iterator g(_groups.find(std::pair<uint64_t,MulticastGroup>(nwid,mg)));
+	if (g != _groups.end()) {
+		for(std::vector<MulticastGroupMember>::iterator m(g->second.members.begin());m!=g->second.members.end();++m) {
+			if (m->address == member) {
+				g->second.members.erase(m);
+				break;
+			}
+		}
+	}
+}
+
 unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const MulticastGroup &mg,Packet &appendTo,unsigned int limit) const
 {
 	unsigned char *p;
@@ -97,7 +110,7 @@ unsigned int Multicaster::gather(const Address &queryingPeer,uint64_t nwid,const
 		// will return different subsets of a large multicast group.
 		k = 0;
 		while ((added < limit)&&(k < gs->second.members.size())&&((appendTo.size() + ZT_ADDRESS_LENGTH) <= ZT_UDP_DEFAULT_PAYLOAD_MTU)) {
-			rptr = (unsigned int)RR->prng->next32();
+			rptr = (unsigned int)RR->node->prng();
 
 restart_member_scan:
 			a = gs->second.members[rptr % (unsigned int)gs->second.members.size()].address.toInt();
@@ -171,7 +184,7 @@ void Multicaster::send(
 		for(unsigned long i=0;i<gs.members.size();++i)
 			indexes[i] = i;
 		for(unsigned long i=(unsigned long)gs.members.size()-1;i>0;--i) {
-			unsigned long j = RR->prng->next32() % (i + 1);
+			unsigned long j = (unsigned long)RR->node->prng() % (i + 1);
 			unsigned long tmp = indexes[j];
 			indexes[j] = indexes[i];
 			indexes[i] = tmp;

+ 9 - 0
node/Multicaster.hpp

@@ -106,6 +106,15 @@ public:
 	 */
 	void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
 
+	/**
+	 * Remove a multicast group member (if present)
+	 *
+	 * @param nwid Network ID
+	 * @param mg Multicast group
+	 * @param member Member to unsubscribe
+	 */
+	void remove(uint64_t nwid,const MulticastGroup &mg,const Address &member);
+
 	/**
 	 * Append gather results to a packet by choosing registered multicast recipients at random
 	 *

+ 29 - 27
node/Network.cpp

@@ -264,56 +264,58 @@ void Network::requestConfiguration()
 			outp.append((uint64_t)_config->revision());
 		else outp.append((uint64_t)0);
 	}
-	RR->sw->send(outp,true,_id);
+	RR->sw->send(outp,true,0);
 }
 
-void Network::addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept)
+bool Network::validateAndAddMembershipCertificate(const CertificateOfMembership &cert)
 {
 	if (!cert) // sanity check
-		return;
+		return false;
 
 	Mutex::Lock _l(_lock);
 	CertificateOfMembership &old = _membershipCertificates[cert.issuedTo()];
 
 	// Nothing to do if the cert hasn't changed -- we get duplicates due to zealous cert pushing
 	if (old == cert)
-		return;
+		return true; // but if it's a duplicate of one we already accepted, return is 'true'
 
 	// Check signature, log and return if cert is invalid
-	if (!forceAccept) {
-		if (cert.signedBy() != controller()) {
-			TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str());
-			return;
+	if (cert.signedBy() != controller()) {
+		TRACE("rejected network membership certificate for %.16llx signed by %s: signer not a controller of this network",(unsigned long long)_id,cert.signedBy().toString().c_str());
+		return false; // invalid signer
+	}
+
+	if (cert.signedBy() == RR->identity.address()) {
+
+		// We are the controller: RR->identity.address() == controller() == cert.signedBy()
+		// So, verify that we signed th cert ourself
+		if (!cert.verify(RR->identity)) {
+			TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
+			return false; // invalid signature
 		}
 
-		if (cert.signedBy() == RR->identity.address()) {
-			// We are the controller: RR->identity.address() == controller() == cert.signedBy()
-			// So, verify that we signed th cert ourself
-			if (!cert.verify(RR->identity)) {
-				TRACE("rejected network membership certificate for %.16llx self signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
-				return;
-			}
-		} else {
+	} else {
 
-			SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy()));
+		SharedPtr<Peer> signer(RR->topology->getPeer(cert.signedBy()));
 
-			if (!signer) {
-				// This would be rather odd, since this is our controller... could happen
-				// if we get packets before we've gotten config.
-				RR->sw->requestWhois(cert.signedBy());
-				return;
-			}
+		if (!signer) {
+			// This would be rather odd, since this is our controller... could happen
+			// if we get packets before we've gotten config.
+			RR->sw->requestWhois(cert.signedBy());
+			return false; // signer unknown
+		}
 
-			if (!cert.verify(signer->identity())) {
-				TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
-				return;
-			}
+		if (!cert.verify(signer->identity())) {
+			TRACE("rejected network membership certificate for %.16llx signed by %s: signature check failed",(unsigned long long)_id,cert.signedBy().toString().c_str());
+			return false; // invalid signature
 		}
 	}
 
 	// If we made it past authentication, update cert
 	if (cert.revision() != old.revision())
 		old = cert;
+
+	return true;
 }
 
 bool Network::peerNeedsOurMembershipCertificate(const Address &to,uint64_t now)

+ 3 - 3
node/Network.hpp

@@ -179,12 +179,12 @@ public:
 	 * Add or update a membership certificate
 	 *
 	 * @param cert Certificate of membership
-	 * @param forceAccept If true, accept without validating signature
+	 * @return True if certificate was accepted as valid
 	 */
-	void addMembershipCertificate(const CertificateOfMembership &cert,bool forceAccept);
+	bool validateAndAddMembershipCertificate(const CertificateOfMembership &cert);
 
 	/**
-	 * Check if we should push membership certificate to a peer, and update last pushed
+	 * Check if we should push membership certificate to a peer, AND update last pushed
 	 *
 	 * If we haven't pushed a cert to this peer in a long enough time, this returns
 	 * true and updates the last pushed time. Otherwise it returns false.

+ 56 - 22
node/Node.cpp

@@ -37,7 +37,6 @@
 #include "Node.hpp"
 #include "RuntimeEnvironment.hpp"
 #include "NetworkController.hpp"
-#include "CMWC4096.hpp"
 #include "Switch.hpp"
 #include "Multicaster.hpp"
 #include "AntiRecursion.hpp"
@@ -76,16 +75,25 @@ Node::Node(
 	_eventCallback(eventCallback),
 	_networks(),
 	_networks_m(),
+	_prngStreamPtr(0),
 	_now(now),
 	_lastPingCheck(0),
-	_lastHousekeepingRun(0),
-	_lastBeacon(0)
+	_lastHousekeepingRun(0)
 {
 	_newestVersionSeen[0] = ZEROTIER_ONE_VERSION_MAJOR;
 	_newestVersionSeen[1] = ZEROTIER_ONE_VERSION_MINOR;
 	_newestVersionSeen[2] = ZEROTIER_ONE_VERSION_REVISION;
 	_online = false;
 
+	// Use Salsa20 alone as a high-quality non-crypto PRNG
+	{
+		char foo[32];
+		Utils::getSecureRandom(foo,32);
+		_prng.init(foo,256,foo,8);
+		memset(_prngStream,0,sizeof(_prngStream));
+		_prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream));
+	}
+
 	std::string idtmp(dataStoreGet("identity.secret"));
 	if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
 		TRACE("identity.secret not found, generating...");
@@ -104,7 +112,6 @@ Node::Node(
 	}
 
 	try {
-		RR->prng = new CMWC4096();
 		RR->sw = new Switch(RR);
 		RR->mc = new Multicaster(RR);
 		RR->antiRec = new AntiRecursion();
@@ -116,7 +123,6 @@ Node::Node(
 		delete RR->antiRec;
 		delete RR->mc;
 		delete RR->sw;
-		delete RR->prng;
 		throw;
 	}
 
@@ -147,7 +153,6 @@ Node::~Node()
 	delete RR->antiRec;
 	delete RR->mc;
 	delete RR->sw;
-	delete RR->prng;
 }
 
 ZT1_ResultCode Node::processWirePacket(
@@ -269,19 +274,6 @@ ZT1_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *next
 			_online = ((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT);
 			if (oldOnline != _online)
 				postEvent(_online ? ZT1_EVENT_ONLINE : ZT1_EVENT_OFFLINE);
-
-			// Send LAN beacons
-			if ((now - _lastBeacon) >= ZT_BEACON_INTERVAL) {
-				_lastBeacon = now;
-				char beacon[13];
-				void *p = beacon;
-				*(reinterpret_cast<uint32_t *>(p)) = RR->prng->next32();
-				p = beacon + 4;
-				*(reinterpret_cast<uint32_t *>(p)) = RR->prng->next32();
-				RR->identity.address().copyTo(beacon + 8,5);
-				RR->antiRec->logOutgoingZT(beacon,13);
-				putPacket(ZT_DEFAULTS.v4Broadcast,beacon,13);
-			}
 		} catch ( ... ) {
 			return ZT1_RESULT_FATAL_ERROR_INTERNAL;
 		}
@@ -388,10 +380,10 @@ ZT1_PeerList *Node::peers() const
 		p->latency = pi->second->latency();
 		p->role = RR->topology->isRoot(pi->second->identity()) ? ZT1_PEER_ROLE_ROOT : ZT1_PEER_ROLE_LEAF;
 
-		std::vector<Path> paths(pi->second->paths());
-		Path *bestPath = pi->second->getBestPath(_now);
+		std::vector<RemotePath> paths(pi->second->paths());
+		RemotePath *bestPath = pi->second->getBestPath(_now);
 		p->pathCount = 0;
-		for(std::vector<Path>::iterator path(paths.begin());path!=paths.end();++path) {
+		for(std::vector<RemotePath>::iterator path(paths.begin());path!=paths.end();++path) {
 			memcpy(&(p->paths[p->pathCount].address),&(path->address()),sizeof(struct sockaddr_storage));
 			p->paths[p->pathCount].lastSend = path->lastSend();
 			p->paths[p->pathCount].lastReceive = path->lastReceived();
@@ -440,6 +432,24 @@ void Node::freeQueryResult(void *qr)
 		::free(qr);
 }
 
+int Node::addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+	if (Path::isAddressValidForPath(*(reinterpret_cast<const InetAddress *>(addr)))) {
+		Mutex::Lock _l(_directPaths_m);
+		_directPaths.push_back(Path(*(reinterpret_cast<const InetAddress *>(addr)),metric,(Path::Trust)trust,reliable != 0));
+		std::sort(_directPaths.begin(),_directPaths.end());
+		_directPaths.erase(std::unique(_directPaths.begin(),_directPaths.end()),_directPaths.end());
+		return 1;
+	}
+	return 0;
+}
+
+void Node::clearLocalInterfaceAddresses()
+{
+	Mutex::Lock _l(_directPaths_m);
+	_directPaths.clear();
+}
+
 void Node::setNetconfMaster(void *networkControllerInstance)
 {
 	RR->localNetworkController = reinterpret_cast<NetworkController *>(networkControllerInstance);
@@ -506,6 +516,14 @@ void Node::postTrace(const char *module,unsigned int line,const char *fmt,...)
 }
 #endif // ZT_TRACE
 
+uint64_t Node::prng()
+{
+	unsigned int p = (++_prngStreamPtr % (sizeof(_prngStream) / sizeof(uint64_t)));
+	if (!p)
+		_prng.encrypt(_prngStream,_prngStream,sizeof(_prngStream));
+	return _prngStream[p];
+}
+
 } // namespace ZeroTier
 
 /****************************************************************************/
@@ -693,6 +711,22 @@ void ZT1_Node_setNetconfMaster(ZT1_Node *node,void *networkControllerInstance)
 	} catch ( ... ) {}
 }
 
+int ZT1_Node_addLocalInterfaceAddress(ZT1_Node *node,const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable)
+{
+	try {
+		return reinterpret_cast<ZeroTier::Node *>(node)->addLocalInterfaceAddress(addr,metric,trust,reliable);
+	} catch ( ... ) {
+		return 0;
+	}
+}
+
+void ZT1_Node_clearLocalInterfaceAddresses(ZT1_Node *node)
+{
+	try {
+		reinterpret_cast<ZeroTier::Node *>(node)->clearLocalInterfaceAddresses();
+	} catch ( ... ) {}
+}
+
 void ZT1_version(int *major,int *minor,int *revision,unsigned long *featureFlags)
 {
 	if (major) *major = ZEROTIER_ONE_VERSION_MAJOR;

+ 25 - 1
node/Node.hpp

@@ -43,6 +43,8 @@
 #include "Mutex.hpp"
 #include "MAC.hpp"
 #include "Network.hpp"
+#include "Path.hpp"
+#include "Salsa20.hpp"
 
 #undef TRACE
 #ifdef ZT_TRACE
@@ -103,6 +105,8 @@ public:
 	ZT1_VirtualNetworkConfig *networkConfig(uint64_t nwid) const;
 	ZT1_VirtualNetworkList *networks() const;
 	void freeQueryResult(void *qr);
+	int addLocalInterfaceAddress(const struct sockaddr_storage *addr,int metric,ZT1_LocalInterfaceAddressTrust trust,int reliable);
+	void clearLocalInterfaceAddresses();
 	void setNetconfMaster(void *networkControllerInstance);
 
 	// Internal functions ------------------------------------------------------
@@ -171,6 +175,15 @@ public:
 		return nw;
 	}
 
+	/**
+	 * @return Potential direct paths to me a.k.a. local interface addresses
+	 */
+	inline std::vector<Path> directPaths() const
+	{
+		Mutex::Lock _l(_directPaths_m);
+		return _directPaths;
+	}
+
 	inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
 	inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
 	inline void dataStoreDelete(const char *name) { _dataStorePutFunction(reinterpret_cast<ZT1_Node *>(this),_uPtr,name,(const void *)0,0,0); }
@@ -207,6 +220,11 @@ public:
 	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();
+
 private:
 	inline SharedPtr<Network> _network(uint64_t nwid) const
 	{
@@ -236,12 +254,18 @@ private:
 	std::vector< std::pair< uint64_t, SharedPtr<Network> > > _networks;
 	Mutex _networks_m;
 
+	std::vector<Path> _directPaths;
+	Mutex _directPaths_m;
+
 	Mutex _backgroundTasksLock;
 
+	unsigned int _prngStreamPtr;
+	Salsa20 _prng;
+	uint64_t _prngStream[16]; // repeatedly encrypted with _prng to yield a high-quality non-crypto PRNG stream
+
 	uint64_t _now;
 	uint64_t _lastPingCheck;
 	uint64_t _lastHousekeepingRun;
-	uint64_t _lastBeacon;
 	unsigned int _newestVersionSeen[3]; // major, minor, revision
 	bool _online;
 };

+ 2 - 1
node/OutboundMulticast.cpp

@@ -104,7 +104,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA
 {
 	if (_haveCom) {
 		SharedPtr<Network> network(RR->node->network(_nwid));
-		if (network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now())) {
+		if ((network)&&(network->peerNeedsOurMembershipCertificate(toAddr,RR->node->now()))) {
 			_packetWithCom.newInitializationVector();
 			_packetWithCom.setDestination(toAddr);
 			//TRACE(">>MC %.16llx -> %s (with COM)",(unsigned long long)this,toAddr.toString().c_str());
@@ -112,6 +112,7 @@ void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toA
 			return;
 		}
 	}
+
 	//TRACE(">>MC %.16llx -> %s (without COM)",(unsigned long long)this,toAddr.toString().c_str());
 	_packetNoCom.newInitializationVector();
 	_packetNoCom.setDestination(toAddr);

+ 1 - 1
node/Packet.cpp

@@ -51,7 +51,7 @@ const char *Packet::verbString(Verb v)
 		case VERB_MULTICAST_GATHER: return "MULTICAST_GATHER";
 		case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
 		case VERB_SET_EPHEMERAL_KEY: return "SET_EPHEMERAL_KEY";
-		case VERB_PHYSICAL_ADDRESS_PUSH: return "PHYSICAL_ADDRESS_PUSH";
+		case VERB_PUSH_DIRECT_PATHS: return "PUSH_DIRECT_PATHS";
 	}
 	return "(unknown)";
 }

+ 120 - 49
node/Packet.hpp

@@ -71,13 +71,14 @@
 /**
  * Maximum hop count allowed by packet structure (3 bits, 0-7)
  * 
- * This is not necessarily the maximum hop counter after which
- * relaying is no longer performed.
+ * This is a protocol constant. It's the maximum allowed by the length
+ * of the hop counter -- three bits. See node/Constants.hpp for the
+ * pragmatic forwarding limit, which is typically lower.
  */
 #define ZT_PROTO_MAX_HOPS 7
 
 /**
- * Cipher suite: Curve25519/Poly1305/Salsa20/12 without payload encryption
+ * Cipher suite: Curve25519/Poly1305/Salsa20/12/NOCRYPT
  *
  * This specifies Poly1305 MAC using a 32-bit key derived from the first
  * 32 bytes of a Salsa20/12 keystream as in the Salsa20/12 cipher suite,
@@ -103,9 +104,7 @@
  *
  * This message is encrypted with the latest negotiated ephemeral (PFS)
  * key pair and cipher suite. If authentication fails, VERB_SET_EPHEMERAL_KEY
- * may be sent to renegotiate ephemeral keys. To prevent attacks, this
- * attempted renegotiation should be limited to some sane rate such as
- * once per second.
+ * may be sent to renegotiate ephemeral keys.
  */
 #define ZT_PROTO_CIPHER_SUITE__EPHEMERAL 7
 
@@ -113,7 +112,7 @@
  * DEPRECATED payload encrypted flag, will be removed for re-use soon.
  *
  * This has been replaced by the two-bit cipher suite selection field where
- * a value of 0 indicated unencrypted (but authenticated) messages.
+ * a value of 0 indicates unencrypted (but authenticated) messages.
  */
 #define ZT_PROTO_FLAG_ENCRYPTED 0x80
 
@@ -132,11 +131,68 @@
 
 /**
  * Rounds used for Salsa20 encryption in ZT
+ *
+ * Discussion:
+ *
+ * DJB (Salsa20's designer) designed Salsa20 with a significant margin of 20
+ * rounds, but has said repeatedly that 12 is likely sufficient. So far (as of
+ * July 2015) there are no published attacks against 12 rounds, let alone 20.
+ *
+ * In cryptography, a "break" means something different from what it means in
+ * common discussion. If a cipher is 256 bits strong and someone finds a way
+ * to reduce key search to 254 bits, this constitues a "break" in the academic
+ * literature. 254 bits is still far beyond what can be leveraged to accomplish
+ * a "break" as most people would understand it -- the actual decryption and
+ * reading of traffic.
+ *
+ * Nevertheless, "attacks only get better" as cryptographers like to say. As
+ * a result, they recommend not using anything that's shown any weakness even
+ * if that weakness is so far only meaningful to academics. It may be a sign
+ * of a deeper problem.
+ *
+ * So why choose a lower round count?
+ *
+ * Turns out the speed difference is nontrivial. On a Macbook Pro (Core i3) 20
+ * rounds of SSE-optimized Salsa20 achieves ~508mb/sec/core, while 12 rounds
+ * hits ~832mb/sec/core. ZeroTier is designed for multiple objectives:
+ * security, simplicity, and performance. In this case a deference was made
+ * for performance.
+ *
+ * Meta discussion:
+ *
+ * The cipher is not the thing you should be paranoid about.
+ *
+ * I'll qualify that. If the cipher is known to be weak, like RC4, or has a
+ * key size that is too small, like DES, then yes you should worry about
+ * the cipher.
+ *
+ * But if the cipher is strong and your adversary is anyone other than the
+ * intelligence apparatus of a major superpower, you are fine in that
+ * department.
+ *
+ * Go ahead. Search for the last ten vulnerabilities discovered in SSL. Not
+ * a single one involved the breaking of a cipher. Now broaden your search.
+ * Look for issues with SSH, IPSec, etc. The only cipher-related issues you
+ * will find might involve the use of RC4 or MD5, algorithms with known
+ * issues or small key/digest sizes. But even weak ciphers are difficult to
+ * exploit in the real world -- you usually need a lot of data and a lot of
+ * compute time. No, virtually EVERY security vulnerability you will find
+ * involves a problem with the IMPLEMENTATION not with the cipher.
+ *
+ * A flaw in ZeroTier's protocol or code is incredibly, unbelievably
+ * more likely than a flaw in Salsa20 or any other cipher or cryptographic
+ * primitive it uses. We're talking odds of dying in a car wreck vs. odds of
+ * being personally impacted on the head by a meteorite. Nobody without a
+ * billion dollar budget is going to break into your network by actually
+ * cracking Salsa20/12 (or even /8) in the field.
+ *
+ * So stop worrying about the cipher unless you are, say, the Kremlin and your
+ * adversary is the NSA and the GCHQ. In that case... well that's above my
+ * pay grade. I'll just say defense in depth.
  */
 #define ZT_PROTO_SALSA20_ROUNDS 12
 
-// Indices of fields in normal packet header -- do not change as this
-// might require both code rework and will break compatibility.
+// Field indexes in packet header
 #define ZT_PACKET_IDX_IV 0
 #define ZT_PACKET_IDX_DEST 8
 #define ZT_PACKET_IDX_SOURCE 13
@@ -147,16 +203,19 @@
 
 /**
  * Packet buffer size (can be changed)
+ *
+ * The current value is big enough for ZT_MAX_PACKET_FRAGMENTS, the pragmatic
+ * packet fragment limit, times the default UDP MTU. Most packets won't be
+ * this big.
  */
 #define ZT_PROTO_MAX_PACKET_LENGTH (ZT_MAX_PACKET_FRAGMENTS * ZT_UDP_DEFAULT_PAYLOAD_MTU)
 
 /**
- * Minimum viable packet length (also length of header)
+ * Minimum viable packet length (a.k.a. header length)
  */
 #define ZT_PROTO_MIN_PACKET_LENGTH ZT_PACKET_IDX_PAYLOAD
 
-// Indexes of fields in fragment header -- also can't be changed without
-// breaking compatibility.
+// Indexes of fields in fragment header
 #define ZT_PACKET_FRAGMENT_IDX_PACKET_ID 0
 #define ZT_PACKET_FRAGMENT_IDX_DEST 8
 #define ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR 13
@@ -165,7 +224,7 @@
 #define ZT_PACKET_FRAGMENT_IDX_PAYLOAD 16
 
 /**
- * Value found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR in fragments
+ * Magic number found at ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR
  */
 #define ZT_PACKET_FRAGMENT_INDICATOR ZT_ADDRESS_RESERVED_PREFIX
 
@@ -174,24 +233,17 @@
  */
 #define ZT_PROTO_MIN_FRAGMENT_LENGTH ZT_PACKET_FRAGMENT_IDX_PAYLOAD
 
-/**
- * Length of LAN beacon packets
- */
-#define ZT_PROTO_BEACON_LENGTH 13
-
-/**
- * Index of address in a LAN beacon
- */
-#define ZT_PROTO_BEACON_IDX_ADDRESS 8
-
-// Destination address types from HELLO and OK(HELLO)
+// Destination address types from HELLO, OK(HELLO), and other message types
 #define ZT_PROTO_DEST_ADDRESS_TYPE_NONE 0
-#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 1
+#define ZT_PROTO_DEST_ADDRESS_TYPE_ZEROTIER 1   // reserved but unused
+#define ZT_PROTO_DEST_ADDRESS_TYPE_ETHERNET 2   // future use
+#define ZT_PROTO_DEST_ADDRESS_TYPE_BLUETOOTH 3  // future use
 #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV4 4
+#define ZT_PROTO_DEST_ADDRESS_TYPE_LTE_DIRECT 5 // future use
 #define ZT_PROTO_DEST_ADDRESS_TYPE_IPV6 6
 
 // Ephemeral key record flags
-#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01
+#define ZT_PROTO_EPHEMERAL_KEY_FLAG_FIPS 0x01   // future use
 
 // Ephemeral key record symmetric cipher types
 #define ZT_PROTO_EPHEMERAL_KEY_SYMMETRIC_CIPHER_SALSA2012_POLY1305 0x01
@@ -326,16 +378,6 @@ namespace ZeroTier {
  *
  * For unencrypted packets, MAC is computed on plaintext. Only HELLO is ever
  * sent in the clear, as it's the "here is my public key" message.
- *
- * Beacon format and beacon packets:
- *   <[8] 8 random bytes>
- *   <[5] sender ZT address>
- *
- * A beacon is a 13-byte packet containing only the address of the sender.
- * Receiving peers may or may not respond to beacons with a HELLO or other
- * message to initiate direct communication.
- *
- * Beacons may be used for direct LAN announcement or NAT traversal.
  */
 class Packet : public Buffer<ZT_PROTO_MAX_PACKET_LENGTH>
 {
@@ -636,7 +678,8 @@ public:
 		 *   <[...] serialized certificate of membership>
 		 *   [ ... additional certificates may follow ...]
 		 *
-		 * Certificate contains network ID, peer it was issued for, etc.
+		 * This is sent in response to ERROR_NEED_MEMBERSHIP_CERTIFICATE and may
+		 * be pushed at any other time to keep exchanged certificates up to date.
 		 *
 		 * OK/ERROR are not generated.
 		 */
@@ -680,10 +723,8 @@ public:
 		/* Network configuration refresh request:
 		 *   <[...] array of 64-bit network IDs>
 		 *
-		 * This message can be sent by the network configuration master node
-		 * to request that nodes refresh their network configuration. It can
-		 * thus be used to "push" updates so that network config changes will
-		 * take effect quickly.
+		 * This can be sent by the network controller to inform a node that it
+		 * should now make a NETWORK_CONFIG_REQUEST.
 		 *
 		 * It does not generate an OK or ERROR message, and is treated only as
 		 * a hint to refresh now.
@@ -769,7 +810,7 @@ public:
 		 */
 		VERB_MULTICAST_FRAME = 14,
 
-		/* Ephemeral (PFS) key push:
+		/* Ephemeral (PFS) key push: (UNFINISHED, NOT IMPLEMENTED YET)
 		 *   <[2] flags (unused and reserved, must be 0)>
 		 *   <[2] length of padding / extra field section>
 		 *   <[...] padding / extra field section>
@@ -826,21 +867,51 @@ public:
 		VERB_SET_EPHEMERAL_KEY = 15,
 
 		/* Push of potential endpoints for direct communication:
+		 *   <[2] 16-bit number of paths>
+		 *   <[...] paths>
+		 *
+		 * Path record format:
 		 *   <[1] flags>
-		 *   <[2] number of addresses>
-		 *   <[...] address types and addresses>
+		 *   <[1] metric from 0 (highest priority) to 255 (lowest priority)>
+		 *   <[2] length of extended path characteristics or 0 for none>
+		 *   <[...] extended path characteristics>
+		 *   <[1] address type>
+		 *   <[1] address length in bytes>
+		 *   <[...] address>
+		 *
+		 * Path record flags:
+		 *   0x01 - Forget this path if it is currently known
+		 *   0x02 - Blacklist this path, do not use
+		 *   0x04 - Reliable path (no NAT keepalives, etc. are necessary)
+		 *   0x08 - Disable encryption (trust: privacy)
+		 *   0x10 - Disable encryption and authentication (trust: ultimate)
 		 *
 		 * Address types and addresses are of the same format as the destination
 		 * address type and address in HELLO.
 		 *
 		 * The receiver may, upon receiving a push, attempt to establish a
-		 * direct link to one or more of the indicated addresses. Senders should
-		 * only send address pushes to peers that they have some relationship
-		 * with such as a shared network membership or a mutual trust.
+		 * direct link to one or more of the indicated addresses. It is the
+		 * responsibility of the sender to limit which peers it pushes direct
+		 * paths to to those with whom it has a trust relationship. The receiver
+		 * must obey any restrictions provided such as exclusivity or blacklists.
+		 * OK responses to this message are optional.
 		 *
-		 * OK/ERROR are not generated.
+		 * Note that a direct path push does not imply that learned paths can't
+		 * be used unless they are blacklisted explicitly or unless flag 0x01
+		 * is set.
+		 *
+		 * Only a subset of this functionality is currently implemented: basic
+		 * path pushing and learning. Metrics, most flags, and OK responses are
+		 * not yet implemented as of 1.0.4.
+		 *
+		 * OK response payload:
+		 *   <[2] 16-bit number of active direct paths we already have>
+		 *   <[2] 16-bit number of paths in push that we don't already have>
+		 *   <[2] 16-bit number of new paths we are trying (or will try)>
+		 *
+		 * ERROR is presently not sent.
 		 */
-		VERB_PHYSICAL_ADDRESS_PUSH = 16
+		VERB_PUSH_DIRECT_PATHS = 16
 	};
 
 	/**

+ 63 - 109
node/Path.hpp

@@ -28,144 +28,65 @@
 #ifndef ZT_PATH_HPP
 #define ZT_PATH_HPP
 
-#include <stdint.h>
-#include <string.h>
-
-#include <stdexcept>
-#include <string>
-#include <algorithm>
-
 #include "Constants.hpp"
-#include "Node.hpp"
 #include "InetAddress.hpp"
 #include "Utils.hpp"
-#include "AntiRecursion.hpp"
-#include "RuntimeEnvironment.hpp"
 
 namespace ZeroTier {
 
-/**
- * WAN address and protocol for reaching a peer
- *
- * This structure is volatile and memcpy-able, and depends on
- * InetAddress being similarly safe.
- */
 class Path
 {
 public:
-	Path() :
-		_addr(),
-		_lastSend(0),
-		_lastReceived(0),
-		_fixed(false) {}
-
-	Path(const Path &p) throw() { memcpy(this,&p,sizeof(Path)); }
-
-	Path(const InetAddress &addr,bool fixed) :
-		_addr(addr),
-		_lastSend(0),
-		_lastReceived(0),
-		_fixed(fixed) {}
-
-	inline void init(const InetAddress &addr,bool fixed)
-	{
-		_addr = addr;
-		_lastSend = 0;
-		_lastReceived = 0;
-		_fixed = fixed;
-	}
-
-	inline Path &operator=(const Path &p)
+	// Must be the same values as ZT1_LocalInterfaceAddressTrust in ZeroTierOne.h
+	enum Trust
 	{
-		if (this != &p)
-			memcpy(this,&p,sizeof(Path));
-		return *this;
-	}
-
-	inline const InetAddress &address() const throw() { return _addr; }
-
-	inline uint64_t lastSend() const throw() { return _lastSend; }
-	inline uint64_t lastReceived() const throw() { return _lastReceived; }
+		TRUST_NORMAL = 0,
+		TRUST_PRIVACY = 1,
+		TRUST_ULTIMATE = 2
+	};
 
-	/**
-	 * Called when a packet is sent to this path
-	 *
-	 * This is called automatically by Path::send().
-	 *
-	 * @param t Time of send
-	 */
-	inline void sent(uint64_t t)
-		throw()
+	Path() :
+		_addr(),
+		_metric(0),
+		_trust(TRUST_NORMAL),
+		_reliable(false)
 	{
-		_lastSend = t;
 	}
 
-	/**
-	 * Called when a packet is received from this path
-	 *
-	 * @param t Time of receive
-	 */
-	inline void received(uint64_t t)
-		throw()
+	Path(const InetAddress &addr,int metric,Trust trust,bool reliable) :
+		_addr(addr),
+		_metric(metric),
+		_trust(trust),
+		_reliable(reliable)
 	{
-		_lastReceived = t;
 	}
 
 	/**
-	 * @return Is this a fixed path?
+	 * @return Physical address
 	 */
-	inline bool fixed() const throw() { return _fixed; }
+	inline const InetAddress &address() const throw() { return _addr; }
 
 	/**
-	 * @param f New value of fixed path flag
+	 * @return Metric (higher == worse) or negative if path is blacklisted
 	 */
-	inline void setFixed(bool f) throw() { _fixed = f; }
+	inline int metric() const throw() { return _metric; }
 
 	/**
-	 * @param now Current time
-	 * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms
+	 * @return Path trust level
 	 */
-	inline bool active(uint64_t now) const
-		throw()
-	{
-		return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) );
-	}
+	inline Trust trust() const throw() { return _trust; }
 
 	/**
-	 * Send a packet via this path
-	 *
-	 * @param RR Runtime environment
-	 * @param data Packet data
-	 * @param len Packet length
-	 * @param now Current time
-	 * @return True if transport reported success
+	 * @return True if path is considered reliable (no NAT keepalives etc. are needed)
 	 */
-	inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
-	{
-		if (RR->node->putPacket(_addr,data,len)) {
-			sent(now);
-			RR->antiRec->logOutgoingZT(data,len);
-			return true;
-		}
-		return false;
-	}
+	inline bool reliable() const throw() { return _reliable; }
 
 	/**
-	 * @param now Current time
-	 * @return Human-readable address and other information about this path
+	 * @return True if address is non-NULL
 	 */
-	inline std::string toString(uint64_t now) const
-	{
-		char tmp[1024];
-		Utils::snprintf(tmp,sizeof(tmp),"%s(%s)",
-			_addr.toString().c_str(),
-			((_fixed) ? "fixed" : (active(now) ? "active" : "inactive"))
-		);
-		return std::string(tmp);
-	}
-
 	inline operator bool() const throw() { return (_addr); }
 
+	// Comparisons are by address only
 	inline bool operator==(const Path &p) const throw() { return (_addr == p._addr); }
 	inline bool operator!=(const Path &p) const throw() { return (_addr != p._addr); }
 	inline bool operator<(const Path &p) const throw() { return (_addr < p._addr); }
@@ -173,11 +94,44 @@ public:
 	inline bool operator<=(const Path &p) const throw() { return (_addr <= p._addr); }
 	inline bool operator>=(const Path &p) const throw() { return (_addr >= p._addr); }
 
-private:
+	/**
+	 * Check whether this address is valid for a ZeroTier path
+	 *
+	 * This checks the address type and scope against address types and scopes
+	 * that we currently support for ZeroTier communication.
+	 *
+	 * @param a Address to check
+	 * @return True if address is good for ZeroTier path use
+	 */
+	static inline bool isAddressValidForPath(const InetAddress &a)
+		throw()
+	{
+		if ((a.ss_family == AF_INET)||(a.ss_family == AF_INET6)) {
+			switch(a.ipScope()) {
+				/* Note: we don't do link-local at the moment. Unfortunately these
+				 * cause several issues. The first is that they usually require a
+				 * device qualifier, which we don't handle yet and can't portably
+				 * push in PUSH_DIRECT_PATHS. The second is that some OSes assign
+				 * these very ephemerally or otherwise strangely. So we'll use
+				 * private, pseudo-private, shared (e.g. carrier grade NAT), or
+				 * global IP addresses. */
+				case InetAddress::IP_SCOPE_PRIVATE:
+				case InetAddress::IP_SCOPE_PSEUDOPRIVATE:
+				case InetAddress::IP_SCOPE_SHARED:
+				case InetAddress::IP_SCOPE_GLOBAL:
+					return true;
+				default:
+					return false;
+			}
+		}
+		return false;
+	}
+
+protected:
 	InetAddress _addr;
-	uint64_t _lastSend;
-	uint64_t _lastReceived;
-	bool _fixed;
+	int _metric; // negative == blacklisted
+	Trust _trust;
+	bool _reliable;
 };
 
 } // namespace ZeroTier

+ 72 - 5
node/Peer.cpp

@@ -46,6 +46,7 @@ Peer::Peer(const Identity &myIdentity,const Identity &peerIdentity)
 	_lastMulticastFrame(0),
 	_lastAnnouncedTo(0),
 	_lastPathConfirmationSent(0),
+	_lastDirectPathPush(0),
 	_vMajor(0),
 	_vMinor(0),
 	_vRevision(0),
@@ -86,7 +87,7 @@ void Peer::received(
 			if (!pathIsConfirmed) {
 				if ((verb == Packet::VERB_OK)&&(inReVerb == Packet::VERB_HELLO)) {
 					// Learn paths if they've been confirmed via a HELLO
-					Path *slot = (Path *)0;
+					RemotePath *slot = (RemotePath *)0;
 					if (np < ZT1_MAX_PEER_NETWORK_PATHS) {
 						// Add new path
 						slot = &(_paths[np++]);
@@ -101,7 +102,7 @@ void Peer::received(
 						}
 					}
 					if (slot) {
-						slot->init(remoteAddr,false);
+						*slot = RemotePath(remoteAddr,false);
 						slot->received(now);
 						_numPaths = np;
 						pathIsConfirmed = true;
@@ -193,7 +194,7 @@ void Peer::attemptToContactAt(const RuntimeEnvironment *RR,const InetAddress &at
 
 void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
 {
-	Path *const bestPath = getBestPath(now);
+	RemotePath *const bestPath = getBestPath(now);
 	if ((bestPath)&&(bestPath->active(now))) {
 		if ((now - bestPath->lastReceived()) >= ZT_PEER_DIRECT_PING_DELAY) {
 			TRACE("PING %s(%s)",_id.address().toString().c_str(),bestPath->address().toString().c_str());
@@ -207,7 +208,73 @@ void Peer::doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now)
 	}
 }
 
-void Peer::addPath(const Path &newp)
+void Peer::pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force)
+{
+	if ((((now - _lastDirectPathPush) >= ZT_DIRECT_PATH_PUSH_INTERVAL)||(force))) {
+		_lastDirectPathPush = now;
+
+		std::vector<Path> dps(RR->node->directPaths());
+		TRACE("pushing %u direct paths (local interface addresses) to %s",(unsigned int)dps.size(),_id.address().toString().c_str());
+
+		std::vector<Path>::const_iterator p(dps.begin());
+		while (p != dps.end()) {
+			Packet outp(_id.address(),RR->identity.address(),Packet::VERB_PUSH_DIRECT_PATHS);
+			outp.addSize(2); // leave room for count
+
+			unsigned int count = 0;
+			while ((p != dps.end())&&((outp.size() + 24) < ZT_PROTO_MAX_PACKET_LENGTH)) {
+				uint8_t addressType = 4;
+				switch(p->address().ss_family) {
+					case AF_INET:
+						break;
+					case AF_INET6:
+						addressType = 6;
+						break;
+					default:
+						++p;
+						continue;
+				}
+
+				uint8_t flags = 0;
+				if (p->metric() < 0)
+					flags |= (0x01 | 0x02); // forget and blacklist
+				else {
+					if (p->reliable())
+						flags |= 0x04; // no NAT keepalives and such
+					switch(p->trust()) {
+						default:
+							break;
+						case Path::TRUST_PRIVACY:
+							flags |= 0x08; // no encryption
+							break;
+						case Path::TRUST_ULTIMATE:
+							flags |= (0x08 | 0x10); // no encryption, no authentication (redundant but go ahead and set both)
+							break;
+					}
+				}
+
+				outp.append(flags);
+				outp.append((uint8_t)((p->metric() >= 0) ? ((p->metric() <= 255) ? p->metric() : 255) : 0));
+				outp.append((uint16_t)0);
+				outp.append(addressType);
+				outp.append((uint8_t)((addressType == 4) ? 6 : 18));
+				outp.append(p->address().rawIpData(),((addressType == 4) ? 4 : 16));
+				outp.append((uint16_t)p->address().port());
+
+				++count;
+				++p;
+			}
+
+			if (count) {
+				outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
+				outp.armor(_key,true);
+				path->send(RR,outp.data(),outp.size(),now);
+			}
+		}
+	}
+}
+
+void Peer::addPath(const RemotePath &newp)
 {
 	unsigned int np = _numPaths;
 
@@ -218,7 +285,7 @@ void Peer::addPath(const Path &newp)
 		}
 	}
 
-	Path *slot = (Path *)0;
+	RemotePath *slot = (RemotePath *)0;
 	if (np < ZT1_MAX_PEER_NETWORK_PATHS) {
 		// Add new path
 		slot = &(_paths[np++]);

+ 22 - 15
node/Peer.hpp

@@ -40,7 +40,7 @@
 #include "../include/ZeroTierOne.h"
 
 #include "RuntimeEnvironment.hpp"
-#include "Path.hpp"
+#include "RemotePath.hpp"
 #include "Address.hpp"
 #include "Utils.hpp"
 #include "Identity.hpp"
@@ -53,11 +53,7 @@
 namespace ZeroTier {
 
 /**
- * Peer on P2P Network
- *
- * This struture is not locked, volatile, and memcpy-able. NonCopyable
- * semantics are just there to prevent bugs, not because it isn't safe
- * to copy.
+ * Peer on P2P Network (virtual layer 1)
  */
 class Peer : NonCopyable
 {
@@ -130,9 +126,9 @@ public:
 	 * @param now Current time
 	 * @return Best path or NULL if there are no active (or fixed) direct paths
 	 */
-	inline Path *getBestPath(uint64_t now)
+	inline RemotePath *getBestPath(uint64_t now)
 	{
-		Path *bestPath = (Path *)0;
+		RemotePath *bestPath = (RemotePath *)0;
 		uint64_t lrMax = 0;
 		for(unsigned int p=0,np=_numPaths;p<np;++p) {
 			if ((_paths[p].active(now))&&(_paths[p].lastReceived() >= lrMax)) {
@@ -152,14 +148,14 @@ public:
 	 * @param now Current time
 	 * @return Path used on success or NULL on failure
 	 */
-	inline Path *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+	inline RemotePath *send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
 	{
-		Path *bestPath = getBestPath(now);
+		RemotePath *bestPath = getBestPath(now);
 		if (bestPath) {
 			if (bestPath->send(RR,data,len,now))
 				return bestPath;
 		}
-		return (Path *)0;
+		return (RemotePath *)0;
 	}
 
 	/**
@@ -182,12 +178,22 @@ public:
 	 */
 	void doPingAndKeepalive(const RuntimeEnvironment *RR,uint64_t now);
 
+	/**
+	 * Push direct paths if we haven't done so in [rate limit] milliseconds
+	 *
+	 * @param RR Runtime environment
+	 * @param path Remote path to use to send the push
+	 * @param now Current time
+	 * @param force If true, push regardless of rate limit
+	 */
+	void pushDirectPaths(const RuntimeEnvironment *RR,RemotePath *path,uint64_t now,bool force);
+
 	/**
 	 * @return All known direct paths to this peer
 	 */
-	inline std::vector<Path> paths() const
+	inline std::vector<RemotePath> paths() const
 	{
-		std::vector<Path> pp;
+		std::vector<RemotePath> pp;
 		for(unsigned int p=0,np=_numPaths;p<np;++p)
 			pp.push_back(_paths[p]);
 		return pp;
@@ -295,7 +301,7 @@ public:
 	 *
 	 * @param p New path to add
 	 */
-	void addPath(const Path &newp);
+	void addPath(const RemotePath &newp);
 
 	/**
 	 * Clear paths
@@ -412,12 +418,13 @@ private:
 	uint64_t _lastMulticastFrame;
 	uint64_t _lastAnnouncedTo;
 	uint64_t _lastPathConfirmationSent;
+	uint64_t _lastDirectPathPush;
 	uint16_t _vProto;
 	uint16_t _vMajor;
 	uint16_t _vMinor;
 	uint16_t _vRevision;
 	Identity _id;
-	Path _paths[ZT1_MAX_PEER_NETWORK_PATHS];
+	RemotePath _paths[ZT1_MAX_PEER_NETWORK_PATHS];
 	unsigned int _numPaths;
 	unsigned int _latency;
 

+ 142 - 0
node/RemotePath.hpp

@@ -0,0 +1,142 @@
+/*
+ * ZeroTier One - Network Virtualization Everywhere
+ * Copyright (C) 2011-2015  ZeroTier, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * --
+ *
+ * ZeroTier may be used and distributed under the terms of the GPLv3, which
+ * are available at: http://www.gnu.org/licenses/gpl-3.0.html
+ *
+ * If you would like to embed ZeroTier into a commercial application or
+ * redistribute it in a modified binary form, please contact ZeroTier Networks
+ * LLC. Start here: http://www.zerotier.com/
+ */
+
+#ifndef ZT_REMOTEPATH_HPP
+#define ZT_REMOTEPATH_HPP
+
+#include <stdint.h>
+#include <string.h>
+
+#include <stdexcept>
+#include <algorithm>
+
+#include "Path.hpp"
+#include "Node.hpp"
+#include "AntiRecursion.hpp"
+#include "RuntimeEnvironment.hpp"
+
+namespace ZeroTier {
+
+/**
+ * Path to a remote peer
+ *
+ * This extends Path to include status information about path activity.
+ */
+class RemotePath : public Path
+{
+public:
+	RemotePath() :
+		Path(),
+		_lastSend(0),
+		_lastReceived(0),
+		_fixed(false) {}
+
+	RemotePath(const InetAddress &addr,bool fixed) :
+		Path(addr,0,TRUST_NORMAL,false),
+		_lastSend(0),
+		_lastReceived(0),
+		_fixed(fixed) {}
+
+	inline uint64_t lastSend() const throw() { return _lastSend; }
+	inline uint64_t lastReceived() const throw() { return _lastReceived; }
+
+	/**
+	 * @return Is this a fixed path?
+	 */
+	inline bool fixed() const throw() { return _fixed; }
+
+	/**
+	 * @param f New value of fixed flag
+	 */
+	inline void setFixed(const bool f)
+		throw()
+	{
+		_fixed = f;
+	}
+
+	/**
+	 * Called when a packet is sent to this remote path
+	 *
+	 * This is called automatically by RemotePath::send().
+	 *
+	 * @param t Time of send
+	 */
+	inline void sent(uint64_t t)
+		throw()
+	{
+		_lastSend = t;
+	}
+
+	/**
+	 * Called when a packet is received from this remote path
+	 *
+	 * @param t Time of receive
+	 */
+	inline void received(uint64_t t)
+		throw()
+	{
+		_lastReceived = t;
+	}
+
+	/**
+	 * @param now Current time
+	 * @return True if this path is fixed or has received data in last ACTIVITY_TIMEOUT ms
+	 */
+	inline bool active(uint64_t now) const
+		throw()
+	{
+		return ( (_fixed) || ((now - _lastReceived) < ZT_PEER_ACTIVITY_TIMEOUT) );
+	}
+
+	/**
+	 * Send a packet via this path
+	 *
+	 * @param RR Runtime environment
+	 * @param data Packet data
+	 * @param len Packet length
+	 * @param now Current time
+	 * @return True if transport reported success
+	 */
+	inline bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now)
+	{
+		if (RR->node->putPacket(_addr,data,len)) {
+			sent(now);
+			RR->antiRec->logOutgoingZT(data,len);
+			return true;
+		}
+		return false;
+	}
+
+private:
+	uint64_t _lastSend;
+	uint64_t _lastReceived;
+	bool _fixed;
+};
+
+} // namespace ZeroTier
+
+#endif

+ 0 - 3
node/RuntimeEnvironment.hpp

@@ -38,7 +38,6 @@ namespace ZeroTier {
 class NodeConfig;
 class Switch;
 class Topology;
-class CMWC4096;
 class Node;
 class Multicaster;
 class AntiRecursion;
@@ -55,7 +54,6 @@ public:
 		node(n),
 		identity(),
 		localNetworkController((NetworkController *)0),
-		prng((CMWC4096 *)0),
 		sw((Switch *)0),
 		mc((Multicaster *)0),
 		antiRec((AntiRecursion *)0),
@@ -83,7 +81,6 @@ public:
 	 * These are constant and never null after startup unless indicated.
 	 */
 
-	CMWC4096 *prng;
 	Switch *sw;
 	Multicaster *mc;
 	AntiRecursion *antiRec;

+ 1 - 1
node/SelfAwareness.cpp

@@ -120,7 +120,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &reporterPhysi
 		// they are still considered alive so that we will re-establish direct links.
 		SharedPtr<Peer> sn(RR->topology->getBestRoot());
 		if (sn) {
-			Path *snp = sn->getBestPath(now);
+			RemotePath *snp = sn->getBestPath(now);
 			if (snp) {
 				for(std::vector< SharedPtr<Peer> >::const_iterator p(rset.peersReset.begin());p!=rset.peersReset.end();++p) {
 					if ((*p)->alive(now)) {

+ 85 - 100
node/Switch.cpp

@@ -42,15 +42,30 @@
 #include "InetAddress.hpp"
 #include "Topology.hpp"
 #include "Peer.hpp"
-#include "CMWC4096.hpp"
 #include "AntiRecursion.hpp"
 #include "Packet.hpp"
 
 namespace ZeroTier {
 
+#ifdef ZT_TRACE
+static const char *etherTypeName(const unsigned int etherType)
+{
+	switch(etherType) {
+		case ZT_ETHERTYPE_IPV4:  return "IPV4";
+		case ZT_ETHERTYPE_ARP:   return "ARP";
+		case ZT_ETHERTYPE_RARP:  return "RARP";
+		case ZT_ETHERTYPE_ATALK: return "ATALK";
+		case ZT_ETHERTYPE_AARP:  return "AARP";
+		case ZT_ETHERTYPE_IPX_A: return "IPX_A";
+		case ZT_ETHERTYPE_IPX_B: return "IPX_B";
+		case ZT_ETHERTYPE_IPV6:  return "IPV6";
+	}
+	return "UNKNOWN";
+}
+#endif // ZT_TRACE
+
 Switch::Switch(const RuntimeEnvironment *renv) :
-	RR(renv),
-	_lastBeacon(0)
+	RR(renv)
 {
 }
 
@@ -61,9 +76,7 @@ Switch::~Switch()
 void Switch::onRemotePacket(const InetAddress &fromAddr,const void *data,unsigned int len)
 {
 	try {
-		if (len == ZT_PROTO_BEACON_LENGTH) {
-			_handleBeacon(fromAddr,Buffer<ZT_PROTO_BEACON_LENGTH>(data,len));
-		} else if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) {
+		if (len > ZT_PROTO_MIN_FRAGMENT_LENGTH) {
 			if (((const unsigned char *)data)[ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR] == ZT_PACKET_FRAGMENT_INDICATOR) {
 				_handleRemotePacketFragment(fromAddr,data,len);
 			} else if (len >= ZT_PROTO_MIN_PACKET_LENGTH) {
@@ -165,42 +178,34 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	if (to[0] == MAC::firstOctetForNetwork(network->id())) {
 		// Destination is another ZeroTier peer on the same network
 
-		Address toZT(to.toAddress(network->id()));
-		if (network->isAllowed(toZT)) {
-			if (network->peerNeedsOurMembershipCertificate(toZT,RR->node->now())) {
-				// TODO: once there are no more <1.0.0 nodes around, we can
-				// bundle this with EXT_FRAME instead of sending two packets.
-				Packet outp(toZT,RR->identity.address(),Packet::VERB_NETWORK_MEMBERSHIP_CERTIFICATE);
+		Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
+		const bool includeCom = network->peerNeedsOurMembershipCertificate(toZT,RR->node->now());
+		if ((fromBridged)||(includeCom)) {
+			Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
+			outp.append(network->id());
+			if (includeCom) {
+				outp.append((unsigned char)0x01); // 0x01 -- COM included
 				nconf->com().serialize(outp);
-				send(outp,true,network->id());
-			}
-
-			if (fromBridged) {
-				// EXT_FRAME is used for bridging or if we want to include a COM
-				Packet outp(toZT,RR->identity.address(),Packet::VERB_EXT_FRAME);
-				outp.append(network->id());
-				outp.append((unsigned char)0);
-				to.appendTo(outp);
-				from.appendTo(outp);
-				outp.append((uint16_t)etherType);
-				outp.append(data,len);
-				outp.compress();
-				send(outp,true,network->id());
 			} else {
-				// FRAME is a shorter version that can be used when there's no bridging and no COM
-				Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
-				outp.append(network->id());
-				outp.append((uint16_t)etherType);
-				outp.append(data,len);
-				outp.compress();
-				send(outp,true,network->id());
+				outp.append((unsigned char)0x00);
 			}
-
-			//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged);
+			to.appendTo(outp);
+			from.appendTo(outp);
+			outp.append((uint16_t)etherType);
+			outp.append(data,len);
+			outp.compress();
+			send(outp,true,network->id());
 		} else {
-			TRACE("%.16llx: UNICAST: %s -> %s etherType==%s dropped, destination not a member of private network",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
+			Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
+			outp.append(network->id());
+			outp.append((uint16_t)etherType);
+			outp.append(data,len);
+			outp.compress();
+			send(outp,true,network->id());
 		}
 
+		//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
+
 		return;
 	}
 
@@ -210,22 +215,19 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		Address bridges[ZT_MAX_BRIDGE_SPAM];
 		unsigned int numBridges = 0;
 
+		/* Create an array of up to ZT_MAX_BRIDGE_SPAM recipients for this bridged frame. */
 		bridges[0] = network->findBridgeTo(to);
-		if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->isAllowed(bridges[0]))&&(network->permitsBridging(bridges[0]))) {
-			// We have a known bridge route for this MAC.
+		if ((bridges[0])&&(bridges[0] != RR->identity.address())&&(network->permitsBridging(bridges[0]))) {
+			/* We have a known bridge route for this MAC, send it there. */
 			++numBridges;
 		} else if (!nconf->activeBridges().empty()) {
 			/* If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active
-			 * bridges. This is similar to what many switches do -- if they do not
-			 * know which port corresponds to a MAC, they send it to all ports. If
-			 * there aren't any active bridges, numBridges will stay 0 and packet
-			 * is dropped. */
+			 * bridges. If someone responds, we'll learn the route. */
 			std::vector<Address>::const_iterator ab(nconf->activeBridges().begin());
 			if (nconf->activeBridges().size() <= ZT_MAX_BRIDGE_SPAM) {
 				// If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all
 				while (ab != nconf->activeBridges().end()) {
-					if (network->isAllowed(*ab)) // config sanity check
-						bridges[numBridges++] = *ab;
+					bridges[numBridges++] = *ab;
 					++ab;
 				}
 			} else {
@@ -233,9 +235,8 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 				while (numBridges < ZT_MAX_BRIDGE_SPAM) {
 					if (ab == nconf->activeBridges().end())
 						ab = nconf->activeBridges().begin();
-					if (((unsigned long)RR->prng->next32() % (unsigned long)nconf->activeBridges().size()) == 0) {
-						if (network->isAllowed(*ab)) // config sanity check
-							bridges[numBridges++] = *ab;
+					if (((unsigned long)RR->node->prng() % (unsigned long)nconf->activeBridges().size()) == 0) {
+						bridges[numBridges++] = *ab;
 						++ab;
 					} else ++ab;
 				}
@@ -245,7 +246,12 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		for(unsigned int b=0;b<numBridges;++b) {
 			Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(network->id());
-			outp.append((unsigned char)0);
+			if (network->peerNeedsOurMembershipCertificate(bridges[b],RR->node->now())) {
+				outp.append((unsigned char)0x01); // 0x01 -- COM included
+				nconf->com().serialize(outp);
+			} else {
+				outp.append((unsigned char)0);
+			}
 			to.appendTo(outp);
 			from.appendTo(outp);
 			outp.append((uint16_t)etherType);
@@ -320,7 +326,7 @@ bool Switch::unite(const Address &p1,const Address &p2,bool force)
 	 * the order we make each attempted NAT-t favor one or the other going
 	 * first, meaning if it doesn't succeed the first time it might the second
 	 * and so forth. */
-	unsigned int alt = RR->prng->next32() & 1;
+	unsigned int alt = (unsigned int)RR->node->prng() & 1;
 	unsigned int completed = alt + 2;
 	while (alt != completed) {
 		if ((alt & 1) == 0) {
@@ -529,22 +535,6 @@ unsigned long Switch::doTimerTasks(uint64_t now)
 	return nextDelay;
 }
 
-const char *Switch::etherTypeName(const unsigned int etherType)
-	throw()
-{
-	switch(etherType) {
-		case ZT_ETHERTYPE_IPV4:  return "IPV4";
-		case ZT_ETHERTYPE_ARP:   return "ARP";
-		case ZT_ETHERTYPE_RARP:  return "RARP";
-		case ZT_ETHERTYPE_ATALK: return "ATALK";
-		case ZT_ETHERTYPE_AARP:  return "AARP";
-		case ZT_ETHERTYPE_IPX_A: return "IPX_A";
-		case ZT_ETHERTYPE_IPX_B: return "IPX_B";
-		case ZT_ETHERTYPE_IPV6:  return "IPV6";
-	}
-	return "UNKNOWN";
-}
-
 void Switch::_handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len)
 {
 	Packet::Fragment fragment(data,len);
@@ -687,23 +677,6 @@ void Switch::_handleRemotePacketHead(const InetAddress &fromAddr,const void *dat
 	}
 }
 
-void Switch::_handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEACON_LENGTH> &data)
-{
-	Address beaconAddr(data.field(ZT_PROTO_BEACON_IDX_ADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
-	if (beaconAddr == RR->identity.address())
-		return;
-	SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
-	if (peer) {
-		const uint64_t now = RR->node->now();
-		if ((now - _lastBeacon) >= ZT_MIN_BEACON_RESPONSE_INTERVAL) {
-			_lastBeacon = now;
-			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
-			outp.armor(peer->key(),false);
-			RR->node->putPacket(fromAddr,outp.data(),outp.size());
-		}
-	}
-}
-
 Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
 {
 	SharedPtr<Peer> root(RR->topology->getBestRoot(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
@@ -724,32 +697,43 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
 	if (peer) {
 		const uint64_t now = RR->node->now();
 
-		Path *viaPath = peer->getBestPath(now);
+		SharedPtr<Network> network;
+		SharedPtr<NetworkConfig> nconf;
+		if (nwid) {
+			network = RR->node->network(nwid);
+			if (!network)
+				return false; // we probably just left this network, let its packets die
+			nconf = network->config2();
+			if (!nconf)
+				return false; // sanity check: unconfigured network? why are we trying to talk to it?
+		}
+
+		RemotePath *viaPath = peer->getBestPath(now);
+		SharedPtr<Peer> relay;
 		if (!viaPath) {
-			SharedPtr<Peer> relay;
-
-			if (nwid) {
-				SharedPtr<Network> network(RR->node->network(nwid));
-				if (network) {
-					SharedPtr<NetworkConfig> nconf(network->config2());
-					if (nconf) {
-						unsigned int latency = ~((unsigned int)0);
-						for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
-							if (r->first != peer->address()) {
-								SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
-								if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
-									rp.swap(relay);
-							}
-						}
+			// See if this network has a preferred relay (if packet has an associated network)
+			if (nconf) {
+				unsigned int latency = ~((unsigned int)0);
+				for(std::vector< std::pair<Address,InetAddress> >::const_iterator r(nconf->relays().begin());r!=nconf->relays().end();++r) {
+					if (r->first != peer->address()) {
+						SharedPtr<Peer> rp(RR->topology->getPeer(r->first));
+						if ((rp)&&(rp->hasActiveDirectPath(now))&&(rp->latency() <= latency))
+							rp.swap(relay);
 					}
 				}
 			}
 
+			// Otherwise relay off a root server
 			if (!relay)
 				relay = RR->topology->getBestRoot();
 
 			if (!(relay)||(!(viaPath = relay->getBestPath(now))))
-				return false;
+				return false; // no paths, no root servers?
+		}
+
+		if ((network)&&(relay)&&(network->isAllowed(peer->address()))) {
+			// Push hints for direct connectivity to this peer if we are relaying
+			peer->pushDirectPaths(RR,viaPath,now,false);
 		}
 
 		Packet tmp(packet);
@@ -761,7 +745,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
 
 		if (viaPath->send(RR,tmp.data(),chunkSize,now)) {
 			if (chunkSize < tmp.size()) {
-				// Too big for one bite, fragment the rest
+				// Too big for one packet, fragment the rest
 				unsigned int fragStart = chunkSize;
 				unsigned int remaining = tmp.size() - chunkSize;
 				unsigned int fragsRemaining = (remaining / (ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH));
@@ -777,6 +761,7 @@ bool Switch::_trySend(const Packet &packet,bool encrypt,uint64_t nwid)
 					remaining -= chunkSize;
 				}
 			}
+
 			return true;
 		}
 	} else {

+ 5 - 10
node/Switch.hpp

@@ -108,10 +108,14 @@ public:
 	 * 
 	 * Needless to say, the packet's source must be this node. Otherwise it
 	 * won't be encrypted right. (This is not used for relaying.)
+	 *
+	 * The network ID should only be specified for frames and other actual
+	 * network traffic. Other traffic such as controller requests and regular
+	 * protocol messages should specify zero.
 	 * 
 	 * @param packet Packet to send
 	 * @param encrypt Encrypt packet payload? (always true except for HELLO)
-	 * @param nwid Network ID or 0 if message is not related to a specific network
+	 * @param nwid Related network ID or 0 if message is not in-network traffic
 	 */
 	void send(const Packet &packet,bool encrypt,uint64_t nwid);
 
@@ -173,22 +177,13 @@ public:
 	 */
 	unsigned long doTimerTasks(uint64_t now);
 
-	/**
-	 * @param etherType Ethernet type ID
-	 * @return Human-readable name
-	 */
-	static const char *etherTypeName(const unsigned int etherType)
-		throw();
-
 private:
 	void _handleRemotePacketFragment(const InetAddress &fromAddr,const void *data,unsigned int len);
 	void _handleRemotePacketHead(const InetAddress &fromAddr,const void *data,unsigned int len);
-	void _handleBeacon(const InetAddress &fromAddr,const Buffer<ZT_PROTO_BEACON_LENGTH> &data);
 	Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
 	bool _trySend(const Packet &packet,bool encrypt,uint64_t nwid);
 
 	const RuntimeEnvironment *const RR;
-	volatile uint64_t _lastBeacon;
 
 	// Outsanding WHOIS requests and how many retries they've undergone
 	struct WhoisRequest

+ 1 - 1
node/Topology.cpp

@@ -62,7 +62,7 @@ void Topology::setRootServers(const std::map< Identity,std::vector<InetAddress>
 			if (!p)
 				p = SharedPtr<Peer>(new Peer(RR->identity,i->first));
 			for(std::vector<InetAddress>::const_iterator j(i->second.begin());j!=i->second.end();++j)
-				p->addPath(Path(*j,true));
+				p->addPath(RemotePath(*j,true));
 			p->use(now);
 			_rootPeers.push_back(p);
 		}

+ 4 - 14
node/Utils.hpp

@@ -60,20 +60,10 @@ public:
 	static inline bool secureEq(const void *a,const void *b,unsigned int len)
 		throw()
 	{
-		const char *p1 = (const char *)a;
-		const char *p2 = (const char *)b;
-		uint64_t diff = 0;
-
-		while (len >= 8) {
-			diff |= (*((const uint64_t *)p1) ^ *((const uint64_t *)p2));
-			p1 += 8;
-			p2 += 8;
-			len -= 8;
-		}
-		while (len--)
-			diff |= (uint64_t)(*p1++ ^ *p2++);
-
-		return (diff == 0ULL);
+		char diff = 0;
+		for(unsigned int i=0;i<len;++i)
+			diff |= ( (reinterpret_cast<const char *>(a))[i] ^ (reinterpret_cast<const char *>(b))[i] );
+		return (diff == 0);
 	}
 
 	/**

+ 10 - 7
osdep/WindowsEthernetTap.cpp

@@ -692,17 +692,20 @@ void WindowsEthernetTap::threadMain()
 					ULONGLONG tc = GetTickCount64();
 					if ((tc - timeOfLastBorkCheck) >= 2500) {
 						timeOfLastBorkCheck = tc;
-						MIB_IF_TABLE2 *ift = NULL;
-						if ((GetIfTable2(&ift) == NO_ERROR)&&(ift)) {
+						char aabuf[16384];
+						ULONG aalen = sizeof(aabuf);
+						if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_UNICAST|GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER|GAA_FLAG_SKIP_FRIENDLY_NAME,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
 							bool isBorked = false;
-							for(ULONG r=0;r<ift->NumEntries;++r) {
-								if (ift->Table[r].InterfaceLuid.Value == _deviceLuid.Value) {
-									if ((ift->Table[r].InterfaceAndOperStatusFlags.NotMediaConnected)||(ift->Table[r].MediaConnectState == MediaConnectStateDisconnected))
-										isBorked = true;
+
+							PIP_ADAPTER_ADDRESSES aa = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
+							while (aa) {
+								if (_deviceLuid.Value == aa->Luid.Value) {
+									isBorked = (aa->OperStatus != IfOperStatusUp);
 									break;
 								}
+								aa = aa->Next;
 							}
-							FreeMibTable(ift);
+
 							if (isBorked) {
 								// Close and reopen tap device if there's an issue (outer loop)
 								break;

+ 18 - 0
selftest.cpp

@@ -193,6 +193,24 @@ static int testCrypto()
 	std::cout << "[crypto] Salsa20 SSE: DISABLED" << std::endl;
 #endif
 
+	std::cout << "[crypto] Benchmarking Salsa20/8... "; std::cout.flush();
+	{
+		unsigned char *bb = (unsigned char *)::malloc(1234567);
+		for(unsigned int i=0;i<1234567;++i)
+			bb[i] = (unsigned char)i;
+		Salsa20 s20(s20TV0Key,256,s20TV0Iv,8);
+		double bytes = 0.0;
+		uint64_t start = OSUtils::now();
+		for(unsigned int i=0;i<200;++i) {
+			s20.encrypt(bb,bb,1234567);
+			bytes += 1234567.0;
+		}
+		uint64_t end = OSUtils::now();
+		SHA512::hash(buf1,bb,1234567);
+		std::cout << ((bytes / 1048576.0) / ((double)(end - start) / 1000.0)) << " MiB/second (" << Utils::hex(buf1,16) << ')' << std::endl;
+		::free((void *)bb);
+	}
+
 	std::cout << "[crypto] Benchmarking Salsa20/12... "; std::cout.flush();
 	{
 		unsigned char *bb = (unsigned char *)::malloc(1234567);

+ 84 - 0
service/OneService.cpp

@@ -75,11 +75,17 @@ class SqliteNetworkController;
 #endif // ZT_ENABLE_NETWORK_CONTROLLER
 
 #ifdef __WINDOWS__
+#include <WinSock2.h>
+#include <Windows.h>
 #include <ShlObj.h>
+#include <netioapi.h>
+#include <iphlpapi.h>
 #else
 #include <sys/types.h>
+#include <sys/socket.h>
 #include <sys/wait.h>
 #include <unistd.h>
+#include <ifaddrs.h>
 #endif
 
 // Include the right tap device driver for this platform -- add new platforms here
@@ -123,6 +129,9 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
 // Attempt to engage TCP fallback after this many ms of no reply to packets sent to global-scope IPs
 #define ZT1_TCP_FALLBACK_AFTER 60000
 
+// How often to check for local interface addresses
+#define ZT1_LOCAL_INTERFACE_CHECK_INTERVAL 300000
+
 namespace ZeroTier {
 
 namespace {
@@ -405,6 +414,7 @@ public:
 		_nextBackgroundTaskDeadline(0),
 		_tcpFallbackTunnel((TcpConnection *)0),
 		_termReason(ONE_STILL_RUNNING),
+		_port(port),
 		_run(true)
 	{
 		struct sockaddr_in in4;
@@ -501,6 +511,7 @@ public:
 			_lastRestart = clockShouldBe;
 			uint64_t lastTapMulticastGroupCheck = 0;
 			uint64_t lastTcpFallbackResolve = 0;
+			uint64_t lastLocalInterfaceAddressCheck = 0;
 #ifdef ZT_AUTO_UPDATE
 			uint64_t lastSoftwareUpdateCheck = 0;
 #endif // ZT_AUTO_UPDATE
@@ -554,6 +565,77 @@ public:
 					}
 				}
 
+				if ((now - lastLocalInterfaceAddressCheck) >= ZT1_LOCAL_INTERFACE_CHECK_INTERVAL) {
+					lastLocalInterfaceAddressCheck = now;
+
+#ifdef __UNIX_LIKE__
+					std::vector<std::string> ztDevices;
+					{
+						Mutex::Lock _l(_taps_m);
+						for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
+							ztDevices.push_back(t->second->deviceName());
+					}
+
+					struct ifaddrs *ifatbl = (struct ifaddrs *)0;
+					if ((getifaddrs(&ifatbl) == 0)&&(ifatbl)) {
+						_node->clearLocalInterfaceAddresses();
+						struct ifaddrs *ifa = ifatbl;
+						while (ifa) {
+							if ((ifa->ifa_name)&&(ifa->ifa_addr)) {
+								bool isZT = false;
+								for(std::vector<std::string>::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) {
+									if (*d == ifa->ifa_name) {
+										isZT = true;
+										break;
+									}
+								}
+								if (!isZT) {
+									InetAddress ip(ifa->ifa_addr);
+									ip.setPort(_port);
+									_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0);
+								}
+							}
+							ifa = ifa->ifa_next;
+						}
+						freeifaddrs(ifatbl);
+					}
+#endif // __UNIX_LIKE__
+
+#ifdef __WINDOWS__
+					std::vector<NET_LUID> ztDevices;
+					{
+						Mutex::Lock _l(_taps_m);
+						for(std::map< uint64_t,EthernetTap *>::const_iterator t(_taps.begin());t!=_taps.end();++t)
+							ztDevices.push_back(t->second->luid());
+					}
+
+					char aabuf[16384];
+					ULONG aalen = sizeof(aabuf);
+					if (GetAdaptersAddresses(AF_UNSPEC,GAA_FLAG_SKIP_ANYCAST|GAA_FLAG_SKIP_MULTICAST|GAA_FLAG_SKIP_DNS_SERVER,(void *)0,reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf),&aalen) == NO_ERROR) {
+						PIP_ADAPTER_ADDRESSES a = reinterpret_cast<PIP_ADAPTER_ADDRESSES>(aabuf);
+						while (a) {
+							bool isZT = false;
+							for(std::vector<NET_LUID>::const_iterator d(ztDevices.begin());d!=ztDevices.end();++d) {
+								if (a->Luid.Value == d->Value) {
+									isZT = true;
+									break;
+								}
+							}
+							if (!isZT) {
+								PIP_ADAPTER_UNICAST_ADDRESS ua = a->FirstUnicastAddress;
+								while (ua) {
+									InetAddress ip(ua->Address.lpSockaddr);
+									ip.setPort(_port);
+									_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&ip),0,ZT1_LOCAL_INTERFACE_ADDRESS_TRUST_NORMAL,0);
+									ua = ua->Next;
+								}
+							}
+							a = a->Next;
+						}
+					}
+#endif // __WINDOWS__
+				}
+
 				const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
 				clockShouldBe = now + (uint64_t)delay;
 				_phy.poll(delay);
@@ -1158,6 +1240,8 @@ private:
 	std::string _fatalErrorMessage;
 	Mutex _termReason_m;
 
+	unsigned int _port;
+
 	bool _run;
 	Mutex _run_m;
 };

+ 1 - 0
windows/ZeroTierOne/ZeroTierOne.vcxproj

@@ -94,6 +94,7 @@
     <ClInclude Include="..\..\node\Path.hpp" />
     <ClInclude Include="..\..\node\Peer.hpp" />
     <ClInclude Include="..\..\node\Poly1305.hpp" />
+    <ClInclude Include="..\..\node\RemotePath.hpp" />
     <ClInclude Include="..\..\node\RuntimeEnvironment.hpp" />
     <ClInclude Include="..\..\node\Salsa20.hpp" />
     <ClInclude Include="..\..\node\SelfAwareness.hpp" />

+ 3 - 0
windows/ZeroTierOne/ZeroTierOne.vcxproj.filters

@@ -344,6 +344,9 @@
     <ClInclude Include="..\..\osdep\BackgroundResolver.hpp">
       <Filter>Header Files\osdep</Filter>
     </ClInclude>
+    <ClInclude Include="..\..\node\RemotePath.hpp">
+      <Filter>Header Files\node</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="ZeroTierOne.rc">