2
0
Adam Ierymenko 8 жил өмнө
parent
commit
f18158a52d

+ 26 - 109
include/ZeroTierOne.h

@@ -229,11 +229,6 @@ extern "C" {
  */
  */
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 #define ZT_RULE_PACKET_CHARACTERISTICS_TCP_FIN 0x0000000000000001ULL
 
 
-/**
- * A null/empty sockaddr (all zero) to signify an unspecified socket address
- */
-extern const struct sockaddr_storage ZT_SOCKADDR_NULL;
-
 /****************************************************************************/
 /****************************************************************************/
 /* Structures and other types                                               */
 /* Structures and other types                                               */
 /****************************************************************************/
 /****************************************************************************/
@@ -1067,21 +1062,6 @@ typedef struct
 
 
 /**
 /**
  * ZeroTier core state objects
  * ZeroTier core state objects
- *
- * All of these objects can be persisted if desired. To preserve the
- * identity of a node and its address, the identity (public and secret)
- * must be saved at a minimum.
- *
- * State objects actually have two IDs (uint64_t[2]). If only one is
- * listed the second ([1]) should be zero and is ignored in storage
- * and replication.
- *
- * All state objects should be replicated in cluster mode. The reference
- * clustering implementation uses a rumor mill algorithm in which state
- * updates that are accepted with RESULT_OK (but not RESULT_OK_IGNORED)
- * are flooded to all connected cluster peers. This results in updates
- * being flooded across the cluster until all cluster members have the
- * latest.
  */
  */
 enum ZT_StateObjectType
 enum ZT_StateObjectType
 {
 {
@@ -1108,36 +1088,6 @@ enum ZT_StateObjectType
 	 */
 	 */
 	ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
 	ZT_STATE_OBJECT_IDENTITY_SECRET = 2,
 
 
-	/**
-	 * A peer to which this node is communicating
-	 *
-	 * Object ID: peer address
-	 * Canonical path: <HOME>/peers.d/<ADDRESS> (10-digit hex address)
-	 * Persistence: optional, can be purged at any time
-	 */
-	ZT_STATE_OBJECT_PEER_STATE = 3,
-
-	/**
-	 * Network configuration
-	 *
-	 * Object ID: peer address
-	 * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID)
-	 * Persistence: required if network memberships should persist
-	 */
-	ZT_STATE_OBJECT_NETWORK_CONFIG = 4,
-
-	/**
-	 * Network membership (network X peer intersection)
-	 *
-	 * If these are persisted they must be restored after peer states and
-	 * network configs. Otherwise they are ignored.
-	 *
-	 * Object ID: [0] network ID, [1] peer address
-	 * Canonical path: <HOME>/networks.d/<NETWORKID>/members.d/<ADDRESS>
-	 * Persistence: optional (not usually needed)
-	 */
-	ZT_STATE_OBJECT_NETWORK_MEMBERSHIP = 5,
-
 	/**
 	/**
 	 * The planet (there is only one per... well... planet!)
 	 * The planet (there is only one per... well... planet!)
 	 *
 	 *
@@ -1145,7 +1095,7 @@ enum ZT_StateObjectType
 	 * Canonical path: <HOME>/planet
 	 * Canonical path: <HOME>/planet
 	 * Persistence: recommended
 	 * Persistence: recommended
 	 */
 	 */
-	ZT_STATE_OBJECT_PLANET = 6,
+	ZT_STATE_OBJECT_PLANET = 3,
 
 
 	/**
 	/**
 	 * A moon (federated root set)
 	 * A moon (federated root set)
@@ -1154,12 +1104,25 @@ enum ZT_StateObjectType
 	 * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
 	 * Canonical path: <HOME>/moons.d/<ID>.moon (16-digit hex ID)
 	 * Persistence: required if moon memberships should persist
 	 * Persistence: required if moon memberships should persist
 	 */
 	 */
-	ZT_STATE_OBJECT_MOON = 7,
+	ZT_STATE_OBJECT_MOON = 4,
 
 
 	/**
 	/**
-	 * IDs above this value will not be used by the core (and could be used as implementation-specific IDs)
+	 * Peer and related state
+	 *
+	 * Object ID: peer address
+	 * Canonical path: <HOME>/peers.d/<ID> (10-digit address
+	 * Persistence: optional, can be cleared at any time
+	 */
+	ZT_STATE_OBJECT_PEER = 5,
+
+	/**
+	 * Network configuration
+	 *
+	 * Object ID: peer address
+	 * Canonical path: <HOME>/networks.d/<NETWORKID>.conf (16-digit hex ID)
+	 * Persistence: required if network memberships should persist
 	 */
 	 */
-	ZT_STATE_OBJECT__MAX_ID = 255
+	ZT_STATE_OBJECT_NETWORK_CONFIG = 6
 };
 };
 
 
 /**
 /**
@@ -1277,17 +1240,15 @@ typedef int (*ZT_StateGetFunction)(
  * Parameters:
  * Parameters:
  *  (1) Node
  *  (1) Node
  *  (2) User pointer
  *  (2) User pointer
- *  (3) Local interface address
+ *  (3) Local socket or -1 for "all" or "any"
  *  (4) Remote address
  *  (4) Remote address
  *  (5) Packet data
  *  (5) Packet data
  *  (6) Packet length
  *  (6) Packet length
  *  (7) Desired IP TTL or 0 to use default
  *  (7) Desired IP TTL or 0 to use default
  *
  *
- * If there is only one local interface it is safe to ignore the local
- * interface address. Otherwise if running with multiple interfaces, the
- * correct local interface should be chosen by address unless NULL. If
- * the ss_family field is zero (NULL address), a random or preferred
- * default interface should be used.
+ * If there is only one local socket, the local socket can be ignored.
+ * If the local socket is -1, the packet should be sent out from all
+ * bound local sockets or a random bound local socket.
  *
  *
  * If TTL is nonzero, packets should have their IP TTL value set to this
  * If TTL is nonzero, packets should have their IP TTL value set to this
  * value if possible. If this is not possible it is acceptable to ignore
  * value if possible. If this is not possible it is acceptable to ignore
@@ -1301,7 +1262,7 @@ typedef int (*ZT_WirePacketSendFunction)(
 	ZT_Node *,                        /* Node */
 	ZT_Node *,                        /* Node */
 	void *,                           /* User ptr */
 	void *,                           /* User ptr */
 	void *,                           /* Thread ptr */
 	void *,                           /* Thread ptr */
-	const struct sockaddr_storage *,  /* Local address */
+	int64_t,                          /* Local socket */
 	const struct sockaddr_storage *,  /* Remote address */
 	const struct sockaddr_storage *,  /* Remote address */
 	const void *,                     /* Packet data */
 	const void *,                     /* Packet data */
 	unsigned int,                     /* Packet length */
 	unsigned int,                     /* Packet length */
@@ -1314,7 +1275,7 @@ typedef int (*ZT_WirePacketSendFunction)(
  *  (1) Node
  *  (1) Node
  *  (2) User pointer
  *  (2) User pointer
  *  (3) ZeroTier address or 0 for none/any
  *  (3) ZeroTier address or 0 for none/any
- *  (4) Local interface address
+ *  (4) Local socket or -1 if unknown
  *  (5) Remote address
  *  (5) Remote address
  *
  *
  * This function must return nonzero (true) if the path should be used.
  * This function must return nonzero (true) if the path should be used.
@@ -1333,7 +1294,7 @@ typedef int (*ZT_PathCheckFunction)(
 	void *,                           /* User ptr */
 	void *,                           /* User ptr */
 	void *,                           /* Thread ptr */
 	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address */
 	uint64_t,                         /* ZeroTier address */
-	const struct sockaddr_storage *,  /* Local address */
+	int64_t,                          /* Local socket or -1 if unknown */
 	const struct sockaddr_storage *); /* Remote address */
 	const struct sockaddr_storage *); /* Remote address */
 
 
 /**
 /**
@@ -1441,57 +1402,13 @@ enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct
  */
  */
 void ZT_Node_delete(ZT_Node *node);
 void ZT_Node_delete(ZT_Node *node);
 
 
-/**
- * Notify node of an update to a state object
- *
- * This can be called after node startup to restore cached state objects such
- * as network configurations for joined networks, planet, moons, etc. See
- * the documentation of ZT_StateObjectType for more information. It's okay
- * to call this for everything in the object store, but note that the node
- * will automatically query for some core objects like identities so supplying
- * these via this function is not necessary.
- *
- * Unless clustering is being implemented this function doesn't need to be
- * used after startup. It could be called in response to filesystem changes
- * to allow some degree of live configurability by filesystem observation
- * but this kind of thing is entirely optional.
- *
- * The return value of this function indicates whether the update was accepted
- * as new. A return value of ZT_RESULT_OK indicates that the node gleaned new
- * information from this update and that therefore (in cluster rumor mill mode)
- * this update should be distributed to other members of a cluster. A return
- * value of ZT_RESULT_OK_IGNORED indicates that the object did not provide any
- * new information and therefore should not be propagated in a cluster.
- *
- * If clustering isn't being implemented the return value of this function can
- * generally be ignored.
- *
- * ZT_RESULT_ERROR_BAD_PARAMETER can be returned if the parameter was invalid
- * or not applicable. Object stores may delete the object in this case.
- *
- * @param node Node instance
- * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
- * @param type State object type
- * @param id State object ID (if object type has only one ID, second should be zero)
- * @param data State object data
- * @param len Length of state object data in bytes
- * @return ZT_RESULT_OK if object was accepted or ZT_RESULT_OK_IGNORED if non-informative, error if object was invalid
- */
-enum ZT_ResultCode ZT_Node_processStateUpdate(
-	ZT_Node *node,
-	void *tptr,
-	ZT_StateObjectType type,
-	const uint64_t id[2],
-	const void *data,
-	unsigned int len);
-
 /**
 /**
  * Process a packet received from the physical wire
  * Process a packet received from the physical wire
  *
  *
  * @param node Node instance
  * @param node Node instance
  * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param now Current clock in milliseconds
  * @param now Current clock in milliseconds
- * @param localAddress Local address, or point to ZT_SOCKADDR_NULL if unspecified
+ * @param localSocket Local socket (you can use 0 if only one local socket is bound and ignore this)
  * @param remoteAddress Origin of packet
  * @param remoteAddress Origin of packet
  * @param packetData Packet data
  * @param packetData Packet data
  * @param packetLength Packet length
  * @param packetLength Packet length
@@ -1502,7 +1419,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
 	ZT_Node *node,
 	ZT_Node *node,
 	void *tptr,
 	void *tptr,
 	uint64_t now,
 	uint64_t now,
-	const struct sockaddr_storage *localAddress,
+	int64_t localSocket,
 	const struct sockaddr_storage *remoteAddress,
 	const struct sockaddr_storage *remoteAddress,
 	const void *packetData,
 	const void *packetData,
 	unsigned int packetLength,
 	unsigned int packetLength,

+ 7 - 7
node/IncomingPacket.cpp

@@ -309,7 +309,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool
 		if (ptr < size()) {
 		if (ptr < size()) {
 			ptr += externalSurfaceAddress.deserialize(*this,ptr);
 			ptr += externalSurfaceAddress.deserialize(*this,ptr);
 			if ((externalSurfaceAddress)&&(hops() == 0))
 			if ((externalSurfaceAddress)&&(hops() == 0))
-				RR->sa->iam(tPtr,id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+				RR->sa->iam(tPtr,id.address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
 		}
 		}
 
 
 		// Get primary planet world ID and world timestamp if present
 		// Get primary planet world ID and world timestamp if present
@@ -495,7 +495,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedP
 				peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 				peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 
 
 				if ((externalSurfaceAddress)&&(hops() == 0))
 				if ((externalSurfaceAddress)&&(hops() == 0))
-					RR->sa->iam(tPtr,peer->address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
+					RR->sa->iam(tPtr,peer->address(),_path->localSocket(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
 			}	break;
 			}	break;
 
 
 			case Packet::VERB_WHOIS:
 			case Packet::VERB_WHOIS:
@@ -613,9 +613,9 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const
 				const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 				const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 				if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
 				if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
 					const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
 					const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
-					if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localAddress(),atAddr)) {
-						RR->node->putPacket(tPtr,_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
-						rendezvousWith->attemptToContactAt(tPtr,_path->localAddress(),atAddr,RR->node->now(),false,0);
+					if (RR->node->shouldUsePathForZeroTierTraffic(tPtr,with,_path->localSocket(),atAddr)) {
+						RR->node->putPacket(tPtr,_path->localSocket(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
+						rendezvousWith->attemptToContactAt(tPtr,_path->localSocket(),atAddr,RR->node->now(),false,0);
 						TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
 						TRACE("RENDEZVOUS from %s says %s might be at %s, sent verification attempt",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
 					} else {
 					} else {
 						TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
 						TRACE("RENDEZVOUS from %s says %s might be at %s, ignoring since path is not suitable",peer->address().toString().c_str(),with.toString().c_str(),atAddr.toString().c_str());
@@ -1197,7 +1197,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 					if (
 					if (
 					    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 					    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 							(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
 							(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
-							(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) // should use path
+							(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
 					{
 					{
 						//if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
 						//if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
 						//	peer->setClusterPreferred(a);
 						//	peer->setClusterPreferred(a);
@@ -1214,7 +1214,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPt
 					if (
 					if (
 					    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 					    ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && // not being told to forget
 							(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
 							(!( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) == 0) && (peer->hasActivePathTo(now,a)) )) && // not already known
-							(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) // should use path
+							(RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localSocket(),a)) ) // should use path
 					{
 					{
 						//if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
 						//if ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_CLUSTER_REDIRECT) != 0)
 						//	peer->setClusterPreferred(a);
 						//	peer->setClusterPreferred(a);

+ 7 - 123
node/Node.cpp

@@ -47,8 +47,6 @@
 #include "SelfAwareness.hpp"
 #include "SelfAwareness.hpp"
 #include "Network.hpp"
 #include "Network.hpp"
 
 
-const struct sockaddr_storage ZT_SOCKADDR_NULL = {0};
-
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 /****************************************************************************/
 /****************************************************************************/
@@ -137,114 +135,17 @@ Node::~Node()
 	delete RR->sw;
 	delete RR->sw;
 }
 }
 
 
-ZT_ResultCode Node::processStateUpdate(
-	void *tptr,
-	ZT_StateObjectType type,
-	const uint64_t id[2],
-	const void *data,
-	unsigned int len)
-{
-	ZT_ResultCode r = ZT_RESULT_OK_IGNORED;
-	switch(type) {
-
-		case ZT_STATE_OBJECT_PEER_STATE:
-			if (len) {
-				const SharedPtr<Peer> p(RR->topology->getPeer(tptr,Address(id[0])));
-				if (p) {
-					r = (p->applyStateUpdate(data,len)) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED;
-				} else {
-					r = (Peer::createFromStateUpdate(RR,tptr,data,len)) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED;
-				}
-			}
-			break;
-
-		case ZT_STATE_OBJECT_NETWORK_CONFIG:
-			if (len <= (ZT_NETWORKCONFIG_DICT_CAPACITY - 1)) {
-				if (len < 2) {
-					Mutex::Lock _l(_networks_m);
-					SharedPtr<Network> &nw = _networks[id[0]];
-					if (!nw) {
-						nw = SharedPtr<Network>(new Network(RR,tptr,id[0],(void *)0,(const NetworkConfig *)0));
-						r = ZT_RESULT_OK;
-					}
-				} else {
-					Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dict = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>(reinterpret_cast<const char *>(data),len);
-					try {
-						NetworkConfig *nconf = new NetworkConfig();
-						try {
-							if (nconf->fromDictionary(*dict)) {
-								Mutex::Lock _l(_networks_m);
-								SharedPtr<Network> &nw = _networks[id[0]];
-								if (nw) {
-									switch (nw->setConfiguration(tptr,*nconf,false)) {
-										default:
-											r = ZT_RESULT_ERROR_BAD_PARAMETER;
-											break;
-										case 1:
-											r = ZT_RESULT_OK_IGNORED;
-											break;
-										case 2:
-											r = ZT_RESULT_OK;
-											break;
-									}
-								} else {
-									nw = SharedPtr<Network>(new Network(RR,tptr,id[0],(void *)0,nconf));
-								}
-							} else {
-								r = ZT_RESULT_ERROR_BAD_PARAMETER;
-							}
-						} catch ( ... ) {
-							r = ZT_RESULT_ERROR_BAD_PARAMETER;
-						}
-						delete nconf;
-					} catch ( ... ) {
-						r = ZT_RESULT_ERROR_BAD_PARAMETER;
-					}
-					delete dict;
-				}
-			} else {
-				r = ZT_RESULT_ERROR_BAD_PARAMETER;
-			}
-			break;
-
-		case ZT_STATE_OBJECT_NETWORK_MEMBERSHIP:
-			if (len) {
-			}
-			break;
-
-		case ZT_STATE_OBJECT_PLANET:
-		case ZT_STATE_OBJECT_MOON:
-			if ((len)&&(len <= ZT_WORLD_MAX_SERIALIZED_LENGTH)) {
-				World w;
-				try {
-					w.deserialize(Buffer<ZT_WORLD_MAX_SERIALIZED_LENGTH>(data,len));
-					if (( (w.type() == World::TYPE_MOON)&&(type == ZT_STATE_OBJECT_MOON) )||( (w.type() == World::TYPE_PLANET)&&(type == ZT_STATE_OBJECT_PLANET) )) {
-						r = (RR->topology->addWorld(tptr,w,false)) ? ZT_RESULT_OK : ZT_RESULT_OK_IGNORED;
-					}
-				} catch ( ... ) {
-					r = ZT_RESULT_ERROR_BAD_PARAMETER;
-				}
-			} else {
-				r = ZT_RESULT_ERROR_BAD_PARAMETER;
-			}
-			break;
-
-		default: break;
-	}
-	return r;
-}
-
 ZT_ResultCode Node::processWirePacket(
 ZT_ResultCode Node::processWirePacket(
 	void *tptr,
 	void *tptr,
 	uint64_t now,
 	uint64_t now,
-	const struct sockaddr_storage *localAddress,
+	int64_t localSocket,
 	const struct sockaddr_storage *remoteAddress,
 	const struct sockaddr_storage *remoteAddress,
 	const void *packetData,
 	const void *packetData,
 	unsigned int packetLength,
 	unsigned int packetLength,
 	volatile uint64_t *nextBackgroundTaskDeadline)
 	volatile uint64_t *nextBackgroundTaskDeadline)
 {
 {
 	_now = now;
 	_now = now;
-	RR->sw->onRemotePacket(tptr,*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
+	RR->sw->onRemotePacket(tptr,localSocket,*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
 	return ZT_RESULT_OK;
 	return ZT_RESULT_OK;
 }
 }
 
 
@@ -317,7 +218,7 @@ public:
 			if ((!contacted)&&(_bestCurrentUpstream)) {
 			if ((!contacted)&&(_bestCurrentUpstream)) {
 				const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
 				const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
 				if (up)
 				if (up)
-					p->sendHELLO(_tPtr,up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
+					p->sendHELLO(_tPtr,up->localSocket(),up->address(),_now,up->nextOutgoingCounter());
 			}
 			}
 
 
 			lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
 			lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
@@ -617,7 +518,7 @@ void Node::setNetconfMaster(void *networkControllerInstance)
 /* Node methods used only within node/                                      */
 /* Node methods used only within node/                                      */
 /****************************************************************************/
 /****************************************************************************/
 
 
-bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
+bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress)
 {
 {
 	if (!Path::isAddressValidForPath(remoteAddress))
 	if (!Path::isAddressValidForPath(remoteAddress))
 		return false;
 		return false;
@@ -640,7 +541,7 @@ bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,cons
 		}
 		}
 	}
 	}
 
 
-	return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
+	return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),localSocket,reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
 }
 }
 
 
 #ifdef ZT_TRACE
 #ifdef ZT_TRACE
@@ -837,35 +738,18 @@ void ZT_Node_delete(ZT_Node *node)
 	} catch ( ... ) {}
 	} catch ( ... ) {}
 }
 }
 
 
-enum ZT_ResultCode ZT_Node_processStateUpdate(
-	ZT_Node *node,
-	void *tptr,
-	ZT_StateObjectType type,
-	const uint64_t id[2],
-	const void *data,
-	unsigned int len)
-{
-	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->processStateUpdate(tptr,type,id,data,len);
-	} catch (std::bad_alloc &exc) {
-		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
-	} catch ( ... ) {
-		return ZT_RESULT_FATAL_ERROR_INTERNAL;
-	}
-}
-
 enum ZT_ResultCode ZT_Node_processWirePacket(
 enum ZT_ResultCode ZT_Node_processWirePacket(
 	ZT_Node *node,
 	ZT_Node *node,
 	void *tptr,
 	void *tptr,
 	uint64_t now,
 	uint64_t now,
-	const struct sockaddr_storage *localAddress,
+	int64_t localSocket,
 	const struct sockaddr_storage *remoteAddress,
 	const struct sockaddr_storage *remoteAddress,
 	const void *packetData,
 	const void *packetData,
 	unsigned int packetLength,
 	unsigned int packetLength,
 	volatile uint64_t *nextBackgroundTaskDeadline)
 	volatile uint64_t *nextBackgroundTaskDeadline)
 {
 {
 	try {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
+		return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localSocket,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
 	} catch (std::bad_alloc &exc) {
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
 	} catch ( ... ) {

+ 4 - 10
node/Node.hpp

@@ -82,16 +82,10 @@ public:
 
 
 	// Public API Functions ----------------------------------------------------
 	// Public API Functions ----------------------------------------------------
 
 
-	ZT_ResultCode processStateUpdate(
-		void *tptr,
-		ZT_StateObjectType type,
-		const uint64_t id[2],
-		const void *data,
-		unsigned int len);
 	ZT_ResultCode processWirePacket(
 	ZT_ResultCode processWirePacket(
 		void *tptr,
 		void *tptr,
 		uint64_t now,
 		uint64_t now,
-		const struct sockaddr_storage *localAddress,
+		int64_t localSocket,
 		const struct sockaddr_storage *remoteAddress,
 		const struct sockaddr_storage *remoteAddress,
 		const void *packetData,
 		const void *packetData,
 		unsigned int packetLength,
 		unsigned int packetLength,
@@ -129,13 +123,13 @@ public:
 
 
 	inline uint64_t now() const throw() { return _now; }
 	inline uint64_t now() const throw() { return _now; }
 
 
-	inline bool putPacket(void *tPtr,const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+	inline bool putPacket(void *tPtr,const int64_t localSocket,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
 	{
 	{
 		return (_cb.wirePacketSendFunction(
 		return (_cb.wirePacketSendFunction(
 			reinterpret_cast<ZT_Node *>(this),
 			reinterpret_cast<ZT_Node *>(this),
 			_uPtr,
 			_uPtr,
 			tPtr,
 			tPtr,
-			reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
+			localSocket,
 			reinterpret_cast<const struct sockaddr_storage *>(&addr),
 			reinterpret_cast<const struct sockaddr_storage *>(&addr),
 			data,
 			data,
 			len,
 			len,
@@ -205,7 +199,7 @@ public:
 	void postTrace(const char *module,unsigned int line,const char *fmt,...);
 	void postTrace(const char *module,unsigned int line,const char *fmt,...);
 #endif
 #endif
 
 
-	bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
+	bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const int64_t localSocket,const InetAddress &remoteAddress);
 	inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
 	inline bool externalPathLookup(void *tPtr,const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
 
 
 	uint64_t prng();
 	uint64_t prng();

+ 1 - 1
node/Path.cpp

@@ -32,7 +32,7 @@ namespace ZeroTier {
 
 
 bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,uint64_t now)
 bool Path::send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,uint64_t now)
 {
 {
-	if (RR->node->putPacket(tPtr,_localAddress,address(),data,len)) {
+	if (RR->node->putPacket(tPtr,_localSocket,_addr,data,len)) {
 		_lastOut = now;
 		_lastOut = now;
 		return true;
 		return true;
 	}
 	}

+ 15 - 36
node/Path.hpp

@@ -66,49 +66,28 @@ public:
 	public:
 	public:
 		HashKey() {}
 		HashKey() {}
 
 
-		HashKey(const InetAddress &l,const InetAddress &r)
+		HashKey(const int64_t l,const InetAddress &r)
 		{
 		{
-			// This is an ad-hoc bit packing algorithm to yield unique keys for
-			// remote addresses and their local-side counterparts if defined.
-			// Portability across runtimes is not needed.
 			if (r.ss_family == AF_INET) {
 			if (r.ss_family == AF_INET) {
 				_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
 				_k[0] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_addr.s_addr;
 				_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
 				_k[1] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
-				if (l.ss_family == AF_INET) {
-					_k[2] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&l)->sin_addr.s_addr;
-					_k[3] = (uint64_t)reinterpret_cast<const struct sockaddr_in *>(&r)->sin_port;
-				} else {
-					_k[2] = 0;
-					_k[3] = 0;
-				}
+				_k[2] = (uint64_t)l;
 			} else if (r.ss_family == AF_INET6) {
 			} else if (r.ss_family == AF_INET6) {
-				const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
-				uint8_t *b = reinterpret_cast<uint8_t *>(_k);
-				for(unsigned int i=0;i<16;++i) b[i] = a[i];
-				_k[2] = ~((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port);
-				if (l.ss_family == AF_INET6) {
-					_k[2] ^= ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port) << 32;
-					a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const struct sockaddr_in6 *>(&l)->sin6_addr.s6_addr);
-					b += 24;
-					for(unsigned int i=0;i<8;++i) b[i] = a[i];
-					a += 8;
-					for(unsigned int i=0;i<8;++i) b[i] ^= a[i];
-				}
+				memcpy(_k,reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_addr.s6_addr,16);
+				_k[2] = ((uint64_t)reinterpret_cast<const struct sockaddr_in6 *>(&r)->sin6_port << 32) ^ (uint64_t)l;
 			} else {
 			} else {
-				_k[0] = 0;
-				_k[1] = 0;
-				_k[2] = 0;
-				_k[3] = 0;
+				memcpy(_k,&r,std::min(sizeof(_k),sizeof(InetAddress)));
+				_k[2] += (uint64_t)l;
 			}
 			}
 		}
 		}
 
 
-		inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2] + _k[3]); }
+		inline unsigned long hashCode() const { return (unsigned long)(_k[0] + _k[1] + _k[2]); }
 
 
-		inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) && (_k[3] == k._k[3]) ); }
+		inline bool operator==(const HashKey &k) const { return ( (_k[0] == k._k[0]) && (_k[1] == k._k[1]) && (_k[2] == k._k[2]) ); }
 		inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
 		inline bool operator!=(const HashKey &k) const { return (!(*this == k)); }
 
 
 	private:
 	private:
-		uint64_t _k[4];
+		uint64_t _k[3];
 	};
 	};
 
 
 	Path() :
 	Path() :
@@ -116,29 +95,29 @@ public:
 		_lastIn(0),
 		_lastIn(0),
 		_lastTrustEstablishedPacketReceived(0),
 		_lastTrustEstablishedPacketReceived(0),
 		_incomingLinkQualityFastLog(0xffffffffffffffffULL),
 		_incomingLinkQualityFastLog(0xffffffffffffffffULL),
+		_localSocket(-1),
 		_incomingLinkQualitySlowLogPtr(0),
 		_incomingLinkQualitySlowLogPtr(0),
 		_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
 		_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
 		_incomingLinkQualityPreviousPacketCounter(0),
 		_incomingLinkQualityPreviousPacketCounter(0),
 		_outgoingPacketCounter(0),
 		_outgoingPacketCounter(0),
 		_addr(),
 		_addr(),
-		_localAddress(),
 		_ipScope(InetAddress::IP_SCOPE_NONE)
 		_ipScope(InetAddress::IP_SCOPE_NONE)
 	{
 	{
 		for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
 		for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
 			_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
 			_incomingLinkQualitySlowLog[i] = ZT_PATH_LINK_QUALITY_MAX;
 	}
 	}
 
 
-	Path(const InetAddress &localAddress,const InetAddress &addr) :
+	Path(const int64_t localSocket,const InetAddress &addr) :
 		_lastOut(0),
 		_lastOut(0),
 		_lastIn(0),
 		_lastIn(0),
 		_lastTrustEstablishedPacketReceived(0),
 		_lastTrustEstablishedPacketReceived(0),
 		_incomingLinkQualityFastLog(0xffffffffffffffffULL),
 		_incomingLinkQualityFastLog(0xffffffffffffffffULL),
+		_localSocket(localSocket),
 		_incomingLinkQualitySlowLogPtr(0),
 		_incomingLinkQualitySlowLogPtr(0),
 		_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
 		_incomingLinkQualitySlowLogCounter(-64), // discard first fast log
 		_incomingLinkQualityPreviousPacketCounter(0),
 		_incomingLinkQualityPreviousPacketCounter(0),
 		_outgoingPacketCounter(0),
 		_outgoingPacketCounter(0),
 		_addr(addr),
 		_addr(addr),
-		_localAddress(localAddress),
 		_ipScope(addr.ipScope())
 		_ipScope(addr.ipScope())
 	{
 	{
 		for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
 		for(int i=0;i<(int)sizeof(_incomingLinkQualitySlowLog);++i)
@@ -210,9 +189,9 @@ public:
 	inline void sent(const uint64_t t) { _lastOut = t; }
 	inline void sent(const uint64_t t) { _lastOut = t; }
 
 
 	/**
 	/**
-	 * @return Address of local side of this path or NULL if unspecified
+	 * @return Local socket as specified by external code
 	 */
 	 */
-	inline const InetAddress &localAddress() const { return _localAddress; }
+	inline const int64_t localSocket() const { return _localSocket; }
 
 
 	/**
 	/**
 	 * @return Physical address
 	 * @return Physical address
@@ -328,12 +307,12 @@ private:
 	volatile uint64_t _lastIn;
 	volatile uint64_t _lastIn;
 	volatile uint64_t _lastTrustEstablishedPacketReceived;
 	volatile uint64_t _lastTrustEstablishedPacketReceived;
 	volatile uint64_t _incomingLinkQualityFastLog;
 	volatile uint64_t _incomingLinkQualityFastLog;
+	int64_t _localSocket;
 	volatile unsigned long _incomingLinkQualitySlowLogPtr;
 	volatile unsigned long _incomingLinkQualitySlowLogPtr;
 	volatile signed int _incomingLinkQualitySlowLogCounter;
 	volatile signed int _incomingLinkQualitySlowLogCounter;
 	volatile unsigned int _incomingLinkQualityPreviousPacketCounter;
 	volatile unsigned int _incomingLinkQualityPreviousPacketCounter;
 	volatile unsigned int _outgoingPacketCounter;
 	volatile unsigned int _outgoingPacketCounter;
 	InetAddress _addr;
 	InetAddress _addr;
-	InetAddress _localAddress;
 	InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
 	InetAddress::IpScope _ipScope; // memoize this since it's a computed value checked often
 	volatile uint8_t _incomingLinkQualitySlowLog[32];
 	volatile uint8_t _incomingLinkQualitySlowLog[32];
 	AtomicCounter __refCount;
 	AtomicCounter __refCount;

+ 25 - 34
node/Peer.cpp

@@ -154,25 +154,21 @@ void Peer::received(
 			if ((path->address().ss_family == AF_INET)&&(_v4Path.p)) {
 			if ((path->address().ss_family == AF_INET)&&(_v4Path.p)) {
 				const struct sockaddr_in *const r = reinterpret_cast<const struct sockaddr_in *>(&(path->address()));
 				const struct sockaddr_in *const r = reinterpret_cast<const struct sockaddr_in *>(&(path->address()));
 				const struct sockaddr_in *const l = reinterpret_cast<const struct sockaddr_in *>(&(_v4Path.p->address()));
 				const struct sockaddr_in *const l = reinterpret_cast<const struct sockaddr_in *>(&(_v4Path.p->address()));
-				const struct sockaddr_in *const rl = reinterpret_cast<const struct sockaddr_in *>(&(path->localAddress()));
-				const struct sockaddr_in *const ll = reinterpret_cast<const struct sockaddr_in *>(&(_v4Path.p->localAddress()));
-				if ((r->sin_addr.s_addr == l->sin_addr.s_addr)&&(r->sin_port == l->sin_port)&&(rl->sin_addr.s_addr == ll->sin_addr.s_addr)&&(rl->sin_port == ll->sin_port)) {
+				if ((r->sin_addr.s_addr == l->sin_addr.s_addr)&&(r->sin_port == l->sin_port)&&(path->localSocket() == _v4Path.p->localSocket())) {
 					_v4Path.lr = now;
 					_v4Path.lr = now;
 					pathAlreadyKnown = true;
 					pathAlreadyKnown = true;
 				}
 				}
 			} else if ((path->address().ss_family == AF_INET6)&&(_v6Path.p)) {
 			} else if ((path->address().ss_family == AF_INET6)&&(_v6Path.p)) {
 				const struct sockaddr_in6 *const r = reinterpret_cast<const struct sockaddr_in6 *>(&(path->address()));
 				const struct sockaddr_in6 *const r = reinterpret_cast<const struct sockaddr_in6 *>(&(path->address()));
 				const struct sockaddr_in6 *const l = reinterpret_cast<const struct sockaddr_in6 *>(&(_v6Path.p->address()));
 				const struct sockaddr_in6 *const l = reinterpret_cast<const struct sockaddr_in6 *>(&(_v6Path.p->address()));
-				const struct sockaddr_in6 *const rl = reinterpret_cast<const struct sockaddr_in6 *>(&(path->localAddress()));
-				const struct sockaddr_in6 *const ll = reinterpret_cast<const struct sockaddr_in6 *>(&(_v6Path.p->localAddress()));
-				if ((!memcmp(r->sin6_addr.s6_addr,l->sin6_addr.s6_addr,16))&&(r->sin6_port == l->sin6_port)&&(!memcmp(rl->sin6_addr.s6_addr,ll->sin6_addr.s6_addr,16))&&(rl->sin6_port == ll->sin6_port)) {
+				if ((!memcmp(r->sin6_addr.s6_addr,l->sin6_addr.s6_addr,16))&&(r->sin6_port == l->sin6_port)&&(path->localSocket() == _v6Path.p->localSocket())) {
 					_v6Path.lr = now;
 					_v6Path.lr = now;
 					pathAlreadyKnown = true;
 					pathAlreadyKnown = true;
 				}
 				}
 			}
 			}
 		}
 		}
 
 
-		if ( (!pathAlreadyKnown) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localAddress(),path->address())) ) {
+		if ( (!pathAlreadyKnown) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localSocket(),path->address())) ) {
 			Mutex::Lock _l(_paths_m);
 			Mutex::Lock _l(_paths_m);
 			_PeerPath *potentialNewPeerPath = (_PeerPath *)0;
 			_PeerPath *potentialNewPeerPath = (_PeerPath *)0;
 			if (path->address().ss_family == AF_INET) {
 			if (path->address().ss_family == AF_INET) {
@@ -191,7 +187,7 @@ void Peer::received(
 					_lastWroteState = 0; // force state write now
 					_lastWroteState = 0; // force state write now
 				} else {
 				} else {
 					TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
 					TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
-					attemptToContactAt(tPtr,path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
+					attemptToContactAt(tPtr,path->localSocket(),path->address(),now,true,path->nextOutgoingCounter());
 					path->sent(now);
 					path->sent(now);
 				}
 				}
 			}
 			}
@@ -318,7 +314,7 @@ SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
 	return SharedPtr<Path>();
 	return SharedPtr<Path>();
 }
 }
 
 
-void Peer::sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
+void Peer::sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,uint64_t now,unsigned int counter)
 {
 {
 	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
 	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
 
 
@@ -360,21 +356,21 @@ void Peer::sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &
 
 
 	if (atAddress) {
 	if (atAddress) {
 		outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
 		outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
-		RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
+		RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
 	} else {
 	} else {
 		RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
 		RR->sw->send(tPtr,outp,false); // false == don't encrypt full payload, but add MAC
 	}
 	}
 }
 }
 
 
-void Peer::attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
+void Peer::attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
 {
 {
 	if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
 	if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
 		Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
 		Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
 		RR->node->expectReplyTo(outp.packetId());
 		RR->node->expectReplyTo(outp.packetId());
 		outp.armor(_key,true,counter);
 		outp.armor(_key,true,counter);
-		RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
+		RR->node->putPacket(tPtr,localSocket,atAddress,outp.data(),outp.size());
 	} else {
 	} else {
-		sendHELLO(tPtr,localAddr,atAddress,now,counter);
+		sendHELLO(tPtr,localSocket,atAddress,now,counter);
 	}
 	}
 }
 }
 
 
@@ -402,13 +398,13 @@ bool Peer::doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily)
 
 
 		if (v6lr > v4lr) {
 		if (v6lr > v4lr) {
 			if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
 			if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
-				attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
+				attemptToContactAt(tPtr,_v6Path.p->localSocket(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
 				_v6Path.p->sent(now);
 				_v6Path.p->sent(now);
 				return true;
 				return true;
 			}
 			}
 		} else if (v4lr) {
 		} else if (v4lr) {
 			if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
 			if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
-				attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
+				attemptToContactAt(tPtr,_v4Path.p->localSocket(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
 				_v4Path.p->sent(now);
 				_v4Path.p->sent(now);
 				return true;
 				return true;
 			}
 			}
@@ -416,13 +412,13 @@ bool Peer::doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily)
 	} else {
 	} else {
 		if ( (inetAddressFamily == AF_INET) && ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
 		if ( (inetAddressFamily == AF_INET) && ((now - _v4Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
 			if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
 			if ( ((now - _v4Path.lr) >= ZT_PEER_PING_PERIOD) || (_v4Path.p->needsHeartbeat(now)) ) {
-				attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
+				attemptToContactAt(tPtr,_v4Path.p->localSocket(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
 				_v4Path.p->sent(now);
 				_v4Path.p->sent(now);
 				return true;
 				return true;
 			}
 			}
 		} else if ( (inetAddressFamily == AF_INET6) && ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
 		} else if ( (inetAddressFamily == AF_INET6) && ((now - _v6Path.lr) < ZT_PEER_PATH_EXPIRATION) ) {
 			if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
 			if ( ((now - _v6Path.lr) >= ZT_PEER_PING_PERIOD) || (_v6Path.p->needsHeartbeat(now)) ) {
-				attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
+				attemptToContactAt(tPtr,_v6Path.p->localSocket(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
 				_v6Path.p->sent(now);
 				_v6Path.p->sent(now);
 				return true;
 				return true;
 			}
 			}
@@ -456,7 +452,6 @@ void Peer::writeState(void *tPtr,const uint64_t now)
 				b.append(_v4Path.p->lastIn());
 				b.append(_v4Path.p->lastIn());
 				b.append(_v4Path.p->lastTrustEstablishedPacketReceived());
 				b.append(_v4Path.p->lastTrustEstablishedPacketReceived());
 				_v4Path.p->address().serialize(b);
 				_v4Path.p->address().serialize(b);
-				_v4Path.p->localAddress().serialize(b);
 			}
 			}
 			if (_v6Path.lr) {
 			if (_v6Path.lr) {
 				b.append(_v6Path.lr);
 				b.append(_v6Path.lr);
@@ -464,7 +459,6 @@ void Peer::writeState(void *tPtr,const uint64_t now)
 				b.append(_v6Path.p->lastIn());
 				b.append(_v6Path.p->lastIn());
 				b.append(_v6Path.p->lastTrustEstablishedPacketReceived());
 				b.append(_v6Path.p->lastTrustEstablishedPacketReceived());
 				_v6Path.p->address().serialize(b);
 				_v6Path.p->address().serialize(b);
-				_v6Path.p->localAddress().serialize(b);
 			}
 			}
 		}
 		}
 
 
@@ -491,7 +485,7 @@ void Peer::writeState(void *tPtr,const uint64_t now)
 
 
 		uint64_t tmp[2];
 		uint64_t tmp[2];
 		tmp[0] = _id.address().toInt(); tmp[1] = 0;
 		tmp[0] = _id.address().toInt(); tmp[1] = 0;
-		RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER_STATE,tmp,b.data(),b.size());
+		//RR->node->stateObjectPut(tPtr,ZT_STATE_OBJECT_PEER_STATE,tmp,b.data(),b.size());
 
 
 		_lastWroteState = now;
 		_lastWroteState = now;
 	} catch ( ... ) {} // sanity check, should not be possible
 	} catch ( ... ) {} // sanity check, should not be possible
@@ -522,22 +516,19 @@ bool Peer::applyStateUpdate(const void *data,unsigned int len)
 				const uint64_t lastOut = b.at<uint64_t>(ptr); ptr += 8;
 				const uint64_t lastOut = b.at<uint64_t>(ptr); ptr += 8;
 				const uint64_t lastIn = b.at<uint64_t>(ptr); ptr += 8;
 				const uint64_t lastIn = b.at<uint64_t>(ptr); ptr += 8;
 				const uint64_t lastTrustEstablishedPacketReceived = b.at<uint64_t>(ptr); ptr += 8;
 				const uint64_t lastTrustEstablishedPacketReceived = b.at<uint64_t>(ptr); ptr += 8;
-				InetAddress addr,localAddr;
+				InetAddress addr;
 				ptr += addr.deserialize(b,ptr);
 				ptr += addr.deserialize(b,ptr);
-				ptr += localAddr.deserialize(b,ptr);
-				if (addr.ss_family == localAddr.ss_family) {
-					_PeerPath *p = (_PeerPath *)0;
-					switch(addr.ss_family) {
-						case AF_INET: p = &_v4Path; break;
-						case AF_INET6: p = &_v6Path; break;
-					}
-					if (p) {
-						if ( (!p->p) || ((p->p->address() != addr)||(p->p->localAddress() != localAddr)) ) {
-							p->p = RR->topology->getPath(localAddr,addr);
-						}
-						p->lr = lr;
-						p->p->updateFromRemoteState(lastOut,lastIn,lastTrustEstablishedPacketReceived);
+				_PeerPath *p = (_PeerPath *)0;
+				switch(addr.ss_family) {
+					case AF_INET: p = &_v4Path; break;
+					case AF_INET6: p = &_v6Path; break;
+				}
+				if (p) {
+					if ( (!p->p) || (p->p->address() != addr) ) {
+						p->p = RR->topology->getPath(-1,addr);
 					}
 					}
+					p->lr = lr;
+					p->p->updateFromRemoteState(lastOut,lastIn,lastTrustEstablishedPacketReceived);
 				}
 				}
 			}
 			}
 		}
 		}

+ 6 - 6
node/Peer.hpp

@@ -154,12 +154,12 @@ public:
 	 * No statistics or sent times are updated here.
 	 * No statistics or sent times are updated here.
 	 *
 	 *
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @param localAddr Local address
+	 * @param localSocket Local source socket
 	 * @param atAddress Destination address
 	 * @param atAddress Destination address
 	 * @param now Current time
 	 * @param now Current time
 	 * @param counter Outgoing packet counter
 	 * @param counter Outgoing packet counter
 	 */
 	 */
-	void sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
+	void sendHELLO(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,uint64_t now,unsigned int counter);
 
 
 	/**
 	/**
 	 * Send ECHO (or HELLO for older peers) to this peer at the given address
 	 * Send ECHO (or HELLO for older peers) to this peer at the given address
@@ -167,13 +167,13 @@ public:
 	 * No statistics or sent times are updated here.
 	 * No statistics or sent times are updated here.
 	 *
 	 *
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @param localAddr Local address
+	 * @param localSocket Local source socket
 	 * @param atAddress Destination address
 	 * @param atAddress Destination address
 	 * @param now Current time
 	 * @param now Current time
 	 * @param sendFullHello If true, always send a full HELLO instead of just an ECHO
 	 * @param sendFullHello If true, always send a full HELLO instead of just an ECHO
 	 * @param counter Outgoing packet counter
 	 * @param counter Outgoing packet counter
 	 */
 	 */
-	void attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
+	void attemptToContactAt(void *tPtr,const int64_t localSocket,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
 
 
 	/**
 	/**
 	 * Try a memorized or statically defined path if any are known
 	 * Try a memorized or statically defined path if any are known
@@ -227,11 +227,11 @@ public:
 	{
 	{
 		Mutex::Lock _l(_paths_m);
 		Mutex::Lock _l(_paths_m);
 		if ((inetAddressFamily == AF_INET)&&(_v4Path.lr)&&(_v4Path.p->address().ipScope() == scope)) {
 		if ((inetAddressFamily == AF_INET)&&(_v4Path.lr)&&(_v4Path.p->address().ipScope() == scope)) {
-			attemptToContactAt(tPtr,_v4Path.p->localAddress(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
+			attemptToContactAt(tPtr,_v4Path.p->localSocket(),_v4Path.p->address(),now,false,_v4Path.p->nextOutgoingCounter());
 			_v4Path.p->sent(now);
 			_v4Path.p->sent(now);
 			_v4Path.lr = 0; // path will not be used unless it speaks again
 			_v4Path.lr = 0; // path will not be used unless it speaks again
 		} else if ((inetAddressFamily == AF_INET6)&&(_v6Path.lr)&&(_v6Path.p->address().ipScope() == scope)) {
 		} else if ((inetAddressFamily == AF_INET6)&&(_v6Path.lr)&&(_v6Path.p->address().ipScope() == scope)) {
-			attemptToContactAt(tPtr,_v6Path.p->localAddress(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
+			attemptToContactAt(tPtr,_v6Path.p->localSocket(),_v6Path.p->address(),now,false,_v6Path.p->nextOutgoingCounter());
 			_v6Path.p->sent(now);
 			_v6Path.p->sent(now);
 			_v6Path.lr = 0; // path will not be used unless it speaks again
 			_v6Path.lr = 0; // path will not be used unless it speaks again
 		}
 		}

+ 5 - 5
node/RuntimeEnvironment.hpp

@@ -67,6 +67,11 @@ public:
 		Utils::burn(reinterpret_cast<void *>(const_cast<char *>(secretIdentityStr.data())),(unsigned int)secretIdentityStr.length());
 		Utils::burn(reinterpret_cast<void *>(const_cast<char *>(secretIdentityStr.data())),(unsigned int)secretIdentityStr.length());
 	}
 	}
 
 
+	/**
+	 * A random integer identifying this running instance in a cluster
+	 */
+	uint64_t instanceId;
+
 	// Node instance that owns this RuntimeEnvironment
 	// Node instance that owns this RuntimeEnvironment
 	Node *const node;
 	Node *const node;
 
 
@@ -90,11 +95,6 @@ public:
 	Multicaster *mc;
 	Multicaster *mc;
 	Topology *topology;
 	Topology *topology;
 	SelfAwareness *sa;
 	SelfAwareness *sa;
-
-	/**
-	 * A random integer identifying this run of ZeroTier
-	 */
-	uint32_t instanceId;
 };
 };
 
 
 } // namespace ZeroTier
 } // namespace ZeroTier

+ 2 - 2
node/SelfAwareness.cpp

@@ -69,7 +69,7 @@ SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
 {
 {
 }
 }
 
 
-void SelfAwareness::iam(void *tPtr,const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
+void SelfAwareness::iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
 {
 {
 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
 
 
@@ -77,7 +77,7 @@ void SelfAwareness::iam(void *tPtr,const Address &reporter,const InetAddress &re
 		return;
 		return;
 
 
 	Mutex::Lock _l(_phy_m);
 	Mutex::Lock _l(_phy_m);
-	PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalAddress,reporterPhysicalAddress,scope)];
+	PhySurfaceEntry &entry = _phy[PhySurfaceKey(reporter,receivedOnLocalSocket,reporterPhysicalAddress,scope)];
 
 
 	if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
 	if ( (trusted) && ((now - entry.ts) < ZT_SELFAWARENESS_ENTRY_TIMEOUT) && (!entry.mySurface.ipsEqual(myPhysicalAddress)) ) {
 		// Changes to external surface reported by trusted peers causes path reset in this scope
 		// Changes to external surface reported by trusted peers causes path reset in this scope

+ 5 - 5
node/SelfAwareness.hpp

@@ -55,7 +55,7 @@ public:
 	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 * @param now Current time
 	 * @param now Current time
 	 */
 	 */
-	void iam(void *tPtr,const Address &reporter,const InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
+	void iam(void *tPtr,const Address &reporter,const int64_t receivedOnLocalSocket,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
 
 
 	/**
 	/**
 	 * Clean up database periodically
 	 * Clean up database periodically
@@ -75,15 +75,15 @@ private:
 	struct PhySurfaceKey
 	struct PhySurfaceKey
 	{
 	{
 		Address reporter;
 		Address reporter;
-		InetAddress receivedOnLocalAddress;
+		int64_t receivedOnLocalSocket;
 		InetAddress reporterPhysicalAddress;
 		InetAddress reporterPhysicalAddress;
 		InetAddress::IpScope scope;
 		InetAddress::IpScope scope;
 
 
 		PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
 		PhySurfaceKey() : reporter(),scope(InetAddress::IP_SCOPE_NONE) {}
-		PhySurfaceKey(const Address &r,const InetAddress &rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalAddress(rol),reporterPhysicalAddress(ra),scope(s) {}
+		PhySurfaceKey(const Address &r,const int64_t rol,const InetAddress &ra,InetAddress::IpScope s) : reporter(r),receivedOnLocalSocket(rol),reporterPhysicalAddress(ra),scope(s) {}
 
 
-		inline unsigned long hashCode() const throw() { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
-		inline bool operator==(const PhySurfaceKey &k) const throw() { return ((reporter == k.reporter)&&(receivedOnLocalAddress == k.receivedOnLocalAddress)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
+		inline unsigned long hashCode() const { return ((unsigned long)reporter.toInt() + (unsigned long)scope); }
+		inline bool operator==(const PhySurfaceKey &k) const { return ((reporter == k.reporter)&&(receivedOnLocalSocket == k.receivedOnLocalSocket)&&(reporterPhysicalAddress == k.reporterPhysicalAddress)&&(scope == k.scope)); }
 	};
 	};
 	struct PhySurfaceEntry
 	struct PhySurfaceEntry
 	{
 	{

+ 4 - 4
node/Switch.cpp

@@ -71,12 +71,12 @@ Switch::Switch(const RuntimeEnvironment *renv) :
 {
 {
 }
 }
 
 
-void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
+void Switch::onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len)
 {
 {
 	try {
 	try {
 		const uint64_t now = RR->node->now();
 		const uint64_t now = RR->node->now();
 
 
-		SharedPtr<Path> path(RR->topology->getPath(localAddr,fromAddr));
+		SharedPtr<Path> path(RR->topology->getPath(localSocket,fromAddr));
 		path->received(now);
 		path->received(now);
 
 
 		if (len == 13) {
 		if (len == 13) {
@@ -88,7 +88,7 @@ void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAd
 			const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
 			const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
 			if (beaconAddr == RR->identity.address())
 			if (beaconAddr == RR->identity.address())
 				return;
 				return;
-			if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localAddr,fromAddr))
+			if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localSocket,fromAddr))
 				return;
 				return;
 			const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
 			const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
 			if (peer) { // we'll only respond to beacons from known peers
 			if (peer) { // we'll only respond to beacons from known peers
@@ -752,7 +752,7 @@ bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
 		viaPath = peer->getBestPath(now,false);
 		viaPath = peer->getBestPath(now,false);
 		if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isUpstream(peer->identity())) ) {
 		if ( (viaPath) && (!viaPath->alive(now)) && (!RR->topology->isUpstream(peer->identity())) ) {
 			if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
 			if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
-				peer->attemptToContactAt(tPtr,viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
+				peer->attemptToContactAt(tPtr,viaPath->localSocket(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
 				viaPath->sent(now);
 				viaPath->sent(now);
 			}
 			}
 			viaPath.zero();
 			viaPath.zero();

+ 2 - 2
node/Switch.hpp

@@ -68,12 +68,12 @@ public:
 	 * Called when a packet is received from the real network
 	 * Called when a packet is received from the real network
 	 *
 	 *
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
-	 * @param localAddr Local interface address
+	 * @param localSocket Local I/O socket as supplied by external code
 	 * @param fromAddr Internet IP address of origin
 	 * @param fromAddr Internet IP address of origin
 	 * @param data Packet data
 	 * @param data Packet data
 	 * @param len Packet length
 	 * @param len Packet length
 	 */
 	 */
-	void onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
+	void onRemotePacket(void *tPtr,const int64_t localSocket,const InetAddress &fromAddr,const void *data,unsigned int len);
 
 
 	/**
 	/**
 	 * Called when a packet comes from a local Ethernet tap
 	 * Called when a packet comes from a local Ethernet tap

+ 3 - 1
node/Topology.cpp

@@ -125,10 +125,11 @@ SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
 			return *ap;
 			return *ap;
 	}
 	}
 
 
+	/*
 	try {
 	try {
 		char buf[ZT_PEER_MAX_SERIALIZED_STATE_SIZE];
 		char buf[ZT_PEER_MAX_SERIALIZED_STATE_SIZE];
 		uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0;
 		uint64_t idbuf[2]; idbuf[0] = zta.toInt(); idbuf[1] = 0;
-		int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER_STATE,idbuf,buf,(unsigned int)sizeof(buf));
+		int len = RR->node->stateObjectGet(tPtr,ZT_STATE_OBJECT_PEER,idbuf,buf,(unsigned int)sizeof(buf));
 		if (len > 0) {
 		if (len > 0) {
 			Mutex::Lock _l(_peers_m);
 			Mutex::Lock _l(_peers_m);
 			SharedPtr<Peer> &ap = _peers[zta];
 			SharedPtr<Peer> &ap = _peers[zta];
@@ -140,6 +141,7 @@ SharedPtr<Peer> Topology::getPeer(void *tPtr,const Address &zta)
 			return ap;
 			return ap;
 		}
 		}
 	} catch ( ... ) {} // ignore invalid identities or other strage failures
 	} catch ( ... ) {} // ignore invalid identities or other strage failures
+	*/
 
 
 	return SharedPtr<Peer>();
 	return SharedPtr<Peer>();
 }
 }

+ 2 - 2
node/Topology.hpp

@@ -110,11 +110,11 @@ public:
 	/**
 	/**
 	 * Get a Path object for a given local and remote physical address, creating if needed
 	 * Get a Path object for a given local and remote physical address, creating if needed
 	 *
 	 *
-	 * @param l Local address or NULL for 'any' or 'wildcard'
+	 * @param l Local socket
 	 * @param r Remote address
 	 * @param r Remote address
 	 * @return Pointer to canonicalized Path object
 	 * @return Pointer to canonicalized Path object
 	 */
 	 */
-	inline SharedPtr<Path> getPath(const InetAddress &l,const InetAddress &r)
+	inline SharedPtr<Path> getPath(const int64_t l,const InetAddress &r)
 	{
 	{
 		Mutex::Lock _l(_paths_m);
 		Mutex::Lock _l(_paths_m);
 		SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];
 		SharedPtr<Path> &p = _paths[Path::HashKey(l,r)];

+ 17 - 92
osdep/Binder.hpp

@@ -88,11 +88,7 @@ class Binder : NonCopyable
 private:
 private:
 	struct _Binding
 	struct _Binding
 	{
 	{
-		_Binding() :
-			udpSock((PhySocket *)0),
-			tcpListenSock((PhySocket *)0),
-			address() {}
-
+		_Binding() : udpSock((PhySocket *)0),tcpListenSock((PhySocket *)0) {}
 		PhySocket *udpSock;
 		PhySocket *udpSock;
 		PhySocket *tcpListenSock;
 		PhySocket *tcpListenSock;
 		InetAddress address;
 		InetAddress address;
@@ -373,93 +369,6 @@ public:
 		_bindings.swap(newBindings);
 		_bindings.swap(newBindings);
 	}
 	}
 
 
-	/**
-	 * Send a UDP packet from the specified local interface, or all
-	 *
-	 * Unfortunately even by examining the routing table there is no ultimately
-	 * robust way to tell where we might reach another host that works in all
-	 * environments. As a result, we send packets with null (wildcard) local
-	 * addresses from *every* bound interface.
-	 *
-	 * These are typically initial HELLOs, path probes, etc., since normal
-	 * conversations will have a local endpoint address. So the cost is low and
-	 * if the peer is not reachable via that route then the packet will go
-	 * nowhere and nothing will happen.
-	 *
-	 * It will of course only send via interface bindings of the same socket
-	 * family. No point in sending V4 via V6 or vice versa.
-	 *
-	 * In any case on most hosts there's only one or two interfaces that we
-	 * will use, so none of this is particularly costly.
-	 *
-	 * @param local Local interface address or null address for 'all'
-	 * @param remote Remote address
-	 * @param data Data to send
-	 * @param len Length of data
-	 * @param v4ttl If non-zero, send this packet with the specified IP TTL (IPv4 only)
-	 * @return -1 == local doesn't match any bound address, 0 == send failure, 1 == send successful
-	 */
-	template<typename PHY_HANDLER_TYPE>
-	inline int udpSend(Phy<PHY_HANDLER_TYPE> &phy,const InetAddress &local,const InetAddress &remote,const void *data,unsigned int len,unsigned int v4ttl = 0) const
-	{
-		PhySocket *s;
-		typename std::vector<_Binding>::const_iterator i;
-		int result;
-		Mutex::Lock _l(_lock);
-
-		if (remote.ss_family == AF_INET) {
-			if (local) {
-				for(i=_bindings.begin();i!=_bindings.end();++i) {
-					if (
-					     (i->address.ss_family == AF_INET) &&
-					     (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_port == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_port) &&
-					     (reinterpret_cast<const struct sockaddr_in *>(&(i->address))->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&local)->sin_addr.s_addr)
-					   )
-					{
-						s = i->udpSock;
-						goto Binder_send_packet;
-					}
-				}
-			} else {
-				for(i=_bindings.begin();i!=_bindings.end();++i) {
-					if (i->address.ss_family == AF_INET) {
-						s = i->udpSock;
-						goto Binder_send_packet;
-					}
-				}
-			}
-		} else {
-			if (local) {
-				for(i=_bindings.begin();i!=_bindings.end();++i) {
-					if (
-					     (i->address.ss_family == AF_INET6) &&
-					     (reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_port == reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_port) &&
-					     (!memcmp(reinterpret_cast<const struct sockaddr_in6 *>(&(i->address))->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&local)->sin6_addr.s6_addr,16))
-					   )
-					{
-						s = i->udpSock;
-						goto Binder_send_packet;
-					}
-				}
-			} else {
-				for(i=_bindings.begin();i!=_bindings.end();++i) {
-					if (i->address.ss_family == AF_INET6) {
-						s = i->udpSock;
-						goto Binder_send_packet;
-					}
-				}
-			}
-		}
-
-		return -1;
-
-Binder_send_packet:
-		if (v4ttl) phy.setIp4UdpTtl(s,v4ttl);
-		result = (int)phy.udpSend(s,reinterpret_cast<const struct sockaddr *>(&remote),data,len);
-		if (v4ttl) phy.setIp4UdpTtl(s,255);
-		return result;
-	}
-
 	/**
 	/**
 	 * @return All currently bound local interface addresses
 	 * @return All currently bound local interface addresses
 	 */
 	 */
@@ -472,6 +381,22 @@ Binder_send_packet:
 		return aa;
 		return aa;
 	}
 	}
 
 
+	/**
+	 * Send from all bound UDP sockets
+	 */
+	template<typename PHY_HANDLER_TYPE>
+	inline bool udpSendAll(Phy<PHY_HANDLER_TYPE> &phy,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+	{
+		bool r = false;
+		Mutex::Lock _l(_lock);
+		for(std::vector<_Binding>::const_iterator b(_bindings.begin());b!=_bindings.end();++b) {
+			if (ttl) phy.setIp4UdpTtl(b->udpSock,ttl);
+			if (phy.udpSend(b->udpSock,(const struct sockaddr *)addr,data,len)) r = true;
+			if (ttl) phy.setIp4UdpTtl(b->udpSock,255);
+		}
+		return r;
+	}
+
 	/**
 	/**
 	 * @param addr Address to check
 	 * @param addr Address to check
 	 * @return True if this is a bound local interface address
 	 * @return True if this is a bound local interface address

+ 67 - 542
service/OneService.cpp

@@ -59,8 +59,6 @@
 #include "../osdep/ManagedRoute.hpp"
 #include "../osdep/ManagedRoute.hpp"
 
 
 #include "OneService.hpp"
 #include "OneService.hpp"
-#include "ClusterGeoIpService.hpp"
-#include "ClusterDefinition.hpp"
 #include "SoftwareUpdater.hpp"
 #include "SoftwareUpdater.hpp"
 
 
 #ifdef __WINDOWS__
 #ifdef __WINDOWS__
@@ -157,9 +155,6 @@ namespace ZeroTier { typedef BSDEthernetTap EthernetTap; }
 // Maximum write buffer size for outgoing TCP connections (sanity limit)
 // Maximum write buffer size for outgoing TCP connections (sanity limit)
 #define ZT_TCP_MAX_WRITEQ_SIZE 33554432
 #define ZT_TCP_MAX_WRITEQ_SIZE 33554432
 
 
-// How often to check TCP connections and cluster links and send status to cluster peers
-#define ZT_TCP_CHECK_PERIOD 15000
-
 // TCP activity timeout
 // TCP activity timeout
 #define ZT_TCP_ACTIVITY_TIMEOUT 60000
 #define ZT_TCP_ACTIVITY_TIMEOUT 60000
 
 
@@ -311,9 +306,9 @@ static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr
 static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
 static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
 static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len);
 static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len);
 static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen);
 static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen);
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
 static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
 static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr);
 static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
 static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
 static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
 static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
 
 
@@ -362,8 +357,7 @@ struct TcpConnection
 		TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection
 		TCP_UNCATEGORIZED_INCOMING, // uncategorized incoming connection
 		TCP_HTTP_INCOMING,
 		TCP_HTTP_INCOMING,
 		TCP_HTTP_OUTGOING,
 		TCP_HTTP_OUTGOING,
-		TCP_TUNNEL_OUTGOING, // TUNNELED mode proxy outbound connection
-		TCP_CLUSTER_BACKPLANE
+		TCP_TUNNEL_OUTGOING // TUNNELED mode proxy outbound connection
 	} type;
 	} type;
 
 
 	OneServiceImpl *parent;
 	OneServiceImpl *parent;
@@ -380,29 +374,11 @@ struct TcpConnection
 	std::string status;
 	std::string status;
 	std::map< std::string,std::string > headers;
 	std::map< std::string,std::string > headers;
 
 
-	// Used for cluster backplane connections
-	uint64_t clusterMemberId;
-	unsigned int clusterMemberVersionMajor;
-	unsigned int clusterMemberVersionMinor;
-	unsigned int clusterMemberVersionRev;
-	std::vector< InetAddress > clusterMemberLocalAddresses;
-	Mutex clusterMemberLocalAddresses_m;
-
 	std::string readq;
 	std::string readq;
 	std::string writeq;
 	std::string writeq;
 	Mutex writeq_m;
 	Mutex writeq_m;
 };
 };
 
 
-/**
- * Message types for cluster backplane communication
- */
-enum ClusterMessageType
-{
-	CLUSTER_MESSAGE_STATUS = 0,
-	CLUSTER_MESSAGE_STATE_OBJECT = 1,
-	CLUSTER_MESSAGE_PROXY_SEND = 2
-};
-
 class OneServiceImpl : public OneService
 class OneServiceImpl : public OneService
 {
 {
 public:
 public:
@@ -421,8 +397,6 @@ public:
 	bool _updateAutoApply;
 	bool _updateAutoApply;
 	unsigned int _primaryPort;
 	unsigned int _primaryPort;
 	volatile unsigned int _udpPortPickerCounter;
 	volatile unsigned int _udpPortPickerCounter;
-	uint64_t _clusterMemberId;
-	uint8_t _clusterKey[32]; // secret key for cluster backplane config
 
 
 	// Local configuration and memo-ized information from it
 	// Local configuration and memo-ized information from it
 	json _localConfig;
 	json _localConfig;
@@ -434,7 +408,6 @@ public:
 	std::vector< InetAddress > _globalV6Blacklist;
 	std::vector< InetAddress > _globalV6Blacklist;
 	std::vector< InetAddress > _allowManagementFrom;
 	std::vector< InetAddress > _allowManagementFrom;
 	std::vector< std::string > _interfacePrefixBlacklist;
 	std::vector< std::string > _interfacePrefixBlacklist;
-	std::vector< InetAddress > _clusterBackplaneAddresses;
 	Mutex _localConfig_m;
 	Mutex _localConfig_m;
 
 
 	/*
 	/*
@@ -518,7 +491,6 @@ public:
 		,_updateAutoApply(false)
 		,_updateAutoApply(false)
 		,_primaryPort(port)
 		,_primaryPort(port)
 		,_udpPortPickerCounter(0)
 		,_udpPortPickerCounter(0)
-		,_clusterMemberId(0)
 		,_lastDirectReceiveFromGlobal(0)
 		,_lastDirectReceiveFromGlobal(0)
 #ifdef ZT_TCP_FALLBACK_RELAY
 #ifdef ZT_TCP_FALLBACK_RELAY
 		,_lastSendToGlobalV4(0)
 		,_lastSendToGlobalV4(0)
@@ -754,23 +726,6 @@ public:
 				}
 				}
 			}
 			}
 
 
-			// Derive the cluster's shared secret backplane encryption key by hashing its shared secret identity
-			{
-				uint8_t tmp[64];
-				uint8_t sk[ZT_C25519_PRIVATE_KEY_LEN + 4];
-				memcpy(sk,_node->identity().privateKeyPair().priv.data,ZT_C25519_PRIVATE_KEY_LEN);
-				sk[ZT_C25519_PRIVATE_KEY_LEN] = 0xab;
-				sk[ZT_C25519_PRIVATE_KEY_LEN + 1] = 0xcd;
-				sk[ZT_C25519_PRIVATE_KEY_LEN + 2] = 0xef;
-				sk[ZT_C25519_PRIVATE_KEY_LEN + 3] = 0xab; // add an arbitrary nonce, just because
-				SHA512::hash(tmp,sk,ZT_C25519_PRIVATE_KEY_LEN + 4);
-				memcpy(_clusterKey,tmp,32);
-			}
-
-			// Assign a random non-zero cluster member ID to identify vs. other cluster members
-			Utils::getSecureRandom(&_clusterMemberId,sizeof(_clusterMemberId));
-			if (!_clusterMemberId) _clusterMemberId = 1;
-
 			// Main I/O loop
 			// Main I/O loop
 			_nextBackgroundTaskDeadline = 0;
 			_nextBackgroundTaskDeadline = 0;
 			uint64_t clockShouldBe = OSUtils::now();
 			uint64_t clockShouldBe = OSUtils::now();
@@ -779,7 +734,6 @@ public:
 			uint64_t lastBindRefresh = 0;
 			uint64_t lastBindRefresh = 0;
 			uint64_t lastUpdateCheck = clockShouldBe;
 			uint64_t lastUpdateCheck = clockShouldBe;
 			uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
 			uint64_t lastLocalInterfaceAddressCheck = (clockShouldBe - ZT_LOCAL_INTERFACE_CHECK_INTERVAL) + 15000; // do this in 15s to give portmapper time to configure and other things time to settle
-			uint64_t lastTcpCheck = 0;
 			for(;;) {
 			for(;;) {
 				_run_m.lock();
 				_run_m.lock();
 				if (!_run) {
 				if (!_run) {
@@ -873,58 +827,6 @@ public:
 						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i)));
 						_node->addLocalInterfaceAddress(reinterpret_cast<const struct sockaddr_storage *>(&(*i)));
 				}
 				}
 
 
-				// Check TCP connections and cluster links
-				if ((now - lastTcpCheck) >= ZT_TCP_CHECK_PERIOD) {
-					lastTcpCheck = now;
-
-					// Send status to active cluster links and close overflowed and dead ones
-					std::vector<PhySocket *> toClose;
-					std::vector<InetAddress> clusterLinksUp;
-					{
-						Mutex::Lock _l(_tcpConnections_m);
-						for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) {
-							TcpConnection *const tc = *c;
-							tc->writeq_m.lock();
-							const unsigned long wql = (unsigned long)tc->writeq.length();
-							tc->writeq_m.unlock();
-							if ((tc->sock)&&((wql > ZT_TCP_MAX_WRITEQ_SIZE)||((now - tc->lastReceive) > ZT_TCP_ACTIVITY_TIMEOUT))) {
-								toClose.push_back(tc->sock);
-							} else if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) {
-								clusterLinksUp.push_back(tc->remoteAddr);
-								sendMyCurrentClusterState(tc);
-							}
-						}
-					}
-					for(std::vector<PhySocket *>::iterator s(toClose.begin());s!=toClose.end();++s)
-						_phy.close(*s,true);
-
-					// Attempt to connect to cluster links we don't have an active connection to
-					{
-						Mutex::Lock _l(_localConfig_m);
-						for(std::vector<InetAddress>::const_iterator ca(_clusterBackplaneAddresses.begin());ca!=_clusterBackplaneAddresses.end();++ca) {
-							if ( (std::find(clusterLinksUp.begin(),clusterLinksUp.end(),*ca) == clusterLinksUp.end()) && (!_binder.isBoundLocalInterfaceAddress(*ca)) ) {
-								TcpConnection *tc = new TcpConnection();
-								{
-									Mutex::Lock _l(_tcpConnections_m);
-									_tcpConnections.push_back(tc);
-								}
-
-								tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE;
-								tc->remoteAddr = *ca;
-								tc->lastReceive = OSUtils::now();
-								tc->parent = this;
-								tc->sock = (PhySocket *)0; // set in connect handler
-								tc->messageSize = 0;
-
-								tc->clusterMemberId = 0; // not known yet
-
-								bool connected = false;
-								_phy.tcpConnect(reinterpret_cast<const struct sockaddr *>(&(*ca)),connected,(void *)tc,true);
-							}
-						}
-					}
-				}
-
 				const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
 				const unsigned long delay = (dl > now) ? (unsigned long)(dl - now) : 100;
 				clockShouldBe = now + (uint64_t)delay;
 				clockShouldBe = now + (uint64_t)delay;
 				_phy.poll(delay);
 				_phy.poll(delay);
@@ -1211,21 +1113,6 @@ public:
 					res["planetWorldId"] = planet.id();
 					res["planetWorldId"] = planet.id();
 					res["planetWorldTimestamp"] = planet.timestamp();
 					res["planetWorldTimestamp"] = planet.timestamp();
 
 
-					{
-						json cj(json::object());
-						Mutex::Lock _l(_tcpConnections_m);
-						Mutex::Lock _l2(_localConfig_m);
-						for(std::vector<InetAddress>::const_iterator ca(_clusterBackplaneAddresses.begin());ca!=_clusterBackplaneAddresses.end();++ca) {
-							uint64_t up = 0;
-							for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) {
-								if (((*c)->remoteAddr == *ca)&&((*c)->clusterMemberId)&&((*c)->lastReceive > up))
-									up = (*c)->lastReceive;
-							}
-							cj[ca->toString()] = up;
-						}
-						res["cluster"] = cj;
-					}
-
 					scode = 200;
 					scode = 200;
 				} else if (ps[0] == "moon") {
 				} else if (ps[0] == "moon") {
 					std::vector<World> moons(_node->moons());
 					std::vector<World> moons(_node->moons());
@@ -1576,16 +1463,6 @@ public:
 			}
 			}
 		}
 		}
 
 
-		json &cl = settings["cluster"];
-		_clusterBackplaneAddresses.clear();
-		if (cl.is_array()) {
-			for(unsigned long i=0;i<cl.size();++i) {
-				const InetAddress cip(OSUtils::jsonString(cl[i],""));
-				if ((cip.ss_family == AF_INET)||(cip.ss_family == AF_INET6))
-					_clusterBackplaneAddresses.push_back(cip);
-			}
-		}
-
 		json &controllerDbHttpHost = settings["controllerDbHttpHost"];
 		json &controllerDbHttpHost = settings["controllerDbHttpHost"];
 		json &controllerDbHttpPort = settings["controllerDbHttpPort"];
 		json &controllerDbHttpPort = settings["controllerDbHttpPort"];
 		json &controllerDbHttpPath = settings["controllerDbHttpPath"];
 		json &controllerDbHttpPath = settings["controllerDbHttpPath"];
@@ -1754,250 +1631,6 @@ public:
 		}
 		}
 	}
 	}
 
 
-	// =========================================================================
-	// Cluster messaging functions
-	// =========================================================================
-
-	// mlen must be at least 24
-	void encryptClusterMessage(char *data,unsigned int mlen)
-	{
-		uint8_t key[32];
-		memcpy(key,_clusterKey,32);
-		for(int i=0;i<8;++i) key[i] ^= data[i];
-		Salsa20 s20(key,data + 8);
-
-		uint8_t macKey[32];
-		uint8_t mac[16];
-		memset(macKey,0,32);
-		s20.crypt12(macKey,macKey,32);
-		s20.crypt12(data + 24,data + 24,mlen - 24);
-		Poly1305::compute(mac,data + 24,mlen - 24,macKey);
-		memcpy(data + 16,mac,8);
-	}
-
-	void announceStatusToClusterMember(TcpConnection *tc)
-	{
-		try {
-			Buffer<8194> buf;
-
-			buf.appendRandom(16);
-			buf.addSize(8); // space for MAC
-			buf.append((uint8_t)CLUSTER_MESSAGE_STATUS);
-			buf.append(_clusterMemberId);
-			buf.append((uint16_t)ZEROTIER_ONE_VERSION_MAJOR);
-			buf.append((uint16_t)ZEROTIER_ONE_VERSION_MINOR);
-			buf.append((uint16_t)ZEROTIER_ONE_VERSION_REVISION);
-
-			std::vector<InetAddress> lif(_binder.allBoundLocalInterfaceAddresses());
-			buf.append((uint16_t)lif.size());
-			for(std::vector<InetAddress>::const_iterator i(lif.begin());i!=lif.end();++i)
-				i->serialize(buf);
-
-			Mutex::Lock _l(tc->writeq_m);
-
-			if (tc->writeq.length() == 0)
-				_phy.setNotifyWritable(tc->sock,true);
-
-			const unsigned int mlen = buf.size();
-			tc->writeq.push_back((char)((mlen >> 16) & 0xff));
-			tc->writeq.push_back((char)((mlen >> 8) & 0xff));
-			tc->writeq.push_back((char)(mlen & 0xff));
-
-			char *const data = reinterpret_cast<char *>(buf.unsafeData());
-			encryptClusterMessage(data,mlen);
-			tc->writeq.append(data,mlen);
-		} catch ( ... ) {
-			fprintf(stderr,"WARNING: unexpected exception announcing status to cluster members" ZT_EOL_S);
-		}
-	}
-
-	bool proxySendViaCluster(const InetAddress &fromAddress,const InetAddress &dest,const void *data,unsigned int len,unsigned int ttl)
-	{
-		Mutex::Lock _l(_tcpConnections_m);
-		for(std::vector<TcpConnection *>::const_iterator c(_tcpConnections.begin());c!=_tcpConnections.end();++c) {
-			TcpConnection *const tc = *c;
-			if ((tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(tc->clusterMemberId)) {
-				Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m);
-				for(std::vector<InetAddress>::const_iterator i(tc->clusterMemberLocalAddresses.begin());i!=tc->clusterMemberLocalAddresses.end();++i) {
-					if (*i == fromAddress) {
-						Buffer<1024> buf;
-
-						buf.appendRandom(16);
-						buf.addSize(8); // space for MAC
-						buf.append((uint8_t)CLUSTER_MESSAGE_PROXY_SEND);
-						buf.append((uint8_t)ttl);
-						dest.serialize(buf);
-						fromAddress.serialize(buf);
-
-						Mutex::Lock _l3(tc->writeq_m);
-
-						if (tc->writeq.length() == 0)
-							_phy.setNotifyWritable(tc->sock,true);
-
-						const unsigned int mlen = buf.size() + len;
-						tc->writeq.push_back((char)((mlen >> 16) & 0xff));
-						tc->writeq.push_back((char)((mlen >> 8) & 0xff));
-						tc->writeq.push_back((char)(mlen & 0xff));
-
-						const unsigned long startpos = (unsigned long)tc->writeq.length();
-						tc->writeq.append(reinterpret_cast<const char *>(buf.data()),buf.size());
-						tc->writeq.append(reinterpret_cast<const char *>(data),len);
-
-						char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos;
-						encryptClusterMessage(outdata,mlen);
-
-						return true;
-					}
-				}
-			}
-		}
-		return false;
-	}
-
-	void replicateStateObject(const ZT_StateObjectType type,const uint64_t id[2],const void *const data,const unsigned int len,TcpConnection *tc)
-	{
-		char buf[42];
-		Mutex::Lock _l2(tc->writeq_m);
-
-		if (tc->writeq.length() == 0)
-			_phy.setNotifyWritable(tc->sock,true);
-
-		const unsigned int mlen = len + 42;
-
-		tc->writeq.push_back((char)((mlen >> 16) & 0xff));
-		tc->writeq.push_back((char)((mlen >> 8) & 0xff));
-		tc->writeq.push_back((char)(mlen & 0xff));
-
-		Utils::getSecureRandom(buf,16);
-		buf[24] = (char)CLUSTER_MESSAGE_STATE_OBJECT;
-		buf[25] = (char)type;
-		buf[26] = (char)((id[0] >> 56) & 0xff);
-		buf[27] = (char)((id[0] >> 48) & 0xff);
-		buf[28] = (char)((id[0] >> 40) & 0xff);
-		buf[29] = (char)((id[0] >> 32) & 0xff);
-		buf[30] = (char)((id[0] >> 24) & 0xff);
-		buf[31] = (char)((id[0] >> 16) & 0xff);
-		buf[32] = (char)((id[0] >> 8) & 0xff);
-		buf[33] = (char)(id[0] & 0xff);
-		buf[34] = (char)((id[1] >> 56) & 0xff);
-		buf[35] = (char)((id[1] >> 48) & 0xff);
-		buf[36] = (char)((id[1] >> 40) & 0xff);
-		buf[37] = (char)((id[1] >> 32) & 0xff);
-		buf[38] = (char)((id[1] >> 24) & 0xff);
-		buf[39] = (char)((id[1] >> 16) & 0xff);
-		buf[40] = (char)((id[1] >> 8) & 0xff);
-		buf[41] = (char)(id[1] & 0xff);
-
-		const unsigned long startpos = (unsigned long)tc->writeq.length();
-		tc->writeq.append(buf,42);
-		tc->writeq.append(reinterpret_cast<const char *>(data),len);
-
-		char *const outdata = const_cast<char *>(tc->writeq.data()) + startpos;
-		encryptClusterMessage(outdata,mlen);
-		tc->writeq.append(outdata,mlen);
-	}
-
-	void writeStateObject(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
-	{
-		char buf[65535];
-		char p[1024];
-		FILE *f;
-		bool secure = false;
-
-		switch(type) {
-			case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
-				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
-				break;
-			case ZT_STATE_OBJECT_IDENTITY_SECRET:
-				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
-				secure = true;
-				break;
-			//case ZT_STATE_OBJECT_PEER_STATE:
-			//	break;
-			case ZT_STATE_OBJECT_NETWORK_CONFIG:
-				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]);
-				secure = true;
-				break;
-			//case ZT_STATE_OBJECT_NETWORK_MEMBERSHIP:
-			//	break;
-			case ZT_STATE_OBJECT_PLANET:
-				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
-				break;
-			case ZT_STATE_OBJECT_MOON:
-				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id[0]);
-				break;
-			default:
-				p[0] = (char)0;
-				break;
-		}
-
-		if (p[0]) {
-			if (len >= 0) {
-				// Check to see if we've already written this first. This reduces
-				// redundant writes and I/O overhead on most platforms and has
-				// little effect on others.
-				f = fopen(p,"r");
-				bool redundant = false;
-				if (f) {
-					long l = (long)fread(buf,1,sizeof(buf),f);
-					fclose(f);
-					redundant = ((l == (long)len)&&(memcmp(data,buf,l) == 0));
-				}
-				if (!redundant) {
-					f = fopen(p,"w");
-					if (f) {
-						if (fwrite(data,len,1,f) != 1)
-							fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p);
-						fclose(f);
-						if (secure)
-							OSUtils::lockDownFile(p,false);
-					} else {
-						fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p);
-					}
-				}
-			} else {
-				OSUtils::rm(p);
-			}
-		}
-	}
-
-	void sendMyCurrentClusterState(TcpConnection *tc)
-	{
-		// We currently don't need to dump everything. Networks and moons are most important.
-		// The rest will get caught up rapidly due to constant peer updates, etc.
-		std::string buf;
-		std::vector<std::string> l(OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "networks.d").c_str(),false));
-		for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) {
-			buf.clear();
-			if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) {
-				if (f->length() == 21) {
-					const uint64_t nwid = Utils::hexStrToU64(f->substr(0,16).c_str());
-					if (nwid) {
-						uint64_t tmp[2];
-						tmp[0] = nwid;
-						tmp[1] = 0;
-						replicateStateObject(ZT_STATE_OBJECT_NETWORK_CONFIG,tmp,buf.data(),(int)buf.length(),tc);
-					}
-				}
-			}
-		}
-		l = OSUtils::listDirectory((_homePath + ZT_PATH_SEPARATOR_S + "moons.d").c_str(),false);
-		for(std::vector<std::string>::const_iterator f(l.begin());f!=l.end();++f) {
-			buf.clear();
-			if (OSUtils::readFile((_homePath + ZT_PATH_SEPARATOR_S + *f).c_str(),buf)) {
-				if (f->length() == 21) {
-					const uint64_t moonId = Utils::hexStrToU64(f->substr(0,16).c_str());
-					if (moonId) {
-						uint64_t tmp[2];
-						tmp[0] = moonId;
-						tmp[1] = 0;
-						replicateStateObject(ZT_STATE_OBJECT_MOON,tmp,buf.data(),(int)buf.length(),tc);
-					}
-				}
-			}
-		}
-	}
-
 	// =========================================================================
 	// =========================================================================
 	// Handlers for Node and Phy<> callbacks
 	// Handlers for Node and Phy<> callbacks
 	// =========================================================================
 	// =========================================================================
@@ -2010,7 +1643,7 @@ public:
 		const ZT_ResultCode rc = _node->processWirePacket(
 		const ZT_ResultCode rc = _node->processWirePacket(
 			(void *)0,
 			(void *)0,
 			OSUtils::now(),
 			OSUtils::now(),
-			reinterpret_cast<const struct sockaddr_storage *>(localAddr),
+			(int64_t)((uintptr_t)sock),
 			(const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
 			(const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
 			data,
 			data,
 			len,
 			len,
@@ -2044,13 +1677,6 @@ public:
 				_phy.close(_tcpFallbackTunnel->sock);
 				_phy.close(_tcpFallbackTunnel->sock);
 			_tcpFallbackTunnel = tc;
 			_tcpFallbackTunnel = tc;
 			_phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO));
 			_phy.streamSend(sock,ZT_TCP_TUNNEL_HELLO,sizeof(ZT_TCP_TUNNEL_HELLO));
-		} else if (tc->type == TcpConnection::TCP_CLUSTER_BACKPLANE) {
-			{
-				Mutex::Lock _l(tc->writeq_m);
-				tc->writeq.push_back((char)0x93); // identifies type of connection as cluster backplane
-			}
-			announceStatusToClusterMember(tc);
-			_phy.setNotifyWritable(sock,true);
 		} else {
 		} else {
 			_phy.close(sock,true);
 			_phy.close(sock,true);
 		}
 		}
@@ -2106,31 +1732,6 @@ public:
 
 
 				case TcpConnection::TCP_UNCATEGORIZED_INCOMING:
 				case TcpConnection::TCP_UNCATEGORIZED_INCOMING:
 					switch(reinterpret_cast<uint8_t *>(data)[0]) {
 					switch(reinterpret_cast<uint8_t *>(data)[0]) {
-						// 0x93 is first byte of cluster backplane connections
-						case 0x93: {
-							// We only allow this from cluster backplane IPs. We also authenticate
-							// each packet cryptographically, so this is just a first line of defense.
-							bool allow = false;
-							{
-								Mutex::Lock _l(_localConfig_m);
-								for(std::vector< InetAddress >::const_iterator i(_clusterBackplaneAddresses.begin());i!=_clusterBackplaneAddresses.end();++i) {
-									if (tc->remoteAddr.ipsEqual(*i)) {
-										allow = true;
-										break;
-									}
-								}
-							}
-							if (allow) {
-								tc->type = TcpConnection::TCP_CLUSTER_BACKPLANE;
-								tc->clusterMemberId = 0; // unknown, waiting for first status message
-								announceStatusToClusterMember(tc);
-								if (len > 1)
-									phyOnTcpData(sock,uptr,reinterpret_cast<uint8_t *>(data) + 1,len - 1);
-							} else {
-								_phy.close(sock);
-							}
-						}	break;
-
 						// HTTP: GET, PUT, POST, HEAD
 						// HTTP: GET, PUT, POST, HEAD
 						case 'G':
 						case 'G':
 						case 'P':
 						case 'P':
@@ -2223,7 +1824,7 @@ public:
 									const ZT_ResultCode rc = _node->processWirePacket(
 									const ZT_ResultCode rc = _node->processWirePacket(
 										(void *)0,
 										(void *)0,
 										OSUtils::now(),
 										OSUtils::now(),
-										reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
+										-1,
 										reinterpret_cast<struct sockaddr_storage *>(&from),
 										reinterpret_cast<struct sockaddr_storage *>(&from),
 										data,
 										data,
 										plen,
 										plen,
@@ -2248,114 +1849,6 @@ public:
 					}
 					}
 					return;
 					return;
 
 
-				case TcpConnection::TCP_CLUSTER_BACKPLANE:
-					tc->readq.append((const char *)data,len);
-					if (tc->readq.length() >= 28) { // got 3-byte message size + 16-byte IV + 8-byte MAC + 1-byte type (encrypted)
-						uint8_t *data = reinterpret_cast<uint8_t *>(const_cast<char *>(tc->readq.data()));
-						unsigned long mlen = ( ((unsigned long)data[0] << 16) | ((unsigned long)data[1] << 8) | (unsigned long)data[2] );
-						if ((mlen < 25)||(mlen > ZT_TCP_MAX_WRITEQ_SIZE)) {
-							_phy.close(sock);
-							return;
-						} else if (tc->readq.length() >= (mlen + 3)) { // got entire message
-							data += 3;
-
-							uint8_t key[32];
-							memcpy(key,_clusterKey,32);
-							for(int i=0;i<8;++i) key[i] ^= data[i]; // first 8 bytes of IV get XORed with key
-							Salsa20 s20(key,data + 8); // last 8 bytes of IV are fed into Salsa20 directly as its 64-bit IV
-
-							uint8_t macKey[32];
-							uint8_t mac[16];
-							memset(macKey,0,32);
-							s20.crypt12(macKey,macKey,32);
-							Poly1305::compute(mac,data + 24,mlen - 24,macKey);
-							if (!Utils::secureEq(mac,data + 16,8)) {
-								_phy.close(sock);
-								return;
-							}
-							s20.crypt12(data + 24,data + 24,mlen - 24);
-
-							switch((ClusterMessageType)data[24]) {
-								case CLUSTER_MESSAGE_STATUS:
-									if (mlen > (25 + 16)) {
-										Buffer<4096> tmp(data + 25,mlen - 25);
-										try {
-											const uint64_t cmid = tmp.at<uint64_t>(0);
-											if (cmid == _clusterMemberId) { // shouldn't happen, but don't allow self-to-self
-												_phy.close(sock);
-												return;
-											}
-											if (!tc->clusterMemberId) {
-												tc->clusterMemberId = cmid;
-												sendMyCurrentClusterState(tc);
-											}
-											tc->clusterMemberVersionMajor = tmp.at<uint16_t>(8);
-											tc->clusterMemberVersionMinor = tmp.at<uint16_t>(10);
-											tc->clusterMemberVersionRev = tmp.at<uint16_t>(12);
-											const unsigned int clusterMemberLocalAddressCount = tmp.at<uint16_t>(14);
-											std::vector<InetAddress> la;
-											unsigned int ptr = 16;
-											for(unsigned int k=0;k<clusterMemberLocalAddressCount;++k) {
-												la.push_back(InetAddress());
-												ptr += la.back().deserialize(tmp,ptr);
-											}
-											{
-												Mutex::Lock _l2(tc->clusterMemberLocalAddresses_m);
-												tc->clusterMemberLocalAddresses.swap(la);
-											}
-										} catch ( ... ) {}
-									}
-									break;
-
-								case CLUSTER_MESSAGE_STATE_OBJECT:
-									if (mlen > 42) { // type + object ID + [data]
-										uint64_t objId[2];
-										objId[0] = (
-											((uint64_t)data[26] << 56) |
-											((uint64_t)data[27] << 48) |
-											((uint64_t)data[28] << 40) |
-											((uint64_t)data[29] << 32) |
-											((uint64_t)data[30] << 24) |
-											((uint64_t)data[31] << 16) |
-											((uint64_t)data[32] << 8) |
-											(uint64_t)data[33]
-										);
-										objId[1] = (
-											((uint64_t)data[34] << 56) |
-											((uint64_t)data[35] << 48) |
-											((uint64_t)data[36] << 40) |
-											((uint64_t)data[37] << 32) |
-											((uint64_t)data[38] << 24) |
-											((uint64_t)data[39] << 16) |
-											((uint64_t)data[40] << 8) |
-											(uint64_t)data[41]
-										);
-										if (_node->processStateUpdate((void *)0,(ZT_StateObjectType)data[25],objId,data + 42,(unsigned int)(mlen - 42)) == ZT_RESULT_OK)
-											writeStateObject((ZT_StateObjectType)data[25],objId,data + 42,(unsigned int)(mlen - 42));
-									}
-									break;
-
-								case CLUSTER_MESSAGE_PROXY_SEND:
-									if (mlen > 25) {
-										Buffer<4096> tmp(data + 25,mlen - 25);
-										try {
-											InetAddress dest,src;
-											const unsigned int ttl = (unsigned int)tmp[0];
-											unsigned int ptr = 1;
-											ptr += dest.deserialize(tmp);
-											ptr += src.deserialize(tmp,ptr);
-											if (ptr < tmp.size())
-												_binder.udpSend(_phy,src,dest,reinterpret_cast<const uint8_t *>(tmp.data()) + ptr,tmp.size() - ptr,ttl);
-										} catch ( ... ) {}
-									}
-									break;
-							}
-
-							tc->readq.erase(tc->readq.begin(),tc->readq.begin() + mlen);
-						}
-					}
-					return;
-
 			}
 			}
 		} catch ( ... ) {
 		} catch ( ... ) {
 			_phy.close(sock);
 			_phy.close(sock);
@@ -2549,18 +2042,57 @@ public:
 
 
 	inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
 	inline void nodeStatePutFunction(enum ZT_StateObjectType type,const uint64_t id[2],const void *data,int len)
 	{
 	{
-		writeStateObject(type,id,data,len);
+		char p[1024];
+		FILE *f;
+		bool secure = false;
 
 
-		std::vector<uint64_t> sentTo;
-		{
-			Mutex::Lock _l(_tcpConnections_m);
-			for(std::vector<TcpConnection *>::const_iterator ci(_tcpConnections.begin());ci!=_tcpConnections.end();++ci) {
-				TcpConnection *const c = *ci;
-				if ((c->type == TcpConnection::TCP_CLUSTER_BACKPLANE)&&(c->clusterMemberId != 0)&&(std::find(sentTo.begin(),sentTo.end(),c->clusterMemberId) == sentTo.end())) {
-					sentTo.push_back(c->clusterMemberId);
-					replicateStateObject(type,id,data,len,c);
-				}
+		switch(type) {
+			case ZT_STATE_OBJECT_IDENTITY_PUBLIC:
+				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.public",_homePath.c_str());
+				break;
+			case ZT_STATE_OBJECT_IDENTITY_SECRET:
+				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "identity.secret",_homePath.c_str());
+				secure = true;
+				break;
+			case ZT_STATE_OBJECT_PLANET:
+				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "planet",_homePath.c_str());
+				break;
+			case ZT_STATE_OBJECT_MOON:
+				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "moons.d/%.16llx.moon",_homePath.c_str(),(unsigned long long)id[0]);
+				break;
+			case ZT_STATE_OBJECT_NETWORK_CONFIG:
+				Utils::ztsnprintf(p,sizeof(p),"%s" ZT_PATH_SEPARATOR_S "networks.d/%.16llx.conf",_homePath.c_str(),(unsigned long long)id[0]);
+				secure = true;
+				break;
+			default:
+				return;
+		}
+
+		if (len >= 0) {
+			// Check to see if we've already written this first. This reduces
+			// redundant writes and I/O overhead on most platforms and has
+			// little effect on others.
+			f = fopen(p,"r");
+			if (f) {
+				char buf[65535];
+				long l = (long)fread(buf,1,sizeof(buf),f);
+				fclose(f);
+				if ((l == (long)len)&&(memcmp(data,buf,l) == 0))
+					return;
 			}
 			}
+
+			f = fopen(p,"w");
+			if (f) {
+				if (fwrite(data,len,1,f) != 1)
+					fprintf(stderr,"WARNING: unable to write to file: %s (I/O error)" ZT_EOL_S,p);
+				fclose(f);
+				if (secure)
+					OSUtils::lockDownFile(p,false);
+			} else {
+				fprintf(stderr,"WARNING: unable to write to file: %s (unable to open)" ZT_EOL_S,p);
+			}
+		} else {
+			OSUtils::rm(p);
 		}
 		}
 	}
 	}
 
 
@@ -2596,7 +2128,7 @@ public:
 		return -1;
 		return -1;
 	}
 	}
 
 
-	inline int nodeWirePacketSendFunction(const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+	inline int nodeWirePacketSendFunction(const int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
 	{
 	{
 #ifdef ZT_TCP_FALLBACK_RELAY
 #ifdef ZT_TCP_FALLBACK_RELAY
 		if (addr->ss_family == AF_INET) {
 		if (addr->ss_family == AF_INET) {
@@ -2646,20 +2178,13 @@ public:
 		// proxy fallback, which is slow.
 		// proxy fallback, which is slow.
 #endif // ZT_TCP_FALLBACK_RELAY
 #endif // ZT_TCP_FALLBACK_RELAY
 
 
-		switch (_binder.udpSend(_phy,*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) {
-			case -1: // local bound address not found, so see if a cluster peer owns it
-				if (localAddr->ss_family != 0) {
-					return (proxySendViaCluster(*(reinterpret_cast<const InetAddress *>(localAddr)),*(reinterpret_cast<const InetAddress *>(addr)),data,len,ttl)) ? 0 : -1;
-				} else {
-					return -1; // failure
-				}
-				break;
-
-			case 0: // failure
-				return -1;
-
-			default: // success
-				return 0;
+		if ((localSocket != 0)&&(localSocket != -1)) {
+			if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),ttl);
+			const bool r = _phy.udpSend((PhySocket *)((uintptr_t)localSocket),(const struct sockaddr *)addr,data,len);
+			if ((ttl)&&(addr->ss_family == AF_INET)) _phy.setIp4UdpTtl((PhySocket *)((uintptr_t)localSocket),255);
+			return ((r) ? 0 : -1);
+		} else {
+			return ((_binder.udpSendAll(_phy,addr,data,len,ttl)) ? 0 : -1);
 		}
 		}
 	}
 	}
 
 
@@ -2671,7 +2196,7 @@ public:
 		n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
 		n->tap->put(MAC(sourceMac),MAC(destMac),etherType,data,len);
 	}
 	}
 
 
-	inline int nodePathCheckFunction(uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
+	inline int nodePathCheckFunction(uint64_t ztaddr,const int64_t localSocket,const struct sockaddr_storage *remoteAddr)
 	{
 	{
 		// Make sure we're not trying to do ZeroTier-over-ZeroTier
 		// Make sure we're not trying to do ZeroTier-over-ZeroTier
 		{
 		{
@@ -2882,12 +2407,12 @@ static void SnodeStatePutFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_St
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); }
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeStatePutFunction(type,id,data,len); }
 static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
 static int SnodeStateGetFunction(ZT_Node *node,void *uptr,void *tptr,enum ZT_StateObjectType type,const uint64_t id[2],void *data,unsigned int maxlen)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); }
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeStateGetFunction(type,id,data,maxlen); }
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localAddr,addr,data,len,ttl); }
+static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,void *tptr,int64_t localSocket,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodeWirePacketSendFunction(localSocket,addr,data,len,ttl); }
 static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,uint64_t sourceMac,uint64_t destMac,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); }
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkFrameFunction(nwid,nuptr,sourceMac,destMac,etherType,vlanId,data,len); }
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
-{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); }
+static int SnodePathCheckFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int64_t localSocket,const struct sockaddr_storage *remoteAddr)
+{ return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localSocket,remoteAddr); }
 static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
 static int SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathLookupFunction(ztaddr,family,result); }
 static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 static void StapFrameHandler(void *uptr,void *tptr,uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)