瀏覽代碼

Add thread PTR that gets passed through the entire ZT core call stack and then passed to handler functions resulting from a call.

Adam Ierymenko 8 年之前
父節點
當前提交
e4896b257f

+ 1 - 1
controller/EmbeddedNetworkController.cpp

@@ -790,7 +790,7 @@ unsigned int EmbeddedNetworkController::handleControlPlaneHttpPOST(
 					test->timestamp = OSUtils::now();
 
 					if (_node) {
-						_node->circuitTestBegin(test,&(EmbeddedNetworkController::_circuitTestCallback));
+						_node->circuitTestBegin((void *)0,test,&(EmbeddedNetworkController::_circuitTestCallback));
 					} else {
 						_tests.pop_back();
 						return 500;

+ 36 - 15
include/ZeroTierOne.h

@@ -1408,6 +1408,7 @@ typedef void ZT_Node;
 typedef int (*ZT_VirtualNetworkConfigFunction)(
 	ZT_Node *,                             /* Node */
 	void *,                                /* User ptr */
+	void *,                                /* Thread ptr */
 	uint64_t,                              /* Network ID */
 	void **,                               /* Modifiable network user PTR */
 	enum ZT_VirtualNetworkConfigOperation, /* Config operation */
@@ -1423,6 +1424,7 @@ typedef int (*ZT_VirtualNetworkConfigFunction)(
 typedef void (*ZT_VirtualNetworkFrameFunction)(
 	ZT_Node *,                             /* Node */
 	void *,                                /* User ptr */
+	void *,                                /* Thread ptr */
 	uint64_t,                              /* Network ID */
 	void **,                               /* Modifiable network user PTR */
 	uint64_t,                              /* Source MAC */
@@ -1442,10 +1444,11 @@ typedef void (*ZT_VirtualNetworkFrameFunction)(
  * in the definition of ZT_Event.
  */
 typedef void (*ZT_EventCallback)(
-	ZT_Node *,
-	void *,
-	enum ZT_Event,
-	const void *);
+	ZT_Node *,                             /* Node */
+	void *,                                /* User ptr */
+	void *,                                /* Thread ptr */
+	enum ZT_Event,                         /* Event type */
+	const void *);                         /* Event payload (if applicable) */
 
 /**
  * Function to get an object from the data store
@@ -1468,8 +1471,9 @@ typedef void (*ZT_EventCallback)(
  * object.
  */
 typedef long (*ZT_DataStoreGetFunction)(
-	ZT_Node *,
-	void *,
+	ZT_Node *,                             /* Node */
+	void *,                                /* User ptr */
+	void *,                                /* Thread ptr */
 	const char *,
 	void *,
 	unsigned long,
@@ -1495,6 +1499,7 @@ typedef long (*ZT_DataStoreGetFunction)(
 typedef int (*ZT_DataStorePutFunction)(
 	ZT_Node *,
 	void *,
+	void *,                                /* Thread ptr */
 	const char *,
 	const void *,
 	unsigned long,
@@ -1529,6 +1534,7 @@ typedef int (*ZT_DataStorePutFunction)(
 typedef int (*ZT_WirePacketSendFunction)(
 	ZT_Node *,                        /* Node */
 	void *,                           /* User ptr */
+	void *,                           /* Thread ptr */
 	const struct sockaddr_storage *,  /* Local address */
 	const struct sockaddr_storage *,  /* Remote address */
 	const void *,                     /* Packet data */
@@ -1562,6 +1568,7 @@ typedef int (*ZT_WirePacketSendFunction)(
 typedef int (*ZT_PathCheckFunction)(
 	ZT_Node *,                        /* Node */
 	void *,                           /* User ptr */
+	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address */
 	const struct sockaddr_storage *,  /* Local address */
 	const struct sockaddr_storage *); /* Remote address */
@@ -1584,6 +1591,7 @@ typedef int (*ZT_PathCheckFunction)(
 typedef int (*ZT_PathLookupFunction)(
 	ZT_Node *,                        /* Node */
 	void *,                           /* User ptr */
+	void *,                           /* Thread ptr */
 	uint64_t,                         /* ZeroTier address (40 bits) */
 	int,                              /* Desired ss_family or -1 for any */
 	struct sockaddr_storage *);       /* Result buffer */
@@ -1654,11 +1662,12 @@ struct ZT_Node_Callbacks
  *
  * @param node Result: pointer is set to new node instance on success
  * @param uptr User pointer to pass to functions/callbacks
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param callbacks Callback function configuration
  * @param now Current clock in milliseconds
  * @return OK (0) or error code if a fatal error condition has occurred
  */
-enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
+enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
 
 /**
  * Delete a node and free all resources it consumes
@@ -1674,6 +1683,7 @@ void ZT_Node_delete(ZT_Node *node);
  * Process a packet received from the physical wire
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param now Current clock in milliseconds
  * @param localAddress Local address, or point to ZT_SOCKADDR_NULL if unspecified
  * @param remoteAddress Origin of packet
@@ -1684,6 +1694,7 @@ void ZT_Node_delete(ZT_Node *node);
  */
 enum ZT_ResultCode ZT_Node_processWirePacket(
 	ZT_Node *node,
+	void *tptr,
 	uint64_t now,
 	const struct sockaddr_storage *localAddress,
 	const struct sockaddr_storage *remoteAddress,
@@ -1695,6 +1706,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
  * Process a frame from a virtual network port (tap)
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param now Current clock in milliseconds
  * @param nwid ZeroTier 64-bit virtual network ID
  * @param sourceMac Source MAC address (least significant 48 bits)
@@ -1708,6 +1720,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
  */
 enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
 	ZT_Node *node,
+	void *tptr,
 	uint64_t now,
 	uint64_t nwid,
 	uint64_t sourceMac,
@@ -1722,11 +1735,12 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
  * Perform periodic background operations
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param now Current clock in milliseconds
  * @param nextBackgroundTaskDeadline Value/result: set to deadline for next call to processBackgroundTasks()
  * @return OK (0) or error code if a fatal error condition has occurred
  */
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
+enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
 
 /**
  * Join a network
@@ -1742,7 +1756,7 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
  * @param uptr An arbitrary pointer to associate with this network (default: NULL)
  * @return OK (0) or error code if a fatal error condition has occurred
  */
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr);
+enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr);
 
 /**
  * Leave a network
@@ -1759,7 +1773,7 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr);
  * @param uptr Target pointer is set to uptr (if not NULL)
  * @return OK (0) or error code if a fatal error condition has occurred
  */
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr);
+enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr);
 
 /**
  * Subscribe to an Ethernet multicast group
@@ -1781,12 +1795,13 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr);
  * This does not generate an update call to networkConfigCallback().
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param nwid 64-bit network ID
  * @param multicastGroup Ethernet multicast or broadcast MAC (least significant 48 bits)
  * @param multicastAdi Multicast ADI (least significant 32 bits only, use 0 if not needed)
  * @return OK (0) or error code if a fatal error condition has occurred
  */
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
 
 /**
  * Unsubscribe from an Ethernet multicast group (or all groups)
@@ -1811,21 +1826,24 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
  * across invocations if the contents of moon.d are scanned and orbit is
  * called for each on startup.
  *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param moonWorldId Moon's world ID
  * @param moonSeed If non-zero, the ZeroTier address of any member of the moon to query for moon definition
  * @param len Length of moonWorld in bytes
  * @return Error if moon was invalid or failed to be added
  */
-enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,uint64_t moonWorldId,uint64_t moonSeed);
+enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
 
 /**
  * Remove a moon (does nothing if not present)
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param moonWorldId World ID of moon to remove
  * @return Error if anything bad happened
  */
-enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,uint64_t moonWorldId);
+enum ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId);
 
 /**
  * Get this node's 40-bit ZeroTier address
@@ -1919,13 +1937,15 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node);
  * There is no delivery guarantee here. Failure can occur if the message is
  * too large or if dest is not a valid ZeroTier address.
  *
+ * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param dest Destination ZeroTier address
  * @param typeId VERB_USER_MESSAGE type ID
  * @param data Payload data to attach to user message
  * @param len Length of data in bytes
  * @return Boolean: non-zero on success, zero on failure
  */
-int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
+int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
 
 /**
  * Set a network configuration master instance for this node
@@ -1957,11 +1977,12 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkConfigMasterInstance);
  * for results forever.
  *
  * @param node Node instance
+ * @param tptr Thread pointer to pass to functions/callbacks resulting from this call
  * @param test Test configuration
  * @param reportCallback Function to call each time a report is received
  * @return OK or error if, for example, test is too big for a packet or support isn't compiled in
  */
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *, ZT_CircuitTest *,const ZT_CircuitTestReport *));
+enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *, ZT_CircuitTest *,const ZT_CircuitTestReport *));
 
 /**
  * Stop listening for results to a given circuit test

+ 3 - 3
node/Capability.cpp

@@ -25,7 +25,7 @@
 
 namespace ZeroTier {
 
-int Capability::verify(const RuntimeEnvironment *RR) const
+int Capability::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	try {
 		// There must be at least one entry, and sanity check for bad chain max length
@@ -46,12 +46,12 @@ int Capability::verify(const RuntimeEnvironment *RR) const
 					return -1; // otherwise if we have another entry it must be from the previous holder in the chain
 			}
 
-			const Identity id(RR->topology->getIdentity(_custody[c].from));
+			const Identity id(RR->topology->getIdentity(tPtr,_custody[c].from));
 			if (id) {
 				if (!id.verify(tmp.data(),tmp.size(),_custody[c].signature))
 					return -1;
 			} else {
-				RR->sw->requestWhois(_custody[c].from);
+				RR->sw->requestWhois(tPtr,_custody[c].from);
 				return 1;
 			}
 		}

+ 1 - 1
node/Capability.hpp

@@ -161,7 +161,7 @@ public:
 	 * @param RR Runtime environment to provide for peer lookup, etc.
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
 	 */
-	int verify(const RuntimeEnvironment *RR) const;
+	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
 
 	template<unsigned int C>
 	static inline void serializeRules(Buffer<C> &b,const ZT_VirtualNetworkRule *rules,unsigned int ruleCount)

+ 3 - 3
node/CertificateOfMembership.cpp

@@ -207,14 +207,14 @@ bool CertificateOfMembership::sign(const Identity &with)
 	}
 }
 
-int CertificateOfMembership::verify(const RuntimeEnvironment *RR) const
+int CertificateOfMembership::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(networkId()))||(_qualifierCount > ZT_NETWORK_COM_MAX_QUALIFIERS))
 		return -1;
 
-	const Identity id(RR->topology->getIdentity(_signedBy));
+	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
 	if (!id) {
-		RR->sw->requestWhois(_signedBy);
+		RR->sw->requestWhois(tPtr,_signedBy);
 		return 1;
 	}
 

+ 2 - 1
node/CertificateOfMembership.hpp

@@ -250,9 +250,10 @@ public:
 	 * Verify this COM and its signature
 	 *
 	 * @param RR Runtime environment for looking up peers
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or credential
 	 */
-	int verify(const RuntimeEnvironment *RR) const;
+	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
 
 	/**
 	 * @return True if signed

+ 3 - 3
node/CertificateOfOwnership.cpp

@@ -25,13 +25,13 @@
 
 namespace ZeroTier {
 
-int CertificateOfOwnership::verify(const RuntimeEnvironment *RR) const
+int CertificateOfOwnership::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
 		return -1;
-	const Identity id(RR->topology->getIdentity(_signedBy));
+	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
 	if (!id) {
-		RR->sw->requestWhois(_signedBy);
+		RR->sw->requestWhois(tPtr,_signedBy);
 		return 1;
 	}
 	try {

+ 2 - 1
node/CertificateOfOwnership.hpp

@@ -137,9 +137,10 @@ public:
 
 	/**
 	 * @param RR Runtime environment to allow identity lookup for signedBy
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature
 	 */
-	int verify(const RuntimeEnvironment *RR) const;
+	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const

+ 147 - 147
node/IncomingPacket.cpp

@@ -44,7 +44,7 @@
 
 namespace ZeroTier {
 
-bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
+bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR,void *tPtr)
 {
 	const Address sourceAddress(source());
 
@@ -65,10 +65,10 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
 			}
 		} else if ((c == ZT_PROTO_CIPHER_SUITE__C25519_POLY1305_NONE)&&(verb() == Packet::VERB_HELLO)) {
 			// Only HELLO is allowed in the clear, but will still have a MAC
-			return _doHELLO(RR,false);
+			return _doHELLO(RR,tPtr,false);
 		}
 
-		const SharedPtr<Peer> peer(RR->topology->getPeer(sourceAddress));
+		const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,sourceAddress));
 		if (peer) {
 			if (!trusted) {
 				if (!dearmor(peer->key())) {
@@ -89,30 +89,30 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
 			switch(v) {
 				//case Packet::VERB_NOP:
 				default: // ignore unknown verbs, but if they pass auth check they are "received"
-					peer->received(_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
+					peer->received(tPtr,_path,hops(),packetId(),v,0,Packet::VERB_NOP,false);
 					return true;
 
-				case Packet::VERB_HELLO:                      return _doHELLO(RR,true);
-				case Packet::VERB_ERROR:                      return _doERROR(RR,peer);
-				case Packet::VERB_OK:                         return _doOK(RR,peer);
-				case Packet::VERB_WHOIS:                      return _doWHOIS(RR,peer);
-				case Packet::VERB_RENDEZVOUS:                 return _doRENDEZVOUS(RR,peer);
-				case Packet::VERB_FRAME:                      return _doFRAME(RR,peer);
-				case Packet::VERB_EXT_FRAME:                  return _doEXT_FRAME(RR,peer);
-				case Packet::VERB_ECHO:                       return _doECHO(RR,peer);
-				case Packet::VERB_MULTICAST_LIKE:             return _doMULTICAST_LIKE(RR,peer);
-				case Packet::VERB_NETWORK_CREDENTIALS:        return _doNETWORK_CREDENTIALS(RR,peer);
-				case Packet::VERB_NETWORK_CONFIG_REQUEST:     return _doNETWORK_CONFIG_REQUEST(RR,peer);
-				case Packet::VERB_NETWORK_CONFIG:             return _doNETWORK_CONFIG(RR,peer);
-				case Packet::VERB_MULTICAST_GATHER:           return _doMULTICAST_GATHER(RR,peer);
-				case Packet::VERB_MULTICAST_FRAME:            return _doMULTICAST_FRAME(RR,peer);
-				case Packet::VERB_PUSH_DIRECT_PATHS:          return _doPUSH_DIRECT_PATHS(RR,peer);
-				case Packet::VERB_CIRCUIT_TEST:               return _doCIRCUIT_TEST(RR,peer);
-				case Packet::VERB_CIRCUIT_TEST_REPORT:        return _doCIRCUIT_TEST_REPORT(RR,peer);
-				case Packet::VERB_USER_MESSAGE:               return _doUSER_MESSAGE(RR,peer);
+				case Packet::VERB_HELLO:                      return _doHELLO(RR,tPtr,true);
+				case Packet::VERB_ERROR:                      return _doERROR(RR,tPtr,peer);
+				case Packet::VERB_OK:                         return _doOK(RR,tPtr,peer);
+				case Packet::VERB_WHOIS:                      return _doWHOIS(RR,tPtr,peer);
+				case Packet::VERB_RENDEZVOUS:                 return _doRENDEZVOUS(RR,tPtr,peer);
+				case Packet::VERB_FRAME:                      return _doFRAME(RR,tPtr,peer);
+				case Packet::VERB_EXT_FRAME:                  return _doEXT_FRAME(RR,tPtr,peer);
+				case Packet::VERB_ECHO:                       return _doECHO(RR,tPtr,peer);
+				case Packet::VERB_MULTICAST_LIKE:             return _doMULTICAST_LIKE(RR,tPtr,peer);
+				case Packet::VERB_NETWORK_CREDENTIALS:        return _doNETWORK_CREDENTIALS(RR,tPtr,peer);
+				case Packet::VERB_NETWORK_CONFIG_REQUEST:     return _doNETWORK_CONFIG_REQUEST(RR,tPtr,peer);
+				case Packet::VERB_NETWORK_CONFIG:             return _doNETWORK_CONFIG(RR,tPtr,peer);
+				case Packet::VERB_MULTICAST_GATHER:           return _doMULTICAST_GATHER(RR,tPtr,peer);
+				case Packet::VERB_MULTICAST_FRAME:            return _doMULTICAST_FRAME(RR,tPtr,peer);
+				case Packet::VERB_PUSH_DIRECT_PATHS:          return _doPUSH_DIRECT_PATHS(RR,tPtr,peer);
+				case Packet::VERB_CIRCUIT_TEST:               return _doCIRCUIT_TEST(RR,tPtr,peer);
+				case Packet::VERB_CIRCUIT_TEST_REPORT:        return _doCIRCUIT_TEST_REPORT(RR,tPtr,peer);
+				case Packet::VERB_USER_MESSAGE:               return _doUSER_MESSAGE(RR,tPtr,peer);
 			}
 		} else {
-			RR->sw->requestWhois(sourceAddress);
+			RR->sw->requestWhois(tPtr,sourceAddress);
 			return false;
 		}
 	} catch ( ... ) {
@@ -123,7 +123,7 @@ bool IncomingPacket::tryDecode(const RuntimeEnvironment *RR)
 	}
 }
 
-bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_ERROR_IDX_IN_RE_VERB];
@@ -163,7 +163,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 			case Packet::ERROR_IDENTITY_COLLISION:
 				// FIXME: for federation this will need a payload with a signature or something.
 				if (RR->topology->isUpstream(peer->identity()))
-					RR->node->postEvent(ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
+					RR->node->postEvent(tPtr,ZT_EVENT_FATAL_ERROR_IDENTITY_COLLISION);
 				break;
 
 			case Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE: {
@@ -171,7 +171,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
 				const uint64_t now = RR->node->now();
 				if ( (network) && (network->config().com) && (peer->rateGateIncomingComRequest(now)) )
-					network->pushCredentialsNow(peer->address(),now);
+					network->pushCredentialsNow(tPtr,peer->address(),now);
 			}	break;
 
 			case Packet::ERROR_NETWORK_ACCESS_DENIED_: {
@@ -185,7 +185,7 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 				// Members of networks can use this error to indicate that they no longer
 				// want to receive multicasts on a given channel.
 				const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD)));
-				if ((network)&&(network->gate(peer))) {
+				if ((network)&&(network->gate(tPtr,peer))) {
 					const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 8,6),6),at<uint32_t>(ZT_PROTO_VERB_ERROR_IDX_PAYLOAD + 14));
 					TRACE("%.16llx: peer %s unsubscrubed from multicast group %s",network->id(),peer->address().toString().c_str(),mg.toString().c_str());
 					RR->mc->remove(network->id(),mg,peer->address());
@@ -195,14 +195,14 @@ bool IncomingPacket::_doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 			default: break;
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_ERROR,inRePacketId,inReVerb,false);
 	} catch ( ... ) {
 		TRACE("dropped ERROR from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated)
+bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated)
 {
 	try {
 		const uint64_t now = RR->node->now();
@@ -226,7 +226,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
 			return true;
 		}
 
-		SharedPtr<Peer> peer(RR->topology->getPeer(id.address()));
+		SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,id.address()));
 		if (peer) {
 			// We already have an identity with this address -- check for collisions
 			if (!alreadyAuthenticated) {
@@ -246,7 +246,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
 							outp.append((uint64_t)pid);
 							outp.append((uint8_t)Packet::ERROR_IDENTITY_COLLISION);
 							outp.armor(key,true,_path->nextOutgoingCounter());
-							_path->send(RR,outp.data(),outp.size(),RR->node->now());
+							_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 						} else {
 							TRACE("rejected HELLO from %s(%s): packet failed authentication",id.address().toString().c_str(),_path->address().toString().c_str());
 						}
@@ -292,7 +292,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
 				return true;
 			}
 
-			peer = RR->topology->addPeer(newPeer);
+			peer = RR->topology->addPeer(tPtr,newPeer);
 
 			// Continue at // VALID
 		}
@@ -304,7 +304,7 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
 		if (ptr < size()) {
 			ptr += externalSurfaceAddress.deserialize(*this,ptr);
 			if ((externalSurfaceAddress)&&(hops() == 0))
-				RR->sa->iam(id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
+				RR->sa->iam(tPtr,id.address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(id),now);
 		}
 
 		// Get primary planet world ID and world timestamp if present
@@ -408,17 +408,17 @@ bool IncomingPacket::_doHELLO(const RuntimeEnvironment *RR,const bool alreadyAut
 		outp.setAt(corSizeAt,(uint16_t)(outp.size() - (corSizeAt + 2)));
 
 		outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-		_path->send(RR,outp.data(),outp.size(),now);
+		_path->send(RR,tPtr,outp.data(),outp.size(),now);
 
 		peer->setRemoteVersion(protoVersion,vMajor,vMinor,vRevision); // important for this to go first so received() knows the version
-		peer->received(_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),pid,Packet::VERB_HELLO,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped HELLO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const Packet::Verb inReVerb = (Packet::Verb)(*this)[ZT_PROTO_VERB_OK_IDX_IN_RE_VERB];
@@ -463,7 +463,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 						while (ptr < endOfWorlds) {
 							World w;
 							ptr += w.deserialize(*this,ptr);
-							RR->topology->addWorld(w,false);
+							RR->topology->addWorld(tPtr,w,false);
 						}
 					} else {
 						ptr += worldsLen;
@@ -490,20 +490,20 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 				peer->setRemoteVersion(vProto,vMajor,vMinor,vRevision);
 
 				if ((externalSurfaceAddress)&&(hops() == 0))
-					RR->sa->iam(peer->address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
+					RR->sa->iam(tPtr,peer->address(),_path->localAddress(),_path->address(),externalSurfaceAddress,RR->topology->isUpstream(peer->identity()),RR->node->now());
 			}	break;
 
 			case Packet::VERB_WHOIS:
 				if (RR->topology->isUpstream(peer->identity())) {
 					const Identity id(*this,ZT_PROTO_VERB_WHOIS__OK__IDX_IDENTITY);
-					RR->sw->doAnythingWaitingForPeer(RR->topology->addPeer(SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
+					RR->sw->doAnythingWaitingForPeer(tPtr,RR->topology->addPeer(tPtr,SharedPtr<Peer>(new Peer(RR,RR->identity,id))));
 				}
 				break;
 
 			case Packet::VERB_NETWORK_CONFIG_REQUEST: {
 				const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PROTO_VERB_OK_IDX_PAYLOAD)));
 				if (network)
-					network->handleConfigChunk(packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
+					network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PROTO_VERB_OK_IDX_PAYLOAD);
 			}	break;
 
 			case Packet::VERB_MULTICAST_GATHER: {
@@ -513,7 +513,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 					const MulticastGroup mg(MAC(field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_MAC,6),6),at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_ADI));
 					//TRACE("%s(%s): OK(MULTICAST_GATHER) %.16llx/%s length %u",source().toString().c_str(),_path->address().toString().c_str(),nwid,mg.toString().c_str(),size());
 					const unsigned int count = at<uint16_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 4);
-					RR->mc->addMultiple(RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
+					RR->mc->addMultiple(tPtr,RR->node->now(),nwid,mg,field(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS + 6,count * 5),count,at<uint32_t>(ZT_PROTO_VERB_MULTICAST_GATHER__OK__IDX_GATHER_RESULTS));
 				}
 			}	break;
 
@@ -532,7 +532,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 						CertificateOfMembership com;
 						offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS);
 						if (com)
-							network->addCredential(com);
+							network->addCredential(tPtr,com);
 					}
 
 					if ((flags & 0x02) != 0) {
@@ -540,7 +540,7 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 						offset += ZT_PROTO_VERB_MULTICAST_FRAME__OK__IDX_COM_AND_GATHER_RESULTS;
 						unsigned int totalKnown = at<uint32_t>(offset); offset += 4;
 						unsigned int count = at<uint16_t>(offset); offset += 2;
-						RR->mc->addMultiple(RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
+						RR->mc->addMultiple(tPtr,RR->node->now(),nwid,mg,field(offset,count * 5),count,totalKnown);
 					}
 				}
 			}	break;
@@ -548,14 +548,14 @@ bool IncomingPacket::_doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &p
 			default: break;
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_OK,inRePacketId,inReVerb,false);
 	} catch ( ... ) {
 		TRACE("dropped OK from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		if ((!RR->topology->amRoot())&&(!peer->rateGateInboundWhoisRequest(RR->node->now()))) {
@@ -573,13 +573,13 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 			const Address addr(field(ptr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 			ptr += ZT_ADDRESS_LENGTH;
 
-			const Identity id(RR->topology->getIdentity(addr));
+			const Identity id(RR->topology->getIdentity(tPtr,addr));
 			if (id) {
 				id.serialize(outp,false);
 				++count;
 			} else {
 				// Request unknown WHOIS from upstream from us (if we have one)
-				RR->sw->requestWhois(addr);
+				RR->sw->requestWhois(tPtr,addr);
 #ifdef ZT_ENABLE_CLUSTER
 				// Distribute WHOIS queries across a cluster if we do not know the ID.
 				// This may result in duplicate OKs to the querying peer, which is fine.
@@ -591,32 +591,32 @@ bool IncomingPacket::_doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 
 		if (count > 0) {
 			outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-			_path->send(RR,outp.data(),outp.size(),RR->node->now());
+			_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_WHOIS,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped WHOIS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		if (!RR->topology->isUpstream(peer->identity())) {
 			TRACE("RENDEZVOUS from %s ignored since source is not upstream",peer->address().toString().c_str());
 		} else {
 			const Address with(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ZTADDRESS,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
-			const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(with));
+			const SharedPtr<Peer> rendezvousWith(RR->topology->getPeer(tPtr,with));
 			if (rendezvousWith) {
 				const unsigned int port = at<uint16_t>(ZT_PROTO_VERB_RENDEZVOUS_IDX_PORT);
 				const unsigned int addrlen = (*this)[ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRLEN];
 				if ((port > 0)&&((addrlen == 4)||(addrlen == 16))) {
 					const InetAddress atAddr(field(ZT_PROTO_VERB_RENDEZVOUS_IDX_ADDRESS,addrlen),addrlen,port);
-					if (RR->node->shouldUsePathForZeroTierTraffic(with,_path->localAddress(),atAddr)) {
-						RR->node->putPacket(_path->localAddress(),atAddr,"ABRE",4,2); // send low-TTL junk packet to 'open' local NAT(s) and stateful firewalls
-						rendezvousWith->attemptToContactAt(_path->localAddress(),atAddr,RR->node->now(),false,0);
+					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);
 						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 {
 						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());
@@ -628,46 +628,46 @@ bool IncomingPacket::_doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<
 				TRACE("ignored RENDEZVOUS from %s(%s) to meet unknown peer %s",peer->address().toString().c_str(),_path->address().toString().c_str(),with.toString().c_str());
 			}
 		}
-		peer->received(_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_RENDEZVOUS,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped RENDEZVOUS from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID);
 		const SharedPtr<Network> network(RR->node->network(nwid));
 		bool trustEstablished = false;
 		if (network) {
-			if (network->gate(peer)) {
+			if (network->gate(tPtr,peer)) {
 				trustEstablished = true;
 				if (size() > ZT_PROTO_VERB_FRAME_IDX_PAYLOAD) {
 					const unsigned int etherType = at<uint16_t>(ZT_PROTO_VERB_FRAME_IDX_ETHERTYPE);
 					const MAC sourceMac(peer->address(),nwid);
 					const unsigned int frameLen = size() - ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
 					const uint8_t *const frameData = reinterpret_cast<const uint8_t *>(data()) + ZT_PROTO_VERB_FRAME_IDX_PAYLOAD;
-					if (network->filterIncomingPacket(peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
-						RR->node->putFrame(nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
+					if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),sourceMac,network->mac(),frameData,frameLen,etherType,0) > 0)
+						RR->node->putFrame(tPtr,nwid,network->userPtr(),sourceMac,network->mac(),etherType,0,(const void *)frameData,frameLen);
 				}
 			} else {
 				TRACE("dropped FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
-				_sendErrorNeedCredentials(RR,peer,nwid);
+				_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 			}
 		} else {
 			TRACE("dropped FRAME from %s(%s): we are not a member of network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
-			_sendErrorNeedCredentials(RR,peer,nwid);
+			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 		}
-		peer->received(_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_FRAME,0,Packet::VERB_NOP,trustEstablished);
 	} catch ( ... ) {
 		TRACE("dropped FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_EXT_FRAME_IDX_NETWORK_ID);
@@ -680,13 +680,13 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 				CertificateOfMembership com;
 				comLen = com.deserialize(*this,ZT_PROTO_VERB_EXT_FRAME_IDX_COM);
 				if (com)
-					network->addCredential(com);
+					network->addCredential(tPtr,com);
 			}
 
-			if (!network->gate(peer)) {
+			if (!network->gate(tPtr,peer)) {
 				TRACE("dropped EXT_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),network->id());
-				_sendErrorNeedCredentials(RR,peer,nwid);
-				peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
+				_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
 				return true;
 			}
 
@@ -699,36 +699,36 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 
 				if ((!from)||(from.isMulticast())||(from == network->mac())) {
 					TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: invalid source MAC %s",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),from.toString().c_str());
-					peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+					peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 					return true;
 				}
 
-				switch (network->filterIncomingPacket(peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
+				switch (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to,frameData,frameLen,etherType,0)) {
 					case 1:
 						if (from != MAC(peer->address(),nwid)) {
 							if (network->config().permitsBridging(peer->address())) {
 								network->learnBridgeRoute(from,peer->address());
 							} else {
 								TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
-								peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+								peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 								return true;
 							}
 						} else if (to != network->mac()) {
 							if (to.isMulticast()) {
 								if (network->config().multicastLimit == 0) {
 									TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: network %.16llx does not allow multicast",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
-									peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+									peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 									return true;
 								}
 							} else if (!network->config().permitsBridging(RR->identity.address())) {
 								TRACE("dropped EXT_FRAME from %s@%s(%s) to %s: I cannot bridge to %.16llx or bridging disabled on network",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
-								peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+								peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 								return true;
 							}
 						}
 						// fall through -- 2 means accept regardless of bridging checks or other restrictions
 					case 2:
-						RR->node->putFrame(nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
+						RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to,etherType,0,(const void *)frameData,frameLen);
 						break;
 				}
 			}
@@ -739,14 +739,14 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 				outp.append((uint64_t)packetId());
 				outp.append((uint64_t)nwid);
 				outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-				_path->send(RR,outp.data(),outp.size(),RR->node->now());
+				_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 			}
 
-			peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,true);
 		} else {
 			TRACE("dropped EXT_FRAME from %s(%s): we are not connected to network %.16llx",source().toString().c_str(),_path->address().toString().c_str(),at<uint64_t>(ZT_PROTO_VERB_FRAME_IDX_NETWORK_ID));
-			_sendErrorNeedCredentials(RR,peer,nwid);
-			peer->received(_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
+			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_EXT_FRAME,0,Packet::VERB_NOP,false);
 		}
 	} catch ( ... ) {
 		TRACE("dropped EXT_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
@@ -754,7 +754,7 @@ bool IncomingPacket::_doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<P
 	return true;
 }
 
-bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		if (!peer->rateGateEchoRequest(RR->node->now())) {
@@ -769,16 +769,16 @@ bool IncomingPacket::_doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer>
 		if (size() > ZT_PACKET_IDX_PAYLOAD)
 			outp.append(reinterpret_cast<const unsigned char *>(data()) + ZT_PACKET_IDX_PAYLOAD,size() - ZT_PACKET_IDX_PAYLOAD);
 		outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-		_path->send(RR,outp.data(),outp.size(),RR->node->now());
+		_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 
-		peer->received(_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),pid,Packet::VERB_ECHO,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped ECHO from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t now = RR->node->now();
@@ -802,9 +802,9 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
 			if (!auth) {
 				if ((!network)||(network->id() != nwid))
 					network = RR->node->network(nwid);
-				const bool authOnNet = ((network)&&(network->gate(peer)));
+				const bool authOnNet = ((network)&&(network->gate(tPtr,peer)));
 				if (!authOnNet)
-					_sendErrorNeedCredentials(RR,peer,nwid);
+					_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 				trustEstablished |= authOnNet;
 				if (authOnNet||RR->mc->cacheAuthorized(peer->address(),nwid,now)) {
 					auth = true;
@@ -815,18 +815,18 @@ bool IncomingPacket::_doMULTICAST_LIKE(const RuntimeEnvironment *RR,const Shared
 
 			if (auth) {
 				const MulticastGroup group(MAC(field(ptr + 8,6),6),at<uint32_t>(ptr + 14));
-				RR->mc->add(now,nwid,group,peer->address());
+				RR->mc->add(tPtr,now,nwid,group,peer->address());
 			}
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_LIKE,0,Packet::VERB_NOP,trustEstablished);
 	} catch ( ... ) {
 		TRACE("dropped MULTICAST_LIKE from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		if (!peer->rateGateCredentialsReceived(RR->node->now())) {
@@ -847,7 +847,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 			if (com) {
 				const SharedPtr<Network> network(RR->node->network(com.networkId()));
 				if (network) {
-					switch (network->addCredential(com)) {
+					switch (network->addCredential(tPtr,com)) {
 						case Membership::ADD_REJECTED:
 							break;
 						case Membership::ADD_ACCEPTED_NEW:
@@ -857,7 +857,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 						case Membership::ADD_DEFERRED_FOR_WHOIS:
 							return false;
 					}
-				} else RR->mc->addCredential(com,false);
+				} else RR->mc->addCredential(tPtr,com,false);
 			}
 		}
 		++p; // skip trailing 0 after COMs if present
@@ -868,7 +868,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 				p += cap.deserialize(*this,p);
 				const SharedPtr<Network> network(RR->node->network(cap.networkId()));
 				if (network) {
-					switch (network->addCredential(cap)) {
+					switch (network->addCredential(tPtr,cap)) {
 						case Membership::ADD_REJECTED:
 							break;
 						case Membership::ADD_ACCEPTED_NEW:
@@ -888,7 +888,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 				p += tag.deserialize(*this,p);
 				const SharedPtr<Network> network(RR->node->network(tag.networkId()));
 				if (network) {
-					switch (network->addCredential(tag)) {
+					switch (network->addCredential(tPtr,tag)) {
 						case Membership::ADD_REJECTED:
 							break;
 						case Membership::ADD_ACCEPTED_NEW:
@@ -908,7 +908,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 				p += revocation.deserialize(*this,p);
 				const SharedPtr<Network> network(RR->node->network(revocation.networkId()));
 				if (network) {
-					switch(network->addCredential(peer->address(),revocation)) {
+					switch(network->addCredential(tPtr,peer->address(),revocation)) {
 						case Membership::ADD_REJECTED:
 							break;
 						case Membership::ADD_ACCEPTED_NEW:
@@ -928,7 +928,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 				p += coo.deserialize(*this,p);
 				const SharedPtr<Network> network(RR->node->network(coo.networkId()));
 				if (network) {
-					switch(network->addCredential(coo)) {
+					switch(network->addCredential(tPtr,coo)) {
 						case Membership::ADD_REJECTED:
 							break;
 						case Membership::ADD_ACCEPTED_NEW:
@@ -942,7 +942,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 			}
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CREDENTIALS,0,Packet::VERB_NOP,trustEstablished);
 	} catch (std::exception &exc) {
 		//fprintf(stderr,"dropped NETWORK_CREDENTIALS from %s(%s): %s" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str(),exc.what());
 		TRACE("dropped NETWORK_CREDENTIALS from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
@@ -953,7 +953,7 @@ bool IncomingPacket::_doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const S
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_NETWORK_CONFIG_REQUEST_IDX_NETWORK_ID);
@@ -972,10 +972,10 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
 			outp.append((unsigned char)Packet::ERROR_UNSUPPORTED_OPERATION);
 			outp.append(nwid);
 			outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-			_path->send(RR,outp.data(),outp.size(),RR->node->now());
+			_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 		}
 
-		peer->received(_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hopCount,requestPacketId,Packet::VERB_NETWORK_CONFIG_REQUEST,0,Packet::VERB_NOP,false);
 	} catch (std::exception &exc) {
 		//fprintf(stderr,"dropped NETWORK_CONFIG_REQUEST from %s(%s): %s" ZT_EOL_S,source().toString().c_str(),_path->address().toString().c_str(),exc.what());
 		TRACE("dropped NETWORK_CONFIG_REQUEST from %s(%s): %s",source().toString().c_str(),_path->address().toString().c_str(),exc.what());
@@ -986,12 +986,12 @@ bool IncomingPacket::_doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,cons
 	return true;
 }
 
-bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const SharedPtr<Network> network(RR->node->network(at<uint64_t>(ZT_PACKET_IDX_PAYLOAD)));
 		if (network) {
-			const uint64_t configUpdateId = network->handleConfigChunk(packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
+			const uint64_t configUpdateId = network->handleConfigChunk(tPtr,packetId(),source(),*this,ZT_PACKET_IDX_PAYLOAD);
 			if (configUpdateId) {
 				Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 				outp.append((uint8_t)Packet::VERB_ECHO);
@@ -999,17 +999,17 @@ bool IncomingPacket::_doNETWORK_CONFIG(const RuntimeEnvironment *RR,const Shared
 				outp.append((uint64_t)network->id());
 				outp.append((uint64_t)configUpdateId);
 				outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-				_path->send(RR,outp.data(),outp.size(),RR->node->now());
+				_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 			}
 		}
-		peer->received(_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_NETWORK_CONFIG,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped NETWORK_CONFIG_REFRESH from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_GATHER_IDX_NETWORK_ID);
@@ -1027,17 +1027,17 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
 				com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_GATHER_IDX_COM);
 				if (com) {
 					if (network)
-						network->addCredential(com);
-					else RR->mc->addCredential(com,false);
+						network->addCredential(tPtr,com);
+					else RR->mc->addCredential(tPtr,com,false);
 				}
 			} catch ( ... ) {
 				TRACE("MULTICAST_GATHER from %s(%s): discarded invalid COM",peer->address().toString().c_str(),_path->address().toString().c_str());
 			}
 		}
 
-		const bool trustEstablished = ((network)&&(network->gate(peer)));
+		const bool trustEstablished = ((network)&&(network->gate(tPtr,peer)));
 		if (!trustEstablished)
-			_sendErrorNeedCredentials(RR,peer,nwid);
+			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
 		if ( ( trustEstablished || RR->mc->cacheAuthorized(peer->address(),nwid,RR->node->now()) ) && (gatherLimit > 0) ) {
 			Packet outp(peer->address(),RR->identity.address(),Packet::VERB_OK);
 			outp.append((unsigned char)Packet::VERB_MULTICAST_GATHER);
@@ -1048,7 +1048,7 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
 			const unsigned int gatheredLocally = RR->mc->gather(peer->address(),nwid,mg,outp,gatherLimit);
 			if (gatheredLocally > 0) {
 				outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-				_path->send(RR,outp.data(),outp.size(),RR->node->now());
+				_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 			}
 
 			// If we are a member of a cluster, distribute this GATHER across it
@@ -1058,14 +1058,14 @@ bool IncomingPacket::_doMULTICAST_GATHER(const RuntimeEnvironment *RR,const Shar
 #endif
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_GATHER,0,Packet::VERB_NOP,trustEstablished);
 	} catch ( ... ) {
 		TRACE("dropped MULTICAST_GATHER from %s(%s): unexpected exception",peer->address().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t nwid = at<uint64_t>(ZT_PROTO_VERB_MULTICAST_FRAME_IDX_NETWORK_ID);
@@ -1081,19 +1081,19 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 				CertificateOfMembership com;
 				offset += com.deserialize(*this,ZT_PROTO_VERB_MULTICAST_FRAME_IDX_COM);
 				if (com)
-					network->addCredential(com);
+					network->addCredential(tPtr,com);
 			}
 
-			if (!network->gate(peer)) {
+			if (!network->gate(tPtr,peer)) {
 				TRACE("dropped MULTICAST_FRAME from %s(%s): not a member of private network %.16llx",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
-				_sendErrorNeedCredentials(RR,peer,nwid);
-				peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
+				_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
 				return true;
 			}
 
 			if (network->config().multicastLimit == 0) {
 				TRACE("dropped MULTICAST_FRAME from %s(%s): network %.16llx does not allow multicast",peer->address().toString().c_str(),_path->address().toString().c_str(),(unsigned long long)network->id());
-				peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
+				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
 				return true;
 			}
 
@@ -1120,12 +1120,12 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 			if ((frameLen > 0)&&(frameLen <= ZT_IF_MTU)) {
 				if (!to.mac().isMulticast()) {
 					TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: destination is unicast, must use FRAME or EXT_FRAME",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str());
-					peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+					peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 					return true;
 				}
 				if ((!from)||(from.isMulticast())||(from == network->mac())) {
 					TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: invalid source MAC",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str());
-					peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+					peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 					return true;
 				}
 
@@ -1134,14 +1134,14 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 						network->learnBridgeRoute(from,peer->address());
 					} else {
 						TRACE("dropped MULTICAST_FRAME from %s@%s(%s) to %s: sender not allowed to bridge into %.16llx",from.toString().c_str(),peer->address().toString().c_str(),_path->address().toString().c_str(),to.toString().c_str(),network->id());
-						peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
+						peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true); // trustEstablished because COM is okay
 						return true;
 					}
 				}
 
 				const uint8_t *const frameData = (const uint8_t *)field(offset + ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME,frameLen);
-				if (network->filterIncomingPacket(peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) {
-					RR->node->putFrame(nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
+				if (network->filterIncomingPacket(tPtr,peer,RR->identity.address(),from,to.mac(),frameData,frameLen,etherType,0) > 0) {
+					RR->node->putFrame(tPtr,nwid,network->userPtr(),from,to.mac(),etherType,0,(const void *)frameData,frameLen);
 				}
 			}
 
@@ -1155,14 +1155,14 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 				outp.append((unsigned char)0x02); // flag 0x02 = contains gather results
 				if (RR->mc->gather(peer->address(),nwid,to,outp,gatherLimit)) {
 					outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-					_path->send(RR,outp.data(),outp.size(),RR->node->now());
+					_path->send(RR,tPtr,outp.data(),outp.size(),RR->node->now());
 				}
 			}
 
-			peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,true);
 		} else {
-			_sendErrorNeedCredentials(RR,peer,nwid);
-			peer->received(_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
+			_sendErrorNeedCredentials(RR,tPtr,peer,nwid);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_MULTICAST_FRAME,0,Packet::VERB_NOP,false);
 		}
 	} catch ( ... ) {
 		TRACE("dropped MULTICAST_FRAME from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
@@ -1170,7 +1170,7 @@ bool IncomingPacket::_doMULTICAST_FRAME(const RuntimeEnvironment *RR,const Share
 	return true;
 }
 
-bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const uint64_t now = RR->node->now();
@@ -1178,7 +1178,7 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 		// First, subject this to a rate limit
 		if (!peer->rateGatePushDirectPaths(now)) {
 			TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): circuit breaker tripped",source().toString().c_str(),_path->address().toString().c_str());
-			peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
 			return true;
 		}
 
@@ -1209,10 +1209,10 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 						redundant = peer->hasActivePathTo(now,a);
 					}
 
-					if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
+					if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) {
 						if (++countPerScope[(int)a.ipScope()][0] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
 							TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
-							peer->attemptToContactAt(InetAddress(),a,now,false,0);
+							peer->attemptToContactAt(tPtr,InetAddress(),a,now,false,0);
 						} else {
 							TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
 						}
@@ -1228,10 +1228,10 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 						redundant = peer->hasActivePathTo(now,a);
 					}
 
-					if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(peer->address(),_path->localAddress(),a)) ) {
+					if ( ((flags & ZT_PUSH_DIRECT_PATHS_FLAG_FORGET_PATH) == 0) && (!redundant) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,peer->address(),_path->localAddress(),a)) ) {
 						if (++countPerScope[(int)a.ipScope()][1] <= ZT_PUSH_DIRECT_PATHS_MAX_PER_SCOPE_AND_FAMILY) {
 							TRACE("attempting to contact %s at pushed direct path %s",peer->address().toString().c_str(),a.toString().c_str());
-							peer->attemptToContactAt(InetAddress(),a,now,false,0);
+							peer->attemptToContactAt(tPtr,InetAddress(),a,now,false,0);
 						} else {
 							TRACE("ignoring contact for %s at %s -- too many per scope",peer->address().toString().c_str(),a.toString().c_str());
 						}
@@ -1241,20 +1241,20 @@ bool IncomingPacket::_doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const Sha
 			ptr += addrLen;
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_PUSH_DIRECT_PATHS,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped PUSH_DIRECT_PATHS from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		const Address originatorAddress(field(ZT_PACKET_IDX_PAYLOAD,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
-		SharedPtr<Peer> originator(RR->topology->getPeer(originatorAddress));
+		SharedPtr<Peer> originator(RR->topology->getPeer(tPtr,originatorAddress));
 		if (!originator) {
-			RR->sw->requestWhois(originatorAddress);
+			RR->sw->requestWhois(tPtr,originatorAddress);
 			return false;
 		}
 
@@ -1285,7 +1285,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 		const unsigned int signatureLength = at<uint16_t>(ZT_PACKET_IDX_PAYLOAD + 27 + vlf);
 		if (!originator->identity().verify(field(ZT_PACKET_IDX_PAYLOAD,27 + vlf),27 + vlf,field(ZT_PACKET_IDX_PAYLOAD + 29 + vlf,signatureLength),signatureLength)) {
 			TRACE("dropped CIRCUIT_TEST from %s(%s): signature by originator %s invalid",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
-			peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
 			return true;
 		}
 		vlf += signatureLength;
@@ -1304,14 +1304,14 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 			SharedPtr<Network> network(RR->node->network(originatorCredentialNetworkId));
 			if ((!network)||(!network->config().circuitTestingAllowed(originatorAddress))) {
 				TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s specified network ID %.16llx as credential, and we don't belong to that network or originator is not allowed'",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str(),originatorCredentialNetworkId);
-				peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+				peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
 				return true;
 			}
-			if (network->gate(peer))
+			if (network->gate(tPtr,peer))
 				reportFlags |= ZT_CIRCUIT_TEST_REPORT_FLAGS_UPSTREAM_AUTHORIZED_IN_PATH;
 		} else {
 			TRACE("dropped CIRCUIT_TEST from %s(%s): originator %s did not specify a credential or credential type",source().toString().c_str(),_path->address().toString().c_str(),originatorAddress.toString().c_str());
-			peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+			peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
 			return true;
 		}
 
@@ -1327,7 +1327,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 			for(unsigned int h=0;h<breadth;++h) {
 				nextHop[h].setTo(field(remainingHopsPtr,ZT_ADDRESS_LENGTH),ZT_ADDRESS_LENGTH);
 				remainingHopsPtr += ZT_ADDRESS_LENGTH;
-				SharedPtr<Peer> nhp(RR->topology->getPeer(nextHop[h]));
+				SharedPtr<Peer> nhp(RR->topology->getPeer(tPtr,nextHop[h]));
 				if (nhp) {
 					SharedPtr<Path> nhbp(nhp->getBestPath(now,false));
 					if ((nhbp)&&(nhbp->alive(now)))
@@ -1362,7 +1362,7 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 				nextHop[h].appendTo(outp);
 				nextHopBestPathAddress[h].serialize(outp); // appends 0 if null InetAddress
 			}
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 		}
 
 		// If there are next hops, forward the test along through the graph
@@ -1377,19 +1377,19 @@ bool IncomingPacket::_doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPt
 				if (RR->identity.address() != nextHop[h]) { // next hops that loop back to the current hop are not valid
 					outp.newInitializationVector();
 					outp.setDestination(nextHop[h]);
-					RR->sw->send(outp,true);
+					RR->sw->send(tPtr,outp,true);
 				}
 			}
 		}
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped CIRCUIT_TEST from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		ZT_CircuitTestReport report;
@@ -1431,14 +1431,14 @@ bool IncomingPacket::_doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const S
 
 		RR->node->postCircuitTestReport(&report);
 
-		peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer)
+bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer)
 {
 	try {
 		if (size() >= (ZT_PACKET_IDX_PAYLOAD + 8)) {
@@ -1447,16 +1447,16 @@ bool IncomingPacket::_doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPt
 			um.typeId = at<uint64_t>(ZT_PACKET_IDX_PAYLOAD);
 			um.data = reinterpret_cast<const void *>(reinterpret_cast<const uint8_t *>(data()) + ZT_PACKET_IDX_PAYLOAD + 8);
 			um.length = size() - (ZT_PACKET_IDX_PAYLOAD + 8);
-			RR->node->postEvent(ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
+			RR->node->postEvent(tPtr,ZT_EVENT_USER_MESSAGE,reinterpret_cast<const void *>(&um));
 		}
-		peer->received(_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
+		peer->received(tPtr,_path,hops(),packetId(),Packet::VERB_CIRCUIT_TEST_REPORT,0,Packet::VERB_NOP,false);
 	} catch ( ... ) {
 		TRACE("dropped CIRCUIT_TEST_REPORT from %s(%s): unexpected exception",source().toString().c_str(),_path->address().toString().c_str());
 	}
 	return true;
 }
 
-void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid)
+void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid)
 {
 	const uint64_t now = RR->node->now();
 	if (peer->rateGateOutgoingComRequest(now)) {
@@ -1466,7 +1466,7 @@ void IncomingPacket::_sendErrorNeedCredentials(const RuntimeEnvironment *RR,cons
 		outp.append((uint8_t)Packet::ERROR_NEED_MEMBERSHIP_CERTIFICATE);
 		outp.append(nwid);
 		outp.armor(peer->key(),true,_path->nextOutgoingCounter());
-		_path->send(RR,outp.data(),outp.size(),now);
+		_path->send(RR,tPtr,outp.data(),outp.size(),now);
 	}
 }
 

+ 22 - 21
node/IncomingPacket.hpp

@@ -102,9 +102,10 @@ public:
 	 * may no longer be valid.
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @return True if decoding and processing is complete, false if caller should try again
 	 */
-	bool tryDecode(const RuntimeEnvironment *RR);
+	bool tryDecode(const RuntimeEnvironment *RR,void *tPtr);
 
 	/**
 	 * @return Time of packet receipt / start of decode
@@ -114,26 +115,26 @@ public:
 private:
 	// These are called internally to handle packet contents once it has
 	// been authenticated, decrypted, decompressed, and classified.
-	bool _doERROR(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doHELLO(const RuntimeEnvironment *RR,const bool alreadyAuthenticated);
-	bool _doOK(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doWHOIS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doRENDEZVOUS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doFRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doEXT_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doECHO(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-	bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer);
-
-	void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,const SharedPtr<Peer> &peer,const uint64_t nwid);
+	bool _doERROR(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doHELLO(const RuntimeEnvironment *RR,void *tPtr,const bool alreadyAuthenticated);
+	bool _doOK(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doWHOIS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doRENDEZVOUS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doFRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doEXT_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doECHO(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doMULTICAST_LIKE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_CREDENTIALS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_CONFIG_REQUEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doNETWORK_CONFIG(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doMULTICAST_GATHER(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doMULTICAST_FRAME(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doPUSH_DIRECT_PATHS(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doCIRCUIT_TEST(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doCIRCUIT_TEST_REPORT(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+	bool _doUSER_MESSAGE(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer);
+
+	void _sendErrorNeedCredentials(const RuntimeEnvironment *RR,void *tPtr,const SharedPtr<Peer> &peer,const uint64_t nwid);
 
 	uint64_t _receiveTime;
 	SharedPtr<Path> _path;

+ 12 - 12
node/Membership.cpp

@@ -40,7 +40,7 @@ Membership::Membership() :
 	for(unsigned int i=0;i<ZT_MAX_CERTIFICATES_OF_OWNERSHIP;++i) _remoteCoos[i] = &(_cooMem[i]);
 }
 
-void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
+void Membership::pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force)
 {
 	bool sendCom = ( (nconf.com) && ( ((now - _lastPushedCom) >= ZT_CREDENTIAL_PUSH_EVERY) || (force) ) );
 
@@ -113,7 +113,7 @@ void Membership::pushCredentials(const RuntimeEnvironment *RR,const uint64_t now
 		outp.setAt(cooCountAt,(uint16_t)thisPacketCooCount);
 
 		outp.compress();
-		RR->sw->send(outp,true);
+		RR->sw->send(tPtr,outp,true);
 	}
 }
 
@@ -123,7 +123,7 @@ const Tag *Membership::getTag(const NetworkConfig &nconf,const uint32_t id) cons
 	return ( ((t != &(_remoteTags[ZT_MAX_NETWORK_CAPABILITIES]))&&((*t)->id == (uint64_t)id)) ? ((((*t)->lastReceived)&&(_isCredentialTimestampValid(nconf,**t))) ? &((*t)->credential) : (const Tag *)0) : (const Tag *)0);
 }
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com)
 {
 	const uint64_t newts = com.timestamp().first;
 	if (newts <= _comRevocationThreshold) {
@@ -141,7 +141,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 		return ADD_ACCEPTED_REDUNDANT;
 	}
 
-	switch(com.verify(RR)) {
+	switch(com.verify(RR,tPtr)) {
 		default:
 			TRACE("addCredential(CertificateOfMembership) for %s on %.16llx REJECTED (invalid signature or object)",com.issuedTo().toString().c_str(),com.networkId());
 			return ADD_REJECTED;
@@ -154,7 +154,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 	}
 }
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag)
 {
 	_RemoteCredential<Tag> *const *htmp = std::lower_bound(&(_remoteTags[0]),&(_remoteTags[ZT_MAX_NETWORK_TAGS]),(uint64_t)tag.id(),_RemoteCredentialComp<Tag>());
 	_RemoteCredential<Tag> *have = ((htmp != &(_remoteTags[ZT_MAX_NETWORK_TAGS]))&&((*htmp)->id == (uint64_t)tag.id())) ? *htmp : (_RemoteCredential<Tag> *)0;
@@ -169,7 +169,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 		}
 	}
 
-	switch(tag.verify(RR)) {
+	switch(tag.verify(RR,tPtr)) {
 		default:
 			TRACE("addCredential(Tag) for %s on %.16llx REJECTED (invalid)",tag.issuedTo().toString().c_str(),tag.networkId());
 			return ADD_REJECTED;
@@ -184,7 +184,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 	}
 }
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap)
 {
 	_RemoteCredential<Capability> *const *htmp = std::lower_bound(&(_remoteCaps[0]),&(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]),(uint64_t)cap.id(),_RemoteCredentialComp<Capability>());
 	_RemoteCredential<Capability> *have = ((htmp != &(_remoteCaps[ZT_MAX_NETWORK_CAPABILITIES]))&&((*htmp)->id == (uint64_t)cap.id())) ? *htmp : (_RemoteCredential<Capability> *)0;
@@ -199,7 +199,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 		}
 	}
 
-	switch(cap.verify(RR)) {
+	switch(cap.verify(RR,tPtr)) {
 		default:
 			TRACE("addCredential(Capability) for %s on %.16llx REJECTED (invalid)",cap.issuedTo().toString().c_str(),cap.networkId());
 			return ADD_REJECTED;
@@ -214,9 +214,9 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 	}
 }
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev)
 {
-	switch(rev.verify(RR)) {
+	switch(rev.verify(RR,tPtr)) {
 		default:
 			return ADD_REJECTED;
 		case 0: {
@@ -239,7 +239,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 	}
 }
 
-Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo)
+Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo)
 {
 	_RemoteCredential<CertificateOfOwnership> *const *htmp = std::lower_bound(&(_remoteCoos[0]),&(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]),(uint64_t)coo.id(),_RemoteCredentialComp<CertificateOfOwnership>());
 	_RemoteCredential<CertificateOfOwnership> *have = ((htmp != &(_remoteCoos[ZT_MAX_CERTIFICATES_OF_OWNERSHIP]))&&((*htmp)->id == (uint64_t)coo.id())) ? *htmp : (_RemoteCredential<CertificateOfOwnership> *)0;
@@ -254,7 +254,7 @@ Membership::AddCredentialResult Membership::addCredential(const RuntimeEnvironme
 		}
 	}
 
-	switch(coo.verify(RR)) {
+	switch(coo.verify(RR,tPtr)) {
 		default:
 			TRACE("addCredential(CertificateOfOwnership) for %s on %.16llx REJECTED (invalid)",coo.issuedTo().toString().c_str(),coo.networkId());
 			return ADD_REJECTED;

+ 7 - 6
node/Membership.hpp

@@ -158,13 +158,14 @@ public:
 	 * sends VERB_NETWORK_CREDENTIALS if the recipient might need them.
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param now Current time
 	 * @param peerAddress Address of member peer (the one that this Membership describes)
 	 * @param nconf My network config
 	 * @param localCapabilityIndex Index of local capability to include (in nconf.capabilities[]) or -1 if none
 	 * @param force If true, send objects regardless of last push time
 	 */
-	void pushCredentials(const RuntimeEnvironment *RR,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
+	void pushCredentials(const RuntimeEnvironment *RR,void *tPtr,const uint64_t now,const Address &peerAddress,const NetworkConfig &nconf,int localCapabilityIndex,const bool force);
 
 	/**
 	 * Check whether we should push MULTICAST_LIKEs to this peer
@@ -226,27 +227,27 @@ public:
 	/**
 	 * Validate and add a credential if signature is okay and it's otherwise good
 	 */
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfMembership &com);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfMembership &com);
 
 	/**
 	 * Validate and add a credential if signature is okay and it's otherwise good
 	 */
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Tag &tag);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Tag &tag);
 
 	/**
 	 * Validate and add a credential if signature is okay and it's otherwise good
 	 */
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Capability &cap);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Capability &cap);
 
 	/**
 	 * Validate and add a credential if signature is okay and it's otherwise good
 	 */
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const Revocation &rev);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const Revocation &rev);
 
 	/**
 	 * Validate and add a credential if signature is okay and it's otherwise good
 	 */
-	AddCredentialResult addCredential(const RuntimeEnvironment *RR,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
+	AddCredentialResult addCredential(const RuntimeEnvironment *RR,void *tPtr,const NetworkConfig &nconf,const CertificateOfOwnership &coo);
 
 private:
 	_RemoteCredential<Tag> *_newTag(const uint64_t id);

+ 12 - 11
node/Multicaster.cpp

@@ -43,14 +43,14 @@ Multicaster::~Multicaster()
 {
 }
 
-void Multicaster::addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
+void Multicaster::addMultiple(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown)
 {
 	const unsigned char *p = (const unsigned char *)addresses;
 	const unsigned char *e = p + (5 * count);
 	Mutex::Lock _l(_groups_m);
 	MulticastGroupStatus &gs = _groups[Multicaster::Key(nwid,mg)];
 	while (p != e) {
-		_add(now,nwid,mg,gs,Address(p,5));
+		_add(tPtr,now,nwid,mg,gs,Address(p,5));
 		p += 5;
 	}
 }
@@ -152,6 +152,7 @@ std::vector<Address> Multicaster::getMembers(uint64_t nwid,const MulticastGroup
 }
 
 void Multicaster::send(
+	void *tPtr,
 	unsigned int limit,
 	uint64_t now,
 	uint64_t nwid,
@@ -207,7 +208,7 @@ void Multicaster::send(
 
 			for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
 				if (*ast != RR->identity.address()) {
-					out.sendOnly(RR,*ast); // optimization: don't use dedup log if it's a one-pass send
+					out.sendOnly(RR,tPtr,*ast); // optimization: don't use dedup log if it's a one-pass send
 					if (++count >= limit)
 						break;
 				}
@@ -217,7 +218,7 @@ void Multicaster::send(
 			while ((count < limit)&&(idx < gs.members.size())) {
 				Address ma(gs.members[indexes[idx++]].address);
 				if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
-					out.sendOnly(RR,ma); // optimization: don't use dedup log if it's a one-pass send
+					out.sendOnly(RR,tPtr,ma); // optimization: don't use dedup log if it's a one-pass send
 					++count;
 				}
 			}
@@ -256,7 +257,7 @@ void Multicaster::send(
 					if (com)
 						com->serialize(outp);
 					RR->node->expectReplyTo(outp.packetId());
-					RR->sw->send(outp,true);
+					RR->sw->send(tPtr,outp,true);
 				}
 			}
 
@@ -280,7 +281,7 @@ void Multicaster::send(
 
 			for(std::vector<Address>::const_iterator ast(alwaysSendTo.begin());ast!=alwaysSendTo.end();++ast) {
 				if (*ast != RR->identity.address()) {
-					out.sendAndLog(RR,*ast);
+					out.sendAndLog(RR,tPtr,*ast);
 					if (++count >= limit)
 						break;
 				}
@@ -290,7 +291,7 @@ void Multicaster::send(
 			while ((count < limit)&&(idx < gs.members.size())) {
 				Address ma(gs.members[indexes[idx++]].address);
 				if (std::find(alwaysSendTo.begin(),alwaysSendTo.end(),ma) == alwaysSendTo.end()) {
-					out.sendAndLog(RR,ma);
+					out.sendAndLog(RR,tPtr,ma);
 					++count;
 				}
 			}
@@ -352,15 +353,15 @@ void Multicaster::clean(uint64_t now)
 	}
 }
 
-void Multicaster::addCredential(const CertificateOfMembership &com,bool alreadyValidated)
+void Multicaster::addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated)
 {
-	if ((alreadyValidated)||(com.verify(RR) == 0)) {
+	if ((alreadyValidated)||(com.verify(RR,tPtr) == 0)) {
 		Mutex::Lock _l(_gatherAuth_m);
 		_gatherAuth[_GatherAuthKey(com.networkId(),com.issuedTo())] = RR->node->now();
 	}
 }
 
-void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
+void Multicaster::_add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member)
 {
 	// assumes _groups_m is locked
 
@@ -383,7 +384,7 @@ void Multicaster::_add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,Multi
 		if (tx->atLimit())
 			gs.txQueue.erase(tx++);
 		else {
-			tx->sendIfNew(RR,member);
+			tx->sendIfNew(RR,tPtr,member);
 			if (tx->atLimit())
 				gs.txQueue.erase(tx++);
 			else ++tx;

+ 8 - 5
node/Multicaster.hpp

@@ -90,10 +90,10 @@ public:
 	 * @param mg Multicast group
 	 * @param member New member address
 	 */
-	inline void add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
+	inline void add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const Address &member)
 	{
 		Mutex::Lock _l(_groups_m);
-		_add(now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
+		_add(tPtr,now,nwid,mg,_groups[Multicaster::Key(nwid,mg)],member);
 	}
 
 	/**
@@ -101,6 +101,7 @@ public:
 	 *
 	 * It's up to the caller to check bounds on the array before calling this.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param now Current time
 	 * @param nwid Network ID
 	 * @param mg Multicast group
@@ -108,7 +109,7 @@ public:
 	 * @param count Number of addresses
 	 * @param totalKnown Total number of known addresses as reported by peer
 	 */
-	void addMultiple(uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
+	void addMultiple(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,const void *addresses,unsigned int count,unsigned int totalKnown);
 
 	/**
 	 * Remove a multicast group member (if present)
@@ -150,6 +151,7 @@ public:
 	/**
 	 * Send a multicast
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param limit Multicast limit
 	 * @param now Current time
 	 * @param nwid Network ID
@@ -162,6 +164,7 @@ public:
 	 * @param len Length of packet data
 	 */
 	void send(
+		void *tPtr,
 		unsigned int limit,
 		uint64_t now,
 		uint64_t nwid,
@@ -191,7 +194,7 @@ public:
 	 * @param com Certificate of membership
 	 * @param alreadyValidated If true, COM has already been checked and found to be valid and signed
 	 */
-	void addCredential(const CertificateOfMembership &com,bool alreadyValidated);
+	void addCredential(void *tPtr,const CertificateOfMembership &com,bool alreadyValidated);
 
 	/**
 	 * Check authorization for GATHER and LIKE for non-network-members
@@ -209,7 +212,7 @@ public:
 	}
 
 private:
-	void _add(uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
+	void _add(void *tPtr,uint64_t now,uint64_t nwid,const MulticastGroup &mg,MulticastGroupStatus &gs,const Address &member);
 
 	const RuntimeEnvironment *RR;
 

+ 57 - 55
node/Network.cpp

@@ -674,7 +674,7 @@ static _doZtFilterResult _doZtFilter(
 
 const ZeroTier::MulticastGroup Network::BROADCAST(ZeroTier::MAC(0xffffffffffffULL),0);
 
-Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
+Network::Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr) :
 	RR(renv),
 	_uPtr(uptr),
 	_id(nwid),
@@ -696,11 +696,11 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
 	Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
 	NetworkConfig *nconf = new NetworkConfig();
 	try {
-		std::string conf(RR->node->dataStoreGet(confn));
+		std::string conf(RR->node->dataStoreGet(tPtr,confn));
 		if (conf.length()) {
 			dconf->load(conf.c_str());
 			if (nconf->fromDictionary(*dconf)) {
-				this->setConfiguration(*nconf,false);
+				this->setConfiguration(tPtr,*nconf,false);
 				_lastConfigUpdate = 0; // we still want to re-request a new config from the network
 				gotConf = true;
 			}
@@ -711,13 +711,13 @@ Network::Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr) :
 
 	if (!gotConf) {
 		// Save a one-byte CR to persist membership while we request a real netconf
-		RR->node->dataStorePut(confn,"\n",1,false);
+		RR->node->dataStorePut(tPtr,confn,"\n",1,false);
 	}
 
 	if (!_portInitialized) {
 		ZT_VirtualNetworkConfig ctmp;
 		_externalConfig(&ctmp);
-		_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+		_portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
 		_portInitialized = true;
 	}
 }
@@ -729,15 +729,16 @@ Network::~Network()
 
 	char n[128];
 	if (_destroyed) {
-		RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
+		RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DESTROY,&ctmp);
 		Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
-		RR->node->dataStoreDelete(n);
+		RR->node->dataStoreDelete((void *)0,n);
 	} else {
-		RR->node->configureVirtualNetworkPort(_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
+		RR->node->configureVirtualNetworkPort((void *)0,_id,&_uPtr,ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_DOWN,&ctmp);
 	}
 }
 
 bool Network::filterOutgoingPacket(
+	void *tPtr,
 	const bool noTee,
 	const Address &ztSource,
 	const Address &ztDest,
@@ -781,7 +782,7 @@ bool Network::filterOutgoingPacket(
 
 						if ((!noTee)&&(cc2)) {
 							Membership &m2 = _membership(cc2);
-							m2.pushCredentials(RR,now,cc2,_config,localCapabilityIndex,false);
+							m2.pushCredentials(RR,tPtr,now,cc2,_config,localCapabilityIndex,false);
 
 							Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
 							outp.append(_id);
@@ -791,7 +792,7 @@ bool Network::filterOutgoingPacket(
 							outp.append((uint16_t)etherType);
 							outp.append(frameData,ccLength2);
 							outp.compress();
-							RR->sw->send(outp,true);
+							RR->sw->send(tPtr,outp,true);
 						}
 
 						break;
@@ -813,11 +814,11 @@ bool Network::filterOutgoingPacket(
 
 	if (accept) {
 		if (membership)
-			membership->pushCredentials(RR,now,ztDest,_config,localCapabilityIndex,false);
+			membership->pushCredentials(RR,tPtr,now,ztDest,_config,localCapabilityIndex,false);
 
 		if ((!noTee)&&(cc)) {
 			Membership &m2 = _membership(cc);
-			m2.pushCredentials(RR,now,cc,_config,localCapabilityIndex,false);
+			m2.pushCredentials(RR,tPtr,now,cc,_config,localCapabilityIndex,false);
 
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
@@ -827,12 +828,12 @@ bool Network::filterOutgoingPacket(
 			outp.append((uint16_t)etherType);
 			outp.append(frameData,ccLength);
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 		}
 
 		if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
 			Membership &m2 = _membership(ztFinalDest);
-			m2.pushCredentials(RR,now,ztFinalDest,_config,localCapabilityIndex,false);
+			m2.pushCredentials(RR,tPtr,now,ztFinalDest,_config,localCapabilityIndex,false);
 
 			Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
@@ -842,7 +843,7 @@ bool Network::filterOutgoingPacket(
 			outp.append((uint16_t)etherType);
 			outp.append(frameData,frameLen);
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 
 			return false; // DROP locally, since we redirected
 		} else {
@@ -854,6 +855,7 @@ bool Network::filterOutgoingPacket(
 }
 
 int Network::filterIncomingPacket(
+	void *tPtr,
 	const SharedPtr<Peer> &sourcePeer,
 	const Address &ztDest,
 	const MAC &macSource,
@@ -898,7 +900,7 @@ int Network::filterIncomingPacket(
 
 				if (accept) {
 					if (cc2) {
-						_membership(cc2).pushCredentials(RR,RR->node->now(),cc2,_config,-1,false);
+						_membership(cc2).pushCredentials(RR,tPtr,RR->node->now(),cc2,_config,-1,false);
 
 						Packet outp(cc2,RR->identity.address(),Packet::VERB_EXT_FRAME);
 						outp.append(_id);
@@ -908,7 +910,7 @@ int Network::filterIncomingPacket(
 						outp.append((uint16_t)etherType);
 						outp.append(frameData,ccLength2);
 						outp.compress();
-						RR->sw->send(outp,true);
+						RR->sw->send(tPtr,outp,true);
 					}
 					break;
 				}
@@ -929,7 +931,7 @@ int Network::filterIncomingPacket(
 
 	if (accept) {
 		if (cc) {
-			_membership(cc).pushCredentials(RR,RR->node->now(),cc,_config,-1,false);
+			_membership(cc).pushCredentials(RR,tPtr,RR->node->now(),cc,_config,-1,false);
 
 			Packet outp(cc,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
@@ -939,11 +941,11 @@ int Network::filterIncomingPacket(
 			outp.append((uint16_t)etherType);
 			outp.append(frameData,ccLength);
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 		}
 
 		if ((ztDest != ztFinalDest)&&(ztFinalDest)) {
-			_membership(ztFinalDest).pushCredentials(RR,RR->node->now(),ztFinalDest,_config,-1,false);
+			_membership(ztFinalDest).pushCredentials(RR,tPtr,RR->node->now(),ztFinalDest,_config,-1,false);
 
 			Packet outp(ztFinalDest,RR->identity.address(),Packet::VERB_EXT_FRAME);
 			outp.append(_id);
@@ -953,7 +955,7 @@ int Network::filterIncomingPacket(
 			outp.append((uint16_t)etherType);
 			outp.append(frameData,frameLen);
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 
 			return 0; // DROP locally, since we redirected
 		}
@@ -972,12 +974,12 @@ bool Network::subscribedToMulticastGroup(const MulticastGroup &mg,bool includeBr
 	return false;
 }
 
-void Network::multicastSubscribe(const MulticastGroup &mg)
+void Network::multicastSubscribe(void *tPtr,const MulticastGroup &mg)
 {
 	Mutex::Lock _l(_lock);
 	if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) {
 		_myMulticastGroups.insert(std::upper_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg),mg);
-		_sendUpdatesToMembers(&mg);
+		_sendUpdatesToMembers(tPtr,&mg);
 	}
 }
 
@@ -989,7 +991,7 @@ void Network::multicastUnsubscribe(const MulticastGroup &mg)
 		_myMulticastGroups.erase(i);
 }
 
-uint64_t Network::handleConfigChunk(const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
+uint64_t Network::handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr)
 {
 	const unsigned int start = ptr;
 
@@ -1043,7 +1045,7 @@ uint64_t Network::handleConfigChunk(const uint64_t packetId,const Address &sourc
 			}
 
 			// If it's not a duplicate, check chunk signature
-			const Identity controllerId(RR->topology->getIdentity(controller()));
+			const Identity controllerId(RR->topology->getIdentity(tPtr,controller()));
 			if (!controllerId) { // we should always have the controller identity by now, otherwise how would we have queried it the first time?
 				TRACE("unable to verify chunk from %s: don't have controller identity",source.toString().c_str());
 				return 0;
@@ -1067,7 +1069,7 @@ uint64_t Network::handleConfigChunk(const uint64_t packetId,const Address &sourc
 					if ((*a != source)&&(*a != controller())) {
 						Packet outp(*a,RR->identity.address(),Packet::VERB_NETWORK_CONFIG);
 						outp.append(reinterpret_cast<const uint8_t *>(chunk.data()) + start,chunk.size() - start);
-						RR->sw->send(outp,true);
+						RR->sw->send(tPtr,outp,true);
 					}
 				}
 			}
@@ -1126,7 +1128,7 @@ uint64_t Network::handleConfigChunk(const uint64_t packetId,const Address &sourc
 	}
 
 	if (nc) {
-		this->setConfiguration(*nc,true);
+		this->setConfiguration(tPtr,*nc,true);
 		delete nc;
 		return configUpdateId;
 	} else {
@@ -1136,7 +1138,7 @@ uint64_t Network::handleConfigChunk(const uint64_t packetId,const Address &sourc
 	return 0;
 }
 
-int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
+int Network::setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk)
 {
 	// _lock is NOT locked when this is called
 	try {
@@ -1156,7 +1158,7 @@ int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
 			_portInitialized = true;
 			_externalConfig(&ctmp);
 		}
-		_portError = RR->node->configureVirtualNetworkPort(_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
+		_portError = RR->node->configureVirtualNetworkPort(tPtr,_id,&_uPtr,(oldPortInitialized) ? ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_CONFIG_UPDATE : ZT_VIRTUAL_NETWORK_CONFIG_OPERATION_UP,&ctmp);
 
 		if (saveToDisk) {
 			Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *d = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
@@ -1164,7 +1166,7 @@ int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
 				char n[64];
 				Utils::snprintf(n,sizeof(n),"networks.d/%.16llx.conf",_id);
 				if (nconf.toDictionary(*d,false))
-					RR->node->dataStorePut(n,(const void *)d->data(),d->sizeBytes(),true);
+					RR->node->dataStorePut(tPtr,n,(const void *)d->data(),d->sizeBytes(),true);
 			} catch ( ... ) {}
 			delete d;
 		}
@@ -1176,7 +1178,7 @@ int Network::setConfiguration(const NetworkConfig &nconf,bool saveToDisk)
 	return 0;
 }
 
-void Network::requestConfiguration()
+void Network::requestConfiguration(void *tPtr)
 {
 	/* ZeroTier addresses can't begin with 0xff, so this is used to mark controllerless
 	 * network IDs. Controllerless network IDs only support unicast IPv6 using the 6plane
@@ -1236,7 +1238,7 @@ void Network::requestConfiguration()
 			nconf->type = ZT_NETWORK_TYPE_PUBLIC;
 			Utils::snprintf(nconf->name,sizeof(nconf->name),"adhoc-%.04x-%.04x",(int)startPortRange,(int)endPortRange);
 
-			this->setConfiguration(*nconf,false);
+			this->setConfiguration(tPtr,*nconf,false);
 			delete nconf;
 		} else {
 			this->setNotFound();
@@ -1284,10 +1286,10 @@ void Network::requestConfiguration()
 	}
 	outp.compress();
 	RR->node->expectReplyTo(outp.packetId());
-	RR->sw->send(outp,true);
+	RR->sw->send(tPtr,outp,true);
 }
 
-bool Network::gate(const SharedPtr<Peer> &peer)
+bool Network::gate(void *tPtr,const SharedPtr<Peer> &peer)
 {
 	const uint64_t now = RR->node->now();
 	Mutex::Lock _l(_lock);
@@ -1298,8 +1300,8 @@ bool Network::gate(const SharedPtr<Peer> &peer)
 				if (!m)
 					m = &(_membership(peer->address()));
 				if (m->shouldLikeMulticasts(now)) {
-					m->pushCredentials(RR,now,peer->address(),_config,-1,false);
-					_announceMulticastGroupsTo(peer->address(),_allMulticastGroups());
+					m->pushCredentials(RR,tPtr,now,peer->address(),_config,-1,false);
+					_announceMulticastGroupsTo(tPtr,peer->address(),_allMulticastGroups());
 					m->likingMulticasts(now);
 				}
 				return true;
@@ -1377,31 +1379,31 @@ void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
 	}
 }
 
-void Network::learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
+void Network::learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,uint64_t now)
 {
 	Mutex::Lock _l(_lock);
 	const unsigned long tmp = (unsigned long)_multicastGroupsBehindMe.size();
 	_multicastGroupsBehindMe.set(mg,now);
 	if (tmp != _multicastGroupsBehindMe.size())
-		_sendUpdatesToMembers(&mg);
+		_sendUpdatesToMembers(tPtr,&mg);
 }
 
-Membership::AddCredentialResult Network::addCredential(const CertificateOfMembership &com)
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const CertificateOfMembership &com)
 {
 	if (com.networkId() != _id)
 		return Membership::ADD_REJECTED;
 	const Address a(com.issuedTo());
 	Mutex::Lock _l(_lock);
 	Membership &m = _membership(a);
-	const Membership::AddCredentialResult result = m.addCredential(RR,_config,com);
+	const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,com);
 	if ((result == Membership::ADD_ACCEPTED_NEW)||(result == Membership::ADD_ACCEPTED_REDUNDANT)) {
-		m.pushCredentials(RR,RR->node->now(),a,_config,-1,false);
-		RR->mc->addCredential(com,true);
+		m.pushCredentials(RR,tPtr,RR->node->now(),a,_config,-1,false);
+		RR->mc->addCredential(tPtr,com,true);
 	}
 	return result;
 }
 
-Membership::AddCredentialResult Network::addCredential(const Address &sentFrom,const Revocation &rev)
+Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev)
 {
 	if (rev.networkId() != _id)
 		return Membership::ADD_REJECTED;
@@ -1409,7 +1411,7 @@ Membership::AddCredentialResult Network::addCredential(const Address &sentFrom,c
 	Mutex::Lock _l(_lock);
 	Membership &m = _membership(rev.target());
 
-	const Membership::AddCredentialResult result = m.addCredential(RR,_config,rev);
+	const Membership::AddCredentialResult result = m.addCredential(RR,tPtr,_config,rev);
 
 	if ((result == Membership::ADD_ACCEPTED_NEW)&&(rev.fastPropagate())) {
 		Address *a = (Address *)0;
@@ -1424,7 +1426,7 @@ Membership::AddCredentialResult Network::addCredential(const Address &sentFrom,c
 				outp.append((uint16_t)1); // one revocation!
 				rev.serialize(outp);
 				outp.append((uint16_t)0); // no certificates of ownership
-				RR->sw->send(outp,true);
+				RR->sw->send(tPtr,outp,true);
 			}
 		}
 	}
@@ -1495,7 +1497,7 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const
 	}
 }
 
-void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup)
+void Network::_sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup)
 {
 	// Assumes _lock is locked
 	const uint64_t now = RR->node->now();
@@ -1521,9 +1523,9 @@ void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGrou
 				outp.append((uint16_t)0); // no tags
 				outp.append((uint16_t)0); // no revocations
 				outp.append((uint16_t)0); // no certificates of ownership
-				RR->sw->send(outp,true);
+				RR->sw->send(tPtr,outp,true);
 			}
-			_announceMulticastGroupsTo(*a,groups);
+			_announceMulticastGroupsTo(tPtr,*a,groups);
 		}
 
 		// Also announce to controller, and send COM to simplify and generalize behavior even though in theory it does not need it
@@ -1537,9 +1539,9 @@ void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGrou
 				outp.append((uint16_t)0); // no tags
 				outp.append((uint16_t)0); // no revocations
 				outp.append((uint16_t)0); // no certificates of ownership
-				RR->sw->send(outp,true);
+				RR->sw->send(tPtr,outp,true);
 			}
-			_announceMulticastGroupsTo(c,groups);
+			_announceMulticastGroupsTo(tPtr,c,groups);
 		}
 	}
 
@@ -1556,17 +1558,17 @@ void Network::_sendUpdatesToMembers(const MulticastGroup *const newMulticastGrou
 		Membership *m = (Membership *)0;
 		Hashtable<Address,Membership>::Iterator i(_memberships);
 		while (i.next(a,m)) {
-			m->pushCredentials(RR,now,*a,_config,-1,false);
+			m->pushCredentials(RR,tPtr,now,*a,_config,-1,false);
 			if ( ((newMulticastGroup)||(m->shouldLikeMulticasts(now))) && (m->isAllowedOnNetwork(_config)) ) {
 				if (!newMulticastGroup)
 					m->likingMulticasts(now);
-				_announceMulticastGroupsTo(*a,groups);
+				_announceMulticastGroupsTo(tPtr,*a,groups);
 			}
 		}
 	}
 }
 
-void Network::_announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups)
+void Network::_announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups)
 {
 	// Assumes _lock is locked
 	Packet outp(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
@@ -1574,7 +1576,7 @@ void Network::_announceMulticastGroupsTo(const Address &peer,const std::vector<M
 	for(std::vector<MulticastGroup>::const_iterator mg(allMulticastGroups.begin());mg!=allMulticastGroups.end();++mg) {
 		if ((outp.size() + 24) >= ZT_PROTO_MAX_PACKET_LENGTH) {
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tPtr,outp,true);
 			outp.reset(peer,RR->identity.address(),Packet::VERB_MULTICAST_LIKE);
 		}
 
@@ -1586,7 +1588,7 @@ void Network::_announceMulticastGroupsTo(const Address &peer,const std::vector<M
 
 	if (outp.size() > ZT_PROTO_MIN_PACKET_LENGTH) {
 		outp.compress();
-		RR->sw->send(outp,true);
+		RR->sw->send(tPtr,outp,true);
 	}
 }
 

+ 38 - 21
node/Network.hpp

@@ -77,10 +77,11 @@ public:
 	 * constructed to actually configure the port.
 	 *
 	 * @param renv Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param nwid Network ID
 	 * @param uptr Arbitrary pointer used by externally-facing API (for user use)
 	 */
-	Network(const RuntimeEnvironment *renv,uint64_t nwid,void *uptr);
+	Network(const RuntimeEnvironment *renv,void *tPtr,uint64_t nwid,void *uptr);
 
 	~Network();
 
@@ -101,6 +102,7 @@ public:
 	 * such as TEE may be taken, and credentials may be pushed, so this is not
 	 * side-effect-free. It's basically step one in sending something over VL2.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param noTee If true, do not TEE anything anywhere (for two-pass filtering as done with multicast and bridging)
 	 * @param ztSource Source ZeroTier address
 	 * @param ztDest Destination ZeroTier address
@@ -113,6 +115,7 @@ public:
 	 * @return True if packet should be sent, false if dropped or redirected
 	 */
 	bool filterOutgoingPacket(
+		void *tPtr,
 		const bool noTee,
 		const Address &ztSource,
 		const Address &ztDest,
@@ -131,6 +134,7 @@ public:
 	 * a match certain actions may be taken such as sending a copy of the packet
 	 * to a TEE or REDIRECT target.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param sourcePeer Source Peer
 	 * @param ztDest Destination ZeroTier address
 	 * @param macSource Ethernet layer source address
@@ -142,6 +146,7 @@ public:
 	 * @return 0 == drop, 1 == accept, 2 == accept even if bridged
 	 */
 	int filterIncomingPacket(
+		void *tPtr,
 		const SharedPtr<Peer> &sourcePeer,
 		const Address &ztDest,
 		const MAC &macSource,
@@ -163,9 +168,10 @@ public:
 	/**
 	 * Subscribe to a multicast group
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param mg New multicast group
 	 */
-	void multicastSubscribe(const MulticastGroup &mg);
+	void multicastSubscribe(void *tPtr,const MulticastGroup &mg);
 
 	/**
 	 * Unsubscribe from a multicast group
@@ -181,22 +187,24 @@ public:
 	 * chunks via OK(NETWORK_CONFIG_REQUEST) or NETWORK_CONFIG. It verifies
 	 * each chunk and once assembled applies the configuration.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param packetId Packet ID or 0 if none (e.g. via cluster path)
 	 * @param source Address of sender of chunk or NULL if none (e.g. via cluster path)
 	 * @param chunk Buffer containing chunk
 	 * @param ptr Index of chunk and related fields in packet
 	 * @return Update ID if update was fully assembled and accepted or 0 otherwise
 	 */
-	uint64_t handleConfigChunk(const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
+	uint64_t handleConfigChunk(void *tPtr,const uint64_t packetId,const Address &source,const Buffer<ZT_PROTO_MAX_PACKET_LENGTH> &chunk,unsigned int ptr);
 
 	/**
 	 * Set network configuration
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param nconf Network configuration
 	 * @param saveToDisk Save to disk? Used during loading, should usually be true otherwise.
 	 * @return 0 == bad, 1 == accepted but duplicate/unchanged, 2 == accepted and new
 	 */
-	int setConfiguration(const NetworkConfig &nconf,bool saveToDisk);
+	int setConfiguration(void *tPtr,const NetworkConfig &nconf,bool saveToDisk);
 
 	/**
 	 * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this
@@ -218,13 +226,18 @@ public:
 
 	/**
 	 * Causes this network to request an updated configuration from its master node now
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 */
-	void requestConfiguration();
+	void requestConfiguration(void *tPtr);
 
 	/**
 	 * Determine whether this peer is permitted to communicate on this network
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 * @param peer Peer to check
 	 */
-	bool gate(const SharedPtr<Peer> &peer);
+	bool gate(void *tPtr,const SharedPtr<Peer> &peer);
 
 	/**
 	 * Do periodic cleanup and housekeeping tasks
@@ -233,11 +246,13 @@ public:
 
 	/**
 	 * Push state to members such as multicast group memberships and latest COM (if needed)
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 */
-	inline void sendUpdatesToMembers()
+	inline void sendUpdatesToMembers(void *tPtr)
 	{
 		Mutex::Lock _l(_lock);
-		_sendUpdatesToMembers((const MulticastGroup *)0);
+		_sendUpdatesToMembers(tPtr,(const MulticastGroup *)0);
 	}
 
 	/**
@@ -264,64 +279,66 @@ public:
 	/**
 	 * Learn a multicast group that is bridged to our tap device
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param mg Multicast group
 	 * @param now Current time
 	 */
-	void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now);
+	void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,uint64_t now);
 
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
-	Membership::AddCredentialResult addCredential(const CertificateOfMembership &com);
+	Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com);
 
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
-	inline Membership::AddCredentialResult addCredential(const Capability &cap)
+	inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap)
 	{
 		if (cap.networkId() != _id)
 			return Membership::ADD_REJECTED;
 		Mutex::Lock _l(_lock);
-		return _membership(cap.issuedTo()).addCredential(RR,_config,cap);
+		return _membership(cap.issuedTo()).addCredential(RR,tPtr,_config,cap);
 	}
 
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
-	inline Membership::AddCredentialResult addCredential(const Tag &tag)
+	inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag)
 	{
 		if (tag.networkId() != _id)
 			return Membership::ADD_REJECTED;
 		Mutex::Lock _l(_lock);
-		return _membership(tag.issuedTo()).addCredential(RR,_config,tag);
+		return _membership(tag.issuedTo()).addCredential(RR,tPtr,_config,tag);
 	}
 
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
-	Membership::AddCredentialResult addCredential(const Address &sentFrom,const Revocation &rev);
+	Membership::AddCredentialResult addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev);
 
 	/**
 	 * Validate a credential and learn it if it passes certificate and other checks
 	 */
-	inline Membership::AddCredentialResult addCredential(const CertificateOfOwnership &coo)
+	inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo)
 	{
 		if (coo.networkId() != _id)
 			return Membership::ADD_REJECTED;
 		Mutex::Lock _l(_lock);
-		return _membership(coo.issuedTo()).addCredential(RR,_config,coo);
+		return _membership(coo.issuedTo()).addCredential(RR,tPtr,_config,coo);
 	}
 
 	/**
 	 * Force push credentials (COM, etc.) to a peer now
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param to Destination peer address
 	 * @param now Current time
 	 */
-	inline void pushCredentialsNow(const Address &to,const uint64_t now)
+	inline void pushCredentialsNow(void *tPtr,const Address &to,const uint64_t now)
 	{
 		Mutex::Lock _l(_lock);
-		_membership(to).pushCredentials(RR,now,to,_config,-1,true);
+		_membership(to).pushCredentials(RR,tPtr,now,to,_config,-1,true);
 	}
 
 	/**
@@ -353,8 +370,8 @@ private:
 	ZT_VirtualNetworkStatus _status() const;
 	void _externalConfig(ZT_VirtualNetworkConfig *ec) const; // assumes _lock is locked
 	bool _gate(const SharedPtr<Peer> &peer);
-	void _sendUpdatesToMembers(const MulticastGroup *const newMulticastGroup);
-	void _announceMulticastGroupsTo(const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
+	void _sendUpdatesToMembers(void *tPtr,const MulticastGroup *const newMulticastGroup);
+	void _announceMulticastGroupsTo(void *tPtr,const Address &peer,const std::vector<MulticastGroup> &allMulticastGroups);
 	std::vector<MulticastGroup> _allMulticastGroups() const;
 	Membership &_membership(const Address &a);
 

+ 71 - 65
node/Node.cpp

@@ -46,7 +46,7 @@ namespace ZeroTier {
 /* Public Node interface (C++, exposed via CAPI bindings)                   */
 /****************************************************************************/
 
-Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
+Node::Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
 	_RR(this),
 	RR(&_RR),
 	_uPtr(uptr),
@@ -72,26 +72,26 @@ Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
 	memset(_prngStream,0,sizeof(_prngStream));
 	_prng.crypt12(_prngStream,_prngStream,sizeof(_prngStream));
 
-	std::string idtmp(dataStoreGet("identity.secret"));
+	std::string idtmp(dataStoreGet(tptr,"identity.secret"));
 	if ((!idtmp.length())||(!RR->identity.fromString(idtmp))||(!RR->identity.hasPrivate())) {
 		TRACE("identity.secret not found, generating...");
 		RR->identity.generate();
 		idtmp = RR->identity.toString(true);
-		if (!dataStorePut("identity.secret",idtmp,true))
+		if (!dataStorePut(tptr,"identity.secret",idtmp,true))
 			throw std::runtime_error("unable to write identity.secret");
 	}
 	RR->publicIdentityStr = RR->identity.toString(false);
 	RR->secretIdentityStr = RR->identity.toString(true);
-	idtmp = dataStoreGet("identity.public");
+	idtmp = dataStoreGet(tptr,"identity.public");
 	if (idtmp != RR->publicIdentityStr) {
-		if (!dataStorePut("identity.public",RR->publicIdentityStr,false))
+		if (!dataStorePut(tptr,"identity.public",RR->publicIdentityStr,false))
 			throw std::runtime_error("unable to write identity.public");
 	}
 
 	try {
 		RR->sw = new Switch(RR);
 		RR->mc = new Multicaster(RR);
-		RR->topology = new Topology(RR);
+		RR->topology = new Topology(RR,tptr);
 		RR->sa = new SelfAwareness(RR);
 	} catch ( ... ) {
 		delete RR->sa;
@@ -101,7 +101,7 @@ Node::Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now) :
 		throw;
 	}
 
-	postEvent(ZT_EVENT_UP);
+	postEvent(tptr,ZT_EVENT_UP);
 }
 
 Node::~Node()
@@ -121,6 +121,7 @@ Node::~Node()
 }
 
 ZT_ResultCode Node::processWirePacket(
+	void *tptr,
 	uint64_t now,
 	const struct sockaddr_storage *localAddress,
 	const struct sockaddr_storage *remoteAddress,
@@ -129,11 +130,12 @@ ZT_ResultCode Node::processWirePacket(
 	volatile uint64_t *nextBackgroundTaskDeadline)
 {
 	_now = now;
-	RR->sw->onRemotePacket(*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
+	RR->sw->onRemotePacket(tptr,*(reinterpret_cast<const InetAddress *>(localAddress)),*(reinterpret_cast<const InetAddress *>(remoteAddress)),packetData,packetLength);
 	return ZT_RESULT_OK;
 }
 
 ZT_ResultCode Node::processVirtualNetworkFrame(
+	void *tptr,
 	uint64_t now,
 	uint64_t nwid,
 	uint64_t sourceMac,
@@ -147,7 +149,7 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
 	_now = now;
 	SharedPtr<Network> nw(this->network(nwid));
 	if (nw) {
-		RR->sw->onLocalEthernet(nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
+		RR->sw->onLocalEthernet(tptr,nw,MAC(sourceMac),MAC(destMac),etherType,vlanId,frameData,frameLength);
 		return ZT_RESULT_OK;
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
@@ -156,9 +158,10 @@ ZT_ResultCode Node::processVirtualNetworkFrame(
 class _PingPeersThatNeedPing
 {
 public:
-	_PingPeersThatNeedPing(const RuntimeEnvironment *renv,Hashtable< Address,std::vector<InetAddress> > &upstreamsToContact,uint64_t now) :
+	_PingPeersThatNeedPing(const RuntimeEnvironment *renv,void *tPtr,Hashtable< Address,std::vector<InetAddress> > &upstreamsToContact,uint64_t now) :
 		lastReceiveFromUpstream(0),
 		RR(renv),
+		_tPtr(tPtr),
 		_upstreamsToContact(upstreamsToContact),
 		_now(now),
 		_bestCurrentUpstream(RR->topology->getUpstreamPeer())
@@ -176,21 +179,21 @@ public:
 			// Upstreams must be pinged constantly over both IPv4 and IPv6 to allow
 			// them to perform three way handshake introductions for both stacks.
 
-			if (!p->doPingAndKeepalive(_now,AF_INET)) {
+			if (!p->doPingAndKeepalive(_tPtr,_now,AF_INET)) {
 				for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
 					const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
 					if (addr.ss_family == AF_INET) {
-						p->sendHELLO(InetAddress(),addr,_now,0);
+						p->sendHELLO(_tPtr,InetAddress(),addr,_now,0);
 						contacted = true;
 						break;
 					}
 				}
 			} else contacted = true;
-			if (!p->doPingAndKeepalive(_now,AF_INET6)) {
+			if (!p->doPingAndKeepalive(_tPtr,_now,AF_INET6)) {
 				for(unsigned long k=0,ptr=(unsigned long)RR->node->prng();k<(unsigned long)upstreamStableEndpoints->size();++k) {
 					const InetAddress &addr = (*upstreamStableEndpoints)[ptr++ % upstreamStableEndpoints->size()];
 					if (addr.ss_family == AF_INET6) {
-						p->sendHELLO(InetAddress(),addr,_now,0);
+						p->sendHELLO(_tPtr,InetAddress(),addr,_now,0);
 						contacted = true;
 						break;
 					}
@@ -200,24 +203,25 @@ public:
 			if ((!contacted)&&(_bestCurrentUpstream)) {
 				const SharedPtr<Path> up(_bestCurrentUpstream->getBestPath(_now,true));
 				if (up)
-					p->sendHELLO(up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
+					p->sendHELLO(_tPtr,up->localAddress(),up->address(),_now,up->nextOutgoingCounter());
 			}
 
 			lastReceiveFromUpstream = std::max(p->lastReceive(),lastReceiveFromUpstream);
 			_upstreamsToContact.erase(p->address()); // erase from upstreams to contact so that we can WHOIS those that remain
 		} else if (p->isActive(_now)) {
-			p->doPingAndKeepalive(_now,-1);
+			p->doPingAndKeepalive(_tPtr,_now,-1);
 		}
 	}
 
 private:
 	const RuntimeEnvironment *RR;
+	void *_tPtr;
 	Hashtable< Address,std::vector<InetAddress> > &_upstreamsToContact;
 	const uint64_t _now;
 	const SharedPtr<Peer> _bestCurrentUpstream;
 };
 
-ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+ZT_ResultCode Node::processBackgroundTasks(void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
 {
 	_now = now;
 	Mutex::Lock bl(_backgroundTasksLock);
@@ -235,16 +239,16 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
 				for(std::vector< std::pair< uint64_t,SharedPtr<Network> > >::const_iterator n(_networks.begin());n!=_networks.end();++n) {
 					if (((now - n->second->lastConfigUpdate()) >= ZT_NETWORK_AUTOCONF_DELAY)||(!n->second->hasConfig()))
 						needConfig.push_back(n->second);
-					n->second->sendUpdatesToMembers();
+					n->second->sendUpdatesToMembers(tptr);
 				}
 			}
 			for(std::vector< SharedPtr<Network> >::const_iterator n(needConfig.begin());n!=needConfig.end();++n)
-				(*n)->requestConfiguration();
+				(*n)->requestConfiguration(tptr);
 
 			// Do pings and keepalives
 			Hashtable< Address,std::vector<InetAddress> > upstreamsToContact;
 			RR->topology->getUpstreamsToContact(upstreamsToContact);
-			_PingPeersThatNeedPing pfunc(RR,upstreamsToContact,now);
+			_PingPeersThatNeedPing pfunc(RR,tptr,upstreamsToContact,now);
 			RR->topology->eachPeer<_PingPeersThatNeedPing &>(pfunc);
 
 			// Run WHOIS to create Peer for any upstreams we could not contact (including pending moon seeds)
@@ -252,13 +256,13 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
 			Address *upstreamAddress = (Address *)0;
 			std::vector<InetAddress> *upstreamStableEndpoints = (std::vector<InetAddress> *)0;
 			while (i.next(upstreamAddress,upstreamStableEndpoints))
-				RR->sw->requestWhois(*upstreamAddress);
+				RR->sw->requestWhois(tptr,*upstreamAddress);
 
 			// Update online status, post status change as event
 			const bool oldOnline = _online;
 			_online = (((now - pfunc.lastReceiveFromUpstream) < ZT_PEER_ACTIVITY_TIMEOUT)||(RR->topology->amRoot()));
 			if (oldOnline != _online)
-				postEvent(_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
+				postEvent(tptr,_online ? ZT_EVENT_ONLINE : ZT_EVENT_OFFLINE);
 		} catch ( ... ) {
 			return ZT_RESULT_FATAL_ERROR_INTERNAL;
 		}
@@ -286,7 +290,7 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
 			*nextBackgroundTaskDeadline = now + ZT_CLUSTER_PERIODIC_TASK_PERIOD; // this is really short so just tick at this rate
 		} else {
 #endif
-			*nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
+			*nextBackgroundTaskDeadline = now + (uint64_t)std::max(std::min(timeUntilNextPingCheck,RR->sw->doTimerTasks(tptr,now)),(unsigned long)ZT_CORE_TIMER_TASK_GRANULARITY);
 #ifdef ZT_ENABLE_CLUSTER
 		}
 #endif
@@ -297,17 +301,17 @@ ZT_ResultCode Node::processBackgroundTasks(uint64_t now,volatile uint64_t *nextB
 	return ZT_RESULT_OK;
 }
 
-ZT_ResultCode Node::join(uint64_t nwid,void *uptr)
+ZT_ResultCode Node::join(uint64_t nwid,void *uptr,void *tptr)
 {
 	Mutex::Lock _l(_networks_m);
 	SharedPtr<Network> nw = _network(nwid);
 	if(!nw)
-		_networks.push_back(std::pair< uint64_t,SharedPtr<Network> >(nwid,SharedPtr<Network>(new Network(RR,nwid,uptr))));
+		_networks.push_back(std::pair< uint64_t,SharedPtr<Network> >(nwid,SharedPtr<Network>(new Network(RR,tptr,nwid,uptr))));
 	std::sort(_networks.begin(),_networks.end()); // will sort by nwid since it's the first in a pair<>
 	return ZT_RESULT_OK;
 }
 
-ZT_ResultCode Node::leave(uint64_t nwid,void **uptr)
+ZT_ResultCode Node::leave(uint64_t nwid,void **uptr,void *tptr)
 {
 	std::vector< std::pair< uint64_t,SharedPtr<Network> > > newn;
 	Mutex::Lock _l(_networks_m);
@@ -324,11 +328,11 @@ ZT_ResultCode Node::leave(uint64_t nwid,void **uptr)
 	return ZT_RESULT_OK;
 }
 
-ZT_ResultCode Node::multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+ZT_ResultCode Node::multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
 {
 	SharedPtr<Network> nw(this->network(nwid));
 	if (nw) {
-		nw->multicastSubscribe(MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
+		nw->multicastSubscribe(tptr,MulticastGroup(MAC(multicastGroup),(uint32_t)(multicastAdi & 0xffffffff)));
 		return ZT_RESULT_OK;
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
@@ -342,15 +346,15 @@ ZT_ResultCode Node::multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,u
 	} else return ZT_RESULT_ERROR_NETWORK_NOT_FOUND;
 }
 
-ZT_ResultCode Node::orbit(uint64_t moonWorldId,uint64_t moonSeed)
+ZT_ResultCode Node::orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
 {
-	RR->topology->addMoon(moonWorldId,Address(moonSeed));
+	RR->topology->addMoon(tptr,moonWorldId,Address(moonSeed));
 	return ZT_RESULT_OK;
 }
 
-ZT_ResultCode Node::deorbit(uint64_t moonWorldId)
+ZT_ResultCode Node::deorbit(void *tptr,uint64_t moonWorldId)
 {
-	RR->topology->removeMoon(moonWorldId);
+	RR->topology->removeMoon(tptr,moonWorldId);
 	return ZT_RESULT_OK;
 }
 
@@ -465,7 +469,7 @@ void Node::clearLocalInterfaceAddresses()
 	_directPaths.clear();
 }
 
-int Node::sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
+int Node::sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
 {
 	try {
 		if (RR->identity.address().toInt() != dest) {
@@ -473,7 +477,7 @@ int Node::sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigne
 			outp.append(typeId);
 			outp.append(data,len);
 			outp.compress();
-			RR->sw->send(outp,true);
+			RR->sw->send(tptr,outp,true);
 			return 1;
 		}
 	} catch ( ... ) {}
@@ -486,7 +490,7 @@ void Node::setNetconfMaster(void *networkControllerInstance)
 	RR->localNetworkController->init(RR->identity,this);
 }
 
-ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+ZT_ResultCode Node::circuitTestBegin(void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
 {
 	if (test->hopCount > 0) {
 		try {
@@ -516,7 +520,7 @@ ZT_ResultCode Node::circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)
 			for(unsigned int a=0;a<test->hops[0].breadth;++a) {
 				outp.newInitializationVector();
 				outp.setDestination(Address(test->hops[0].addresses[a]));
-				RR->sw->send(outp,true);
+				RR->sw->send(tptr,outp,true);
 			}
 		} catch ( ... ) {
 			return ZT_RESULT_FATAL_ERROR_INTERNAL; // probably indicates FIFO too big for packet
@@ -616,13 +620,13 @@ void Node::clusterStatus(ZT_ClusterStatus *cs)
 /* Node methods used only within node/                                      */
 /****************************************************************************/
 
-std::string Node::dataStoreGet(const char *name)
+std::string Node::dataStoreGet(void *tPtr,const char *name)
 {
 	char buf[1024];
 	std::string r;
 	unsigned long olen = 0;
 	do {
-		long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
+		long n = _cb.dataStoreGetFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,buf,sizeof(buf),(unsigned long)r.length(),&olen);
 		if (n <= 0)
 			return std::string();
 		r.append(buf,n);
@@ -630,7 +634,7 @@ std::string Node::dataStoreGet(const char *name)
 	return r;
 }
 
-bool Node::shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
+bool Node::shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress)
 {
 	if (!Path::isAddressValidForPath(remoteAddress))
 		return false;
@@ -650,7 +654,7 @@ bool Node::shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddre
 		}
 	}
 
-	return ( (_cb.pathCheckFunction) ? (_cb.pathCheckFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,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(),reinterpret_cast<const struct sockaddr_storage *>(&localAddress),reinterpret_cast<const struct sockaddr_storage *>(&remoteAddress)) != 0) : true);
 }
 
 #ifdef ZT_TRACE
@@ -728,7 +732,7 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de
 	if (destination == RR->identity.address()) {
 		SharedPtr<Network> n(network(nwid));
 		if (!n) return;
-		n->setConfiguration(nc,true);
+		n->setConfiguration((void *)0,nc,true);
 	} else {
 		Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY> *dconf = new Dictionary<ZT_NETWORKCONFIG_DICT_CAPACITY>();
 		try {
@@ -762,7 +766,7 @@ void Node::ncSendConfig(uint64_t nwid,uint64_t requestPacketId,const Address &de
 					outp.append(sig.data,ZT_C25519_SIGNATURE_LEN);
 
 					outp.compress();
-					RR->sw->send(outp,true);
+					RR->sw->send((void *)0,outp,true);
 					chunkIndex += chunkLen;
 				}
 			}
@@ -779,7 +783,7 @@ void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
 	if (destination == RR->identity.address()) {
 		SharedPtr<Network> n(network(rev.networkId()));
 		if (!n) return;
-		n->addCredential(RR->identity.address(),rev);
+		n->addCredential((void *)0,RR->identity.address(),rev);
 	} else {
 		Packet outp(destination,RR->identity.address(),Packet::VERB_NETWORK_CREDENTIALS);
 		outp.append((uint8_t)0x00);
@@ -788,7 +792,7 @@ void Node::ncSendRevocation(const Address &destination,const Revocation &rev)
 		outp.append((uint16_t)1);
 		rev.serialize(outp);
 		outp.append((uint16_t)0);
-		RR->sw->send(outp,true);
+		RR->sw->send((void *)0,outp,true);
 	}
 }
 
@@ -823,7 +827,7 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
 				break;
 		}
 		outp.append(nwid);
-		RR->sw->send(outp,true);
+		RR->sw->send((void *)0,outp,true);
 	} // else we can't send an ERROR() in response to nothing, so discard
 }
 
@@ -835,11 +839,11 @@ void Node::ncSendError(uint64_t nwid,uint64_t requestPacketId,const Address &des
 
 extern "C" {
 
-enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now)
+enum ZT_ResultCode ZT_Node_new(ZT_Node **node,void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now)
 {
 	*node = (ZT_Node *)0;
 	try {
-		*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,callbacks,now));
+		*node = reinterpret_cast<ZT_Node *>(new ZeroTier::Node(uptr,tptr,callbacks,now));
 		return ZT_RESULT_OK;
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
@@ -859,6 +863,7 @@ void ZT_Node_delete(ZT_Node *node)
 
 enum ZT_ResultCode ZT_Node_processWirePacket(
 	ZT_Node *node,
+	void *tptr,
 	uint64_t now,
 	const struct sockaddr_storage *localAddress,
 	const struct sockaddr_storage *remoteAddress,
@@ -867,7 +872,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
 	volatile uint64_t *nextBackgroundTaskDeadline)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
+		return reinterpret_cast<ZeroTier::Node *>(node)->processWirePacket(tptr,now,localAddress,remoteAddress,packetData,packetLength,nextBackgroundTaskDeadline);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -877,6 +882,7 @@ enum ZT_ResultCode ZT_Node_processWirePacket(
 
 enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
 	ZT_Node *node,
+	void *tptr,
 	uint64_t now,
 	uint64_t nwid,
 	uint64_t sourceMac,
@@ -888,7 +894,7 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
 	volatile uint64_t *nextBackgroundTaskDeadline)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
+		return reinterpret_cast<ZeroTier::Node *>(node)->processVirtualNetworkFrame(tptr,now,nwid,sourceMac,destMac,etherType,vlanId,frameData,frameLength,nextBackgroundTaskDeadline);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -896,10 +902,10 @@ enum ZT_ResultCode ZT_Node_processVirtualNetworkFrame(
 	}
 }
 
-enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
+enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(now,nextBackgroundTaskDeadline);
+		return reinterpret_cast<ZeroTier::Node *>(node)->processBackgroundTasks(tptr,now,nextBackgroundTaskDeadline);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -907,10 +913,10 @@ enum ZT_ResultCode ZT_Node_processBackgroundTasks(ZT_Node *node,uint64_t now,vol
 	}
 }
 
-enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
+enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr,void *tptr)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr);
+		return reinterpret_cast<ZeroTier::Node *>(node)->join(nwid,uptr,tptr);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -918,10 +924,10 @@ enum ZT_ResultCode ZT_Node_join(ZT_Node *node,uint64_t nwid,void *uptr)
 	}
 }
 
-enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
+enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr,void *tptr)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr);
+		return reinterpret_cast<ZeroTier::Node *>(node)->leave(nwid,uptr,tptr);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -929,10 +935,10 @@ enum ZT_ResultCode ZT_Node_leave(ZT_Node *node,uint64_t nwid,void **uptr)
 	}
 }
 
-enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
+enum ZT_ResultCode ZT_Node_multicastSubscribe(ZT_Node *node,void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(nwid,multicastGroup,multicastAdi);
+		return reinterpret_cast<ZeroTier::Node *>(node)->multicastSubscribe(tptr,nwid,multicastGroup,multicastAdi);
 	} catch (std::bad_alloc &exc) {
 		return ZT_RESULT_FATAL_ERROR_OUT_OF_MEMORY;
 	} catch ( ... ) {
@@ -951,19 +957,19 @@ enum ZT_ResultCode ZT_Node_multicastUnsubscribe(ZT_Node *node,uint64_t nwid,uint
 	}
 }
 
-enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,uint64_t moonWorldId,uint64_t moonSeed)
+enum ZT_ResultCode ZT_Node_orbit(ZT_Node *node,void *tptr,uint64_t moonWorldId,uint64_t moonSeed)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->orbit(moonWorldId,moonSeed);
+		return reinterpret_cast<ZeroTier::Node *>(node)->orbit(tptr,moonWorldId,moonSeed);
 	} catch ( ... ) {
 		return ZT_RESULT_FATAL_ERROR_INTERNAL;
 	}
 }
 
-ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,uint64_t moonWorldId)
+ZT_ResultCode ZT_Node_deorbit(ZT_Node *node,void *tptr,uint64_t moonWorldId)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(moonWorldId);
+		return reinterpret_cast<ZeroTier::Node *>(node)->deorbit(tptr,moonWorldId);
 	} catch ( ... ) {
 		return ZT_RESULT_FATAL_ERROR_INTERNAL;
 	}
@@ -1031,10 +1037,10 @@ void ZT_Node_clearLocalInterfaceAddresses(ZT_Node *node)
 	} catch ( ... ) {}
 }
 
-int ZT_Node_sendUserMessage(ZT_Node *node,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
+int ZT_Node_sendUserMessage(ZT_Node *node,void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len)
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(dest,typeId,data,len);
+		return reinterpret_cast<ZeroTier::Node *>(node)->sendUserMessage(tptr,dest,typeId,data,len);
 	} catch ( ... ) {
 		return 0;
 	}
@@ -1047,10 +1053,10 @@ void ZT_Node_setNetconfMaster(ZT_Node *node,void *networkControllerInstance)
 	} catch ( ... ) {}
 }
 
-enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
+enum ZT_ResultCode ZT_Node_circuitTestBegin(ZT_Node *node,void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *))
 {
 	try {
-		return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(test,reportCallback);
+		return reinterpret_cast<ZeroTier::Node *>(node)->circuitTestBegin(tptr,test,reportCallback);
 	} catch ( ... ) {
 		return ZT_RESULT_FATAL_ERROR_INTERNAL;
 	}

+ 23 - 19
node/Node.hpp

@@ -65,7 +65,7 @@ class World;
 class Node : public NetworkController::Sender
 {
 public:
-	Node(void *uptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
+	Node(void *uptr,void *tptr,const struct ZT_Node_Callbacks *callbacks,uint64_t now);
 	virtual ~Node();
 
 	// Get rid of alignment warnings on 32-bit Windows and possibly improve performance
@@ -77,6 +77,7 @@ public:
 	// Public API Functions ----------------------------------------------------
 
 	ZT_ResultCode processWirePacket(
+		void *tptr,
 		uint64_t now,
 		const struct sockaddr_storage *localAddress,
 		const struct sockaddr_storage *remoteAddress,
@@ -84,6 +85,7 @@ public:
 		unsigned int packetLength,
 		volatile uint64_t *nextBackgroundTaskDeadline);
 	ZT_ResultCode processVirtualNetworkFrame(
+		void *tptr,
 		uint64_t now,
 		uint64_t nwid,
 		uint64_t sourceMac,
@@ -93,13 +95,13 @@ public:
 		const void *frameData,
 		unsigned int frameLength,
 		volatile uint64_t *nextBackgroundTaskDeadline);
-	ZT_ResultCode processBackgroundTasks(uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
-	ZT_ResultCode join(uint64_t nwid,void *uptr);
-	ZT_ResultCode leave(uint64_t nwid,void **uptr);
-	ZT_ResultCode multicastSubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
+	ZT_ResultCode processBackgroundTasks(void *tptr,uint64_t now,volatile uint64_t *nextBackgroundTaskDeadline);
+	ZT_ResultCode join(uint64_t nwid,void *uptr,void *tptr);
+	ZT_ResultCode leave(uint64_t nwid,void **uptr,void *tptr);
+	ZT_ResultCode multicastSubscribe(void *tptr,uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
 	ZT_ResultCode multicastUnsubscribe(uint64_t nwid,uint64_t multicastGroup,unsigned long multicastAdi);
-	ZT_ResultCode orbit(uint64_t moonWorldId,uint64_t moonSeed);
-	ZT_ResultCode deorbit(uint64_t moonWorldId);
+	ZT_ResultCode orbit(void *tptr,uint64_t moonWorldId,uint64_t moonSeed);
+	ZT_ResultCode deorbit(void *tptr,uint64_t moonWorldId);
 	uint64_t address() const;
 	void status(ZT_NodeStatus *status) const;
 	ZT_PeerList *peers() const;
@@ -108,9 +110,9 @@ public:
 	void freeQueryResult(void *qr);
 	int addLocalInterfaceAddress(const struct sockaddr_storage *addr);
 	void clearLocalInterfaceAddresses();
-	int sendUserMessage(uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
+	int sendUserMessage(void *tptr,uint64_t dest,uint64_t typeId,const void *data,unsigned int len);
 	void setNetconfMaster(void *networkControllerInstance);
-	ZT_ResultCode circuitTestBegin(ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
+	ZT_ResultCode circuitTestBegin(void *tptr,ZT_CircuitTest *test,void (*reportCallback)(ZT_Node *,ZT_CircuitTest *,const ZT_CircuitTestReport *));
 	void circuitTestEnd(ZT_CircuitTest *test);
 	ZT_ResultCode clusterInit(
 		unsigned int myId,
@@ -132,11 +134,12 @@ public:
 
 	inline uint64_t now() const throw() { return _now; }
 
-	inline bool putPacket(const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
+	inline bool putPacket(void *tPtr,const InetAddress &localAddress,const InetAddress &addr,const void *data,unsigned int len,unsigned int ttl = 0)
 	{
 		return (_cb.wirePacketSendFunction(
 			reinterpret_cast<ZT_Node *>(this),
 			_uPtr,
+			tPtr,
 			reinterpret_cast<const struct sockaddr_storage *>(&localAddress),
 			reinterpret_cast<const struct sockaddr_storage *>(&addr),
 			data,
@@ -144,11 +147,12 @@ public:
 			ttl) == 0);
 	}
 
-	inline void putFrame(uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+	inline void putFrame(void *tPtr,uint64_t nwid,void **nuptr,const MAC &source,const MAC &dest,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 	{
 		_cb.virtualNetworkFrameFunction(
 			reinterpret_cast<ZT_Node *>(this),
 			_uPtr,
+			tPtr,
 			nwid,
 			nuptr,
 			source.toInt(),
@@ -191,14 +195,14 @@ public:
 		return _directPaths;
 	}
 
-	inline bool dataStorePut(const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,data,len,(int)secure) == 0); }
-	inline bool dataStorePut(const char *name,const std::string &data,bool secure) { return dataStorePut(name,(const void *)data.data(),(unsigned int)data.length(),secure); }
-	inline void dataStoreDelete(const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,name,(const void *)0,0,0); }
-	std::string dataStoreGet(const char *name);
+	inline bool dataStorePut(void *tPtr,const char *name,const void *data,unsigned int len,bool secure) { return (_cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,data,len,(int)secure) == 0); }
+	inline bool dataStorePut(void *tPtr,const char *name,const std::string &data,bool secure) { return dataStorePut(tPtr,name,(const void *)data.data(),(unsigned int)data.length(),secure); }
+	inline void dataStoreDelete(void *tPtr,const char *name) { _cb.dataStorePutFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,name,(const void *)0,0,0); }
+	std::string dataStoreGet(void *tPtr,const char *name);
 
-	inline void postEvent(ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,ev,md); }
+	inline void postEvent(void *tPtr,ZT_Event ev,const void *md = (const void *)0) { _cb.eventCallback(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,ev,md); }
 
-	inline int configureVirtualNetworkPort(uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,nwid,nuptr,op,nc); }
+	inline int configureVirtualNetworkPort(void *tPtr,uint64_t nwid,void **nuptr,ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nc) { return _cb.virtualNetworkConfigFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,tPtr,nwid,nuptr,op,nc); }
 
 	inline bool online() const throw() { return _online; }
 
@@ -206,8 +210,8 @@ public:
 	void postTrace(const char *module,unsigned int line,const char *fmt,...);
 #endif
 
-	bool shouldUsePathForZeroTierTraffic(const Address &ztaddr,const InetAddress &localAddress,const InetAddress &remoteAddress);
-	inline bool externalPathLookup(const Address &ztaddr,int family,InetAddress &addr) { return ( (_cb.pathLookupFunction) ? (_cb.pathLookupFunction(reinterpret_cast<ZT_Node *>(this),_uPtr,ztaddr.toInt(),family,reinterpret_cast<struct sockaddr_storage *>(&addr)) != 0) : false ); }
+	bool shouldUsePathForZeroTierTraffic(void *tPtr,const Address &ztaddr,const InetAddress &localAddress,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 ); }
 
 	uint64_t prng();
 	void postCircuitTestReport(const ZT_CircuitTestReport *report);

+ 3 - 3
node/OutboundMulticast.cpp

@@ -85,18 +85,18 @@ void OutboundMulticast::init(
 	memcpy(_frameData,payload,_frameLen);
 }
 
-void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,const Address &toAddr)
+void OutboundMulticast::sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
 {
 	const SharedPtr<Network> nw(RR->node->network(_nwid));
 	const Address toAddr2(toAddr);
-	if ((nw)&&(nw->filterOutgoingPacket(true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
+	if ((nw)&&(nw->filterOutgoingPacket(tPtr,true,RR->identity.address(),toAddr2,_macSrc,_macDest,_frameData,_frameLen,_etherType,0))) {
 		//TRACE(">>MC %.16llx -> %s",(unsigned long long)this,toAddr.toString().c_str());
 		_packet.newInitializationVector();
 		_packet.setDestination(toAddr2);
 		RR->node->expectReplyTo(_packet.packetId());
 
 		Packet tmp(_packet); // make a copy of packet so as not to garble the original -- GitHub issue #461
-		RR->sw->send(tmp,true);
+		RR->sw->send(tPtr,tmp,true);
 	}
 }
 

+ 8 - 5
node/OutboundMulticast.hpp

@@ -99,33 +99,36 @@ public:
 	 * Just send without checking log
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param toAddr Destination address
 	 */
-	void sendOnly(const RuntimeEnvironment *RR,const Address &toAddr);
+	void sendOnly(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr);
 
 	/**
 	 * Just send and log but do not check sent log
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param toAddr Destination address
 	 */
-	inline void sendAndLog(const RuntimeEnvironment *RR,const Address &toAddr)
+	inline void sendAndLog(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
 	{
 		_alreadySentTo.push_back(toAddr);
-		sendOnly(RR,toAddr);
+		sendOnly(RR,tPtr,toAddr);
 	}
 
 	/**
 	 * Try to send this to a given peer if it hasn't been sent to them already
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param toAddr Destination address
 	 * @return True if address is new and packet was sent to switch, false if duplicate
 	 */
-	inline bool sendIfNew(const RuntimeEnvironment *RR,const Address &toAddr)
+	inline bool sendIfNew(const RuntimeEnvironment *RR,void *tPtr,const Address &toAddr)
 	{
 		if (std::find(_alreadySentTo.begin(),_alreadySentTo.end(),toAddr) == _alreadySentTo.end()) {
-			sendAndLog(RR,toAddr);
+			sendAndLog(RR,tPtr,toAddr);
 			return true;
 		} else {
 			return false;

+ 2 - 2
node/Path.cpp

@@ -22,9 +22,9 @@
 
 namespace ZeroTier {
 
-bool Path::send(const RuntimeEnvironment *RR,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(_localAddress,address(),data,len)) {
+	if (RR->node->putPacket(tPtr,_localAddress,address(),data,len)) {
 		_lastOut = now;
 		return true;
 	}

+ 2 - 1
node/Path.hpp

@@ -186,12 +186,13 @@ public:
 	 * Send a packet via this path (last out time is also updated)
 	 *
 	 * @param RR Runtime environment
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param data Packet data
 	 * @param len Packet length
 	 * @param now Current time
 	 * @return True if transport reported success
 	 */
-	bool send(const RuntimeEnvironment *RR,const void *data,unsigned int len,uint64_t now);
+	bool send(const RuntimeEnvironment *RR,void *tPtr,const void *data,unsigned int len,uint64_t now);
 
 	/**
 	 * Manually update last sent time

+ 19 - 18
node/Peer.cpp

@@ -68,6 +68,7 @@ Peer::Peer(const RuntimeEnvironment *renv,const Identity &myIdentity,const Ident
 }
 
 void Peer::received(
+	void *tPtr,
 	const SharedPtr<Path> &path,
 	const unsigned int hops,
 	const uint64_t packetId,
@@ -161,7 +162,7 @@ void Peer::received(
 			}
 		}
 
-		if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(_id.address(),path->localAddress(),path->address())) ) {
+		if ( (!pathIsConfirmed) && (RR->node->shouldUsePathForZeroTierTraffic(tPtr,_id.address(),path->localAddress(),path->address())) ) {
 			if (verb == Packet::VERB_OK) {
 				Mutex::Lock _l(_paths_m);
 
@@ -206,7 +207,7 @@ void Peer::received(
 #endif
 			} else {
 				TRACE("got %s via unknown path %s(%s), confirming...",Packet::verbString(verb),_id.address().toString().c_str(),path->address().toString().c_str());
-				attemptToContactAt(path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
+				attemptToContactAt(tPtr,path->localAddress(),path->address(),now,true,path->nextOutgoingCounter());
 				path->sent(now);
 			}
 		}
@@ -281,7 +282,7 @@ void Peer::received(
 					if (count) {
 						outp.setAt(ZT_PACKET_IDX_PAYLOAD,(uint16_t)count);
 						outp.armor(_key,true,path->nextOutgoingCounter());
-						path->send(RR,outp.data(),outp.size(),now);
+						path->send(RR,tPtr,outp.data(),outp.size(),now);
 					}
 				}
 			}
@@ -299,7 +300,7 @@ bool Peer::hasActivePathTo(uint64_t now,const InetAddress &addr) const
 	return false;
 }
 
-bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead)
+bool Peer::sendDirect(void *tPtr,const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead)
 {
 	Mutex::Lock _l(_paths_m);
 
@@ -316,7 +317,7 @@ bool Peer::sendDirect(const void *data,unsigned int len,uint64_t now,bool forceE
 	}
 
 	if (bestp >= 0) {
-		return _paths[bestp].path->send(RR,data,len,now);
+		return _paths[bestp].path->send(RR,tPtr,data,len,now);
 	} else {
 		return false;
 	}
@@ -345,7 +346,7 @@ SharedPtr<Path> Peer::getBestPath(uint64_t now,bool includeExpired)
 	}
 }
 
-void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
+void Peer::sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter)
 {
 	Packet outp(_id.address(),RR->identity.address(),Packet::VERB_HELLO);
 
@@ -387,35 +388,35 @@ void Peer::sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,u
 
 	if (atAddress) {
 		outp.armor(_key,false,counter); // false == don't encrypt full payload, but add MAC
-		RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
+		RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
 	} else {
-		RR->sw->send(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(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
+void Peer::attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter)
 {
 	if ( (!sendFullHello) && (_vProto >= 5) && (!((_vMajor == 1)&&(_vMinor == 1)&&(_vRevision == 0))) ) {
 		Packet outp(_id.address(),RR->identity.address(),Packet::VERB_ECHO);
 		RR->node->expectReplyTo(outp.packetId());
 		outp.armor(_key,true,counter);
-		RR->node->putPacket(localAddr,atAddress,outp.data(),outp.size());
+		RR->node->putPacket(tPtr,localAddr,atAddress,outp.data(),outp.size());
 	} else {
-		sendHELLO(localAddr,atAddress,now,counter);
+		sendHELLO(tPtr,localAddr,atAddress,now,counter);
 	}
 }
 
-void Peer::tryMemorizedPath(uint64_t now)
+void Peer::tryMemorizedPath(void *tPtr,uint64_t now)
 {
 	if ((now - _lastTriedMemorizedPath) >= ZT_TRY_MEMORIZED_PATH_INTERVAL) {
 		_lastTriedMemorizedPath = now;
 		InetAddress mp;
-		if (RR->node->externalPathLookup(_id.address(),-1,mp))
-			attemptToContactAt(InetAddress(),mp,now,true,0);
+		if (RR->node->externalPathLookup(tPtr,_id.address(),-1,mp))
+			attemptToContactAt(tPtr,InetAddress(),mp,now,true,0);
 	}
 }
 
-bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
+bool Peer::doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily)
 {
 	Mutex::Lock _l(_paths_m);
 
@@ -433,7 +434,7 @@ bool Peer::doPingAndKeepalive(uint64_t now,int inetAddressFamily)
 
 	if (bestp >= 0) {
 		if ( ((now - _paths[bestp].lastReceive) >= ZT_PEER_PING_PERIOD) || (_paths[bestp].path->needsHeartbeat(now)) ) {
-			attemptToContactAt(_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false,_paths[bestp].path->nextOutgoingCounter());
+			attemptToContactAt(tPtr,_paths[bestp].path->localAddress(),_paths[bestp].path->address(),now,false,_paths[bestp].path->nextOutgoingCounter());
 			_paths[bestp].path->sent(now);
 		}
 		return true;
@@ -452,12 +453,12 @@ bool Peer::hasActiveDirectPath(uint64_t now) const
 	return false;
 }
 
-void Peer::resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
+void Peer::resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,uint64_t now)
 {
 	Mutex::Lock _l(_paths_m);
 	for(unsigned int p=0;p<_numPaths;++p) {
 		if ( (_paths[p].path->address().ss_family == inetAddressFamily) && (_paths[p].path->address().ipScope() == scope) ) {
-			attemptToContactAt(_paths[p].path->localAddress(),_paths[p].path->address(),now,false,_paths[p].path->nextOutgoingCounter());
+			attemptToContactAt(tPtr,_paths[p].path->localAddress(),_paths[p].path->address(),now,false,_paths[p].path->nextOutgoingCounter());
 			_paths[p].path->sent(now);
 			_paths[p].lastReceive = 0; // path will not be used unless it speaks again
 		}

+ 16 - 6
node/Peer.hpp

@@ -84,6 +84,7 @@ public:
 	 * This is called by the decode pipe when a packet is proven to be authentic
 	 * and appears to be valid.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param path Path over which packet was received
 	 * @param hops ZeroTier (not IP) hops
 	 * @param packetId Packet ID
@@ -93,6 +94,7 @@ public:
 	 * @param trustEstablished If true, some form of non-trivial trust (like allowed in network) has been established
 	 */
 	void received(
+		void *tPtr,
 		const SharedPtr<Path> &path,
 		const unsigned int hops,
 		const uint64_t packetId,
@@ -125,13 +127,14 @@ public:
 	/**
 	 * Send via best direct path
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param data Packet data
 	 * @param len Packet length
 	 * @param now Current time
 	 * @param forceEvenIfDead If true, send even if the path is not 'alive'
 	 * @return True if we actually sent something
 	 */
-	bool sendDirect(const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead);
+	bool sendDirect(void *tPtr,const void *data,unsigned int len,uint64_t now,bool forceEvenIfDead);
 
 	/**
 	 * Get the best current direct path
@@ -147,41 +150,47 @@ public:
 	 *
 	 * 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 localAddr Local address
 	 * @param atAddress Destination address
 	 * @param now Current time
 	 * @param counter Outgoing packet counter
 	 */
-	void sendHELLO(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
+	void sendHELLO(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,unsigned int counter);
 
 	/**
 	 * Send ECHO (or HELLO for older peers) to this peer at the given address
 	 *
 	 * 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 localAddr Local address
 	 * @param atAddress Destination address
 	 * @param now Current time
 	 * @param sendFullHello If true, always send a full HELLO instead of just an ECHO
 	 * @param counter Outgoing packet counter
 	 */
-	void attemptToContactAt(const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
+	void attemptToContactAt(void *tPtr,const InetAddress &localAddr,const InetAddress &atAddress,uint64_t now,bool sendFullHello,unsigned int counter);
 
 	/**
 	 * Try a memorized or statically defined path if any are known
 	 *
 	 * Under the hood this is done periodically based on ZT_TRY_MEMORIZED_PATH_INTERVAL.
+	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
+	 * @param now Current time
 	 */
-	void tryMemorizedPath(uint64_t now);
+	void tryMemorizedPath(void *tPtr,uint64_t now);
 
 	/**
 	 * Send pings or keepalives depending on configured timeouts
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param now Current time
 	 * @param inetAddressFamily Keep this address family alive, or -1 for any
 	 * @return True if we have at least one direct path of the given family (or any if family is -1)
 	 */
-	bool doPingAndKeepalive(uint64_t now,int inetAddressFamily);
+	bool doPingAndKeepalive(void *tPtr,uint64_t now,int inetAddressFamily);
 
 	/**
 	 * @param now Current time
@@ -195,11 +204,12 @@ public:
 	 * Resetting a path involves sending an ECHO to it and then deactivating
 	 * it until or unless it responds.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param scope IP scope
 	 * @param inetAddressFamily Family e.g. AF_INET
 	 * @param now Current time
 	 */
-	void resetWithinScope(InetAddress::IpScope scope,int inetAddressFamily,uint64_t now);
+	void resetWithinScope(void *tPtr,InetAddress::IpScope scope,int inetAddressFamily,uint64_t now);
 
 	/**
 	 * Get most recently active path addresses for IPv4 and/or IPv6

+ 3 - 3
node/Revocation.cpp

@@ -25,13 +25,13 @@
 
 namespace ZeroTier {
 
-int Revocation::verify(const RuntimeEnvironment *RR) const
+int Revocation::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
 		return -1;
-	const Identity id(RR->topology->getIdentity(_signedBy));
+	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
 	if (!id) {
-		RR->sw->requestWhois(_signedBy);
+		RR->sw->requestWhois(tPtr,_signedBy);
 		return 1;
 	}
 	try {

+ 2 - 1
node/Revocation.hpp

@@ -113,9 +113,10 @@ public:
 	 * Verify this revocation's signature
 	 *
 	 * @param RR Runtime environment to provide for peer lookup, etc.
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or chain
 	 */
-	int verify(const RuntimeEnvironment *RR) const;
+	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const

+ 6 - 4
node/SelfAwareness.cpp

@@ -40,15 +40,17 @@ namespace ZeroTier {
 class _ResetWithinScope
 {
 public:
-	_ResetWithinScope(uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
+	_ResetWithinScope(void *tPtr,uint64_t now,int inetAddressFamily,InetAddress::IpScope scope) :
 		_now(now),
+		_tPtr(tPtr),
 		_family(inetAddressFamily),
 		_scope(scope) {}
 
-	inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_scope,_family,_now); }
+	inline void operator()(Topology &t,const SharedPtr<Peer> &p) { p->resetWithinScope(_tPtr,_scope,_family,_now); }
 
 private:
 	uint64_t _now;
+	void *_tPtr;
 	int _family;
 	InetAddress::IpScope _scope;
 };
@@ -59,7 +61,7 @@ SelfAwareness::SelfAwareness(const RuntimeEnvironment *renv) :
 {
 }
 
-void SelfAwareness::iam(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 InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now)
 {
 	const InetAddress::IpScope scope = myPhysicalAddress.ipScope();
 
@@ -91,7 +93,7 @@ void SelfAwareness::iam(const Address &reporter,const InetAddress &receivedOnLoc
 		}
 
 		// Reset all paths within this scope and address family
-		_ResetWithinScope rset(now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
+		_ResetWithinScope rset(tPtr,now,myPhysicalAddress.ss_family,(InetAddress::IpScope)scope);
 		RR->topology->eachPeer<_ResetWithinScope &>(rset);
 	} else {
 		// Otherwise just update DB to use to determine external surface info

+ 1 - 1
node/SelfAwareness.hpp

@@ -47,7 +47,7 @@ public:
 	 * @param trusted True if this peer is trusted as an authority to inform us of external address changes
 	 * @param now Current time
 	 */
-	void iam(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 InetAddress &receivedOnLocalAddress,const InetAddress &reporterPhysicalAddress,const InetAddress &myPhysicalAddress,bool trusted,uint64_t now);
 
 	/**
 	 * Clean up database periodically

+ 49 - 48
node/Switch.cpp

@@ -64,7 +64,7 @@ Switch::Switch(const RuntimeEnvironment *renv) :
 {
 }
 
-void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
+void Switch::onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len)
 {
 	try {
 		const uint64_t now = RR->node->now();
@@ -81,15 +81,15 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 			const Address beaconAddr(reinterpret_cast<const char *>(data) + 8,5);
 			if (beaconAddr == RR->identity.address())
 				return;
-			if (!RR->node->shouldUsePathForZeroTierTraffic(beaconAddr,localAddr,fromAddr))
+			if (!RR->node->shouldUsePathForZeroTierTraffic(tPtr,beaconAddr,localAddr,fromAddr))
 				return;
-			const SharedPtr<Peer> peer(RR->topology->getPeer(beaconAddr));
+			const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,beaconAddr));
 			if (peer) { // we'll only respond to beacons from known peers
 				if ((now - _lastBeaconResponse) >= 2500) { // limit rate of responses
 					_lastBeaconResponse = now;
 					Packet outp(peer->address(),RR->identity.address(),Packet::VERB_NOP);
 					outp.armor(peer->key(),true,path->nextOutgoingCounter());
-					path->send(RR,outp.data(),outp.size(),now);
+					path->send(RR,tPtr,outp.data(),outp.size(),now);
 				}
 			}
 
@@ -115,8 +115,8 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 
 						// Note: we don't bother initiating NAT-t for fragments, since heads will set that off.
 						// It wouldn't hurt anything, just redundant and unnecessary.
-						SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
-						if ((!relayTo)||(!relayTo->sendDirect(fragment.data(),fragment.size(),now,false))) {
+						SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+						if ((!relayTo)||(!relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,false))) {
 #ifdef ZT_ENABLE_CLUSTER
 							if ((RR->cluster)&&(!isClusterFrontplane)) {
 								RR->cluster->relayViaCluster(Address(),destination,fragment.data(),fragment.size(),false);
@@ -127,7 +127,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 							// Don't know peer or no direct path -- so relay via someone upstream
 							relayTo = RR->topology->getUpstreamPeer();
 							if (relayTo)
-								relayTo->sendDirect(fragment.data(),fragment.size(),now,true);
+								relayTo->sendDirect(tPtr,fragment.data(),fragment.size(),now,true);
 						}
 					} else {
 						TRACE("dropped relay [fragment](%s) -> %s, max hops exceeded",fromAddr.toString().c_str(),destination.toString().c_str());
@@ -171,7 +171,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 								for(unsigned int f=1;f<totalFragments;++f)
 									rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
 
-								if (rq->frag0.tryDecode(RR)) {
+								if (rq->frag0.tryDecode(RR,tPtr)) {
 									rq->timestamp = 0; // packet decoded, free entry
 								} else {
 									rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
@@ -212,8 +212,8 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 						packet.incrementHops();
 #endif
 
-						SharedPtr<Peer> relayTo = RR->topology->getPeer(destination);
-						if ((relayTo)&&(relayTo->sendDirect(packet.data(),packet.size(),now,false))) {
+						SharedPtr<Peer> relayTo = RR->topology->getPeer(tPtr,destination);
+						if ((relayTo)&&(relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,false))) {
 							if ((source != RR->identity.address())&&(_shouldUnite(now,source,destination))) { // don't send RENDEZVOUS for cluster frontplane relays
 								const InetAddress *hintToSource = (InetAddress *)0;
 								const InetAddress *hintToDest = (InetAddress *)0;
@@ -222,7 +222,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 								InetAddress sourceV4,sourceV6;
 								relayTo->getRendezvousAddresses(now,destV4,destV6);
 
-								const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(source));
+								const SharedPtr<Peer> sourcePeer(RR->topology->getPeer(tPtr,source));
 								if (sourcePeer) {
 									sourcePeer->getRendezvousAddresses(now,sourceV4,sourceV6);
 									if ((destV6)&&(sourceV6)) {
@@ -249,7 +249,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 													outp.append((uint8_t)4);
 													outp.append(hintToSource->rawIpData(),4);
 												}
-												send(outp,true);
+												send(tPtr,outp,true);
 											} else {
 												Packet outp(destination,RR->identity.address(),Packet::VERB_RENDEZVOUS);
 												outp.append((uint8_t)0);
@@ -262,7 +262,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 													outp.append((uint8_t)4);
 													outp.append(hintToDest->rawIpData(),4);
 												}
-												send(outp,true);
+												send(tPtr,outp,true);
 											}
 											++alt;
 										}
@@ -278,7 +278,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 #endif
 							relayTo = RR->topology->getUpstreamPeer(&source,1,true);
 							if (relayTo)
-								relayTo->sendDirect(packet.data(),packet.size(),now,true);
+								relayTo->sendDirect(tPtr,packet.data(),packet.size(),now,true);
 						}
 					} else {
 						TRACE("dropped relay %s(%s) -> %s, max hops exceeded",packet.source().toString().c_str(),fromAddr.toString().c_str(),destination.toString().c_str());
@@ -321,7 +321,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 							for(unsigned int f=1;f<rq->totalFragments;++f)
 								rq->frag0.append(rq->frags[f - 1].payload(),rq->frags[f - 1].payloadLength());
 
-							if (rq->frag0.tryDecode(RR)) {
+							if (rq->frag0.tryDecode(RR,tPtr)) {
 								rq->timestamp = 0; // packet decoded, free entry
 							} else {
 								rq->complete = true; // set complete flag but leave entry since it probably needs WHOIS or something
@@ -334,7 +334,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 				} else {
 					// Packet is unfragmented, so just process it
 					IncomingPacket packet(data,len,path,now);
-					if (!packet.tryDecode(RR)) {
+					if (!packet.tryDecode(RR,tPtr)) {
 						Mutex::Lock _l(_rxQueue_m);
 						RXQueueEntry *rq = &(_rxQueue[ZT_RX_QUEUE_SIZE - 1]);
 						unsigned long i = ZT_RX_QUEUE_SIZE - 1;
@@ -362,7 +362,7 @@ void Switch::onRemotePacket(const InetAddress &localAddr,const InetAddress &from
 	}
 }
 
-void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
+void Switch::onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 {
 	if (!network->hasConfig())
 		return;
@@ -474,7 +474,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 					adv[42] = (checksum >> 8) & 0xff;
 					adv[43] = checksum & 0xff;
 
-					RR->node->putFrame(network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
+					RR->node->putFrame(tPtr,network->id(),network->userPtr(),peerMac,from,ZT_ETHERTYPE_IPV6,0,adv,72);
 					return; // NDP emulation done. We have forged a "fake" reply, so no need to send actual NDP query.
 				} // else no NDP emulation
 			} // else no NDP emulation
@@ -491,17 +491,18 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		 * multicast addresses on bridge interfaces and subscribing each slave.
 		 * But in that case this does no harm, as the sets are just merged. */
 		if (fromBridged)
-			network->learnBridgedMulticastGroup(multicastGroup,RR->node->now());
+			network->learnBridgedMulticastGroup(tPtr,multicastGroup,RR->node->now());
 
 		//TRACE("%.16llx: MULTICAST %s -> %s %s %u",network->id(),from.toString().c_str(),multicastGroup.toString().c_str(),etherTypeName(etherType),len);
 
 		// First pass sets noTee to false, but noTee is set to true in OutboundMulticast to prevent duplicates.
-		if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
 			TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 			return;
 		}
 
 		RR->mc->send(
+			tPtr,
 			network->config().multicastLimit,
 			RR->node->now(),
 			network->id(),
@@ -514,14 +515,14 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 			len);
 	} else if (to == network->mac()) {
 		// Destination is this node, so just reinject it
-		RR->node->putFrame(network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
+		RR->node->putFrame(tPtr,network->id(),network->userPtr(),from,to,etherType,vlanId,data,len);
 	} else if (to[0] == MAC::firstOctetForNetwork(network->id())) {
 		// Destination is another ZeroTier peer on the same network
 
 		Address toZT(to.toAddress(network->id())); // since in-network MACs are derived from addresses and network IDs, we can reverse this
-		SharedPtr<Peer> toPeer(RR->topology->getPeer(toZT));
+		SharedPtr<Peer> toPeer(RR->topology->getPeer(tPtr,toZT));
 
-		if (!network->filterOutgoingPacket(false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),toZT,from,to,(const uint8_t *)data,len,etherType,vlanId)) {
 			TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 			return;
 		}
@@ -536,7 +537,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 			outp.append(data,len);
 			if (!network->config().disableCompression())
 				outp.compress();
-			send(outp,true);
+			send(tPtr,outp,true);
 		} else {
 			Packet outp(toZT,RR->identity.address(),Packet::VERB_FRAME);
 			outp.append(network->id());
@@ -544,7 +545,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 			outp.append(data,len);
 			if (!network->config().disableCompression())
 				outp.compress();
-			send(outp,true);
+			send(tPtr,outp,true);
 		}
 
 		//TRACE("%.16llx: UNICAST: %s -> %s etherType==%s(%.4x) vlanId==%u len==%u fromBridged==%d includeCom==%d",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType),etherType,vlanId,len,(int)fromBridged,(int)includeCom);
@@ -554,7 +555,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		// We filter with a NULL destination ZeroTier address first. Filtrations
 		// for each ZT destination are also done below. This is the same rationale
 		// and design as for multicast.
-		if (!network->filterOutgoingPacket(false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+		if (!network->filterOutgoingPacket(tPtr,false,RR->identity.address(),Address(),from,to,(const uint8_t *)data,len,etherType,vlanId)) {
 			TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 			return;
 		}
@@ -592,7 +593,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 		}
 
 		for(unsigned int b=0;b<numBridges;++b) {
-			if (network->filterOutgoingPacket(true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
+			if (network->filterOutgoingPacket(tPtr,true,RR->identity.address(),bridges[b],from,to,(const uint8_t *)data,len,etherType,vlanId)) {
 				Packet outp(bridges[b],RR->identity.address(),Packet::VERB_EXT_FRAME);
 				outp.append(network->id());
 				outp.append((uint8_t)0x00);
@@ -602,7 +603,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 				outp.append(data,len);
 				if (!network->config().disableCompression())
 					outp.compress();
-				send(outp,true);
+				send(tPtr,outp,true);
 			} else {
 				TRACE("%.16llx: %s -> %s %s packet not sent: filterOutgoingPacket() returned false",network->id(),from.toString().c_str(),to.toString().c_str(),etherTypeName(etherType));
 			}
@@ -610,20 +611,20 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
 	}
 }
 
-void Switch::send(Packet &packet,bool encrypt)
+void Switch::send(void *tPtr,Packet &packet,bool encrypt)
 {
 	if (packet.destination() == RR->identity.address()) {
 		TRACE("BUG: caught attempt to send() to self, ignored");
 		return;
 	}
 
-	if (!_trySend(packet,encrypt)) {
+	if (!_trySend(tPtr,packet,encrypt)) {
 		Mutex::Lock _l(_txQueue_m);
 		_txQueue.push_back(TXQueueEntry(packet.destination(),RR->node->now(),packet,encrypt));
 	}
 }
 
-void Switch::requestWhois(const Address &addr)
+void Switch::requestWhois(void *tPtr,const Address &addr)
 {
 #ifdef ZT_TRACE
 	if (addr == RR->identity.address()) {
@@ -644,10 +645,10 @@ void Switch::requestWhois(const Address &addr)
 		}
 	}
 	if (inserted)
-		_sendWhoisRequest(addr,(const Address *)0,0);
+		_sendWhoisRequest(tPtr,addr,(const Address *)0,0);
 }
 
-void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
+void Switch::doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer)
 {
 	{	// cancel pending WHOIS since we now know this peer
 		Mutex::Lock _l(_outstandingWhoisRequests_m);
@@ -660,7 +661,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
 		while (i) {
 			RXQueueEntry *rq = &(_rxQueue[--i]);
 			if ((rq->timestamp)&&(rq->complete)) {
-				if (rq->frag0.tryDecode(RR))
+				if (rq->frag0.tryDecode(RR,tPtr))
 					rq->timestamp = 0;
 			}
 		}
@@ -670,7 +671,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
 		Mutex::Lock _l(_txQueue_m);
 		for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
 			if (txi->dest == peer->address()) {
-				if (_trySend(txi->packet,txi->encrypt))
+				if (_trySend(tPtr,txi->packet,txi->encrypt))
 					_txQueue.erase(txi++);
 				else ++txi;
 			} else ++txi;
@@ -678,7 +679,7 @@ void Switch::doAnythingWaitingForPeer(const SharedPtr<Peer> &peer)
 	}
 }
 
-unsigned long Switch::doTimerTasks(uint64_t now)
+unsigned long Switch::doTimerTasks(void *tPtr,uint64_t now)
 {
 	unsigned long nextDelay = 0xffffffff; // ceiling delay, caller will cap to minimum
 
@@ -695,7 +696,7 @@ unsigned long Switch::doTimerTasks(uint64_t now)
 					_outstandingWhoisRequests.erase(*a);
 				} else {
 					r->lastSent = now;
-					r->peersConsulted[r->retries] = _sendWhoisRequest(*a,r->peersConsulted,(r->retries > 1) ? r->retries : 0);
+					r->peersConsulted[r->retries] = _sendWhoisRequest(tPtr,*a,r->peersConsulted,(r->retries > 1) ? r->retries : 0);
 					TRACE("WHOIS %s (retry %u)",a->toString().c_str(),r->retries);
 					++r->retries;
 					nextDelay = std::min(nextDelay,(unsigned long)ZT_WHOIS_RETRY_DELAY);
@@ -709,7 +710,7 @@ unsigned long Switch::doTimerTasks(uint64_t now)
 	{	// Time out TX queue packets that never got WHOIS lookups or other info.
 		Mutex::Lock _l(_txQueue_m);
 		for(std::list< TXQueueEntry >::iterator txi(_txQueue.begin());txi!=_txQueue.end();) {
-			if (_trySend(txi->packet,txi->encrypt))
+			if (_trySend(tPtr,txi->packet,txi->encrypt))
 				_txQueue.erase(txi++);
 			else if ((now - txi->creationTime) > ZT_TRANSMIT_QUEUE_TIMEOUT) {
 				TRACE("TX %s -> %s timed out",txi->packet.source().toString().c_str(),txi->packet.destination().toString().c_str());
@@ -743,19 +744,19 @@ bool Switch::_shouldUnite(const uint64_t now,const Address &source,const Address
 	return false;
 }
 
-Address Switch::_sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
+Address Switch::_sendWhoisRequest(void *tPtr,const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted)
 {
 	SharedPtr<Peer> upstream(RR->topology->getUpstreamPeer(peersAlreadyConsulted,numPeersAlreadyConsulted,false));
 	if (upstream) {
 		Packet outp(upstream->address(),RR->identity.address(),Packet::VERB_WHOIS);
 		addr.appendTo(outp);
 		RR->node->expectReplyTo(outp.packetId());
-		send(outp,true);
+		send(tPtr,outp,true);
 	}
 	return Address();
 }
 
-bool Switch::_trySend(Packet &packet,bool encrypt)
+bool Switch::_trySend(void *tPtr,Packet &packet,bool encrypt)
 {
 	SharedPtr<Path> viaPath;
 	const uint64_t now = RR->node->now();
@@ -769,7 +770,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 		clusterMostRecentMemberId = RR->cluster->checkSendViaCluster(destination,clusterMostRecentTs,clusterPeerSecret);
 #endif
 
-	const SharedPtr<Peer> peer(RR->topology->getPeer(destination));
+	const SharedPtr<Peer> peer(RR->topology->getPeer(tPtr,destination));
 	if (peer) {
 		/* First get the best path, and if it's dead (and this is not a root)
 		 * we attempt to re-activate that path but this packet will flow
@@ -784,7 +785,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 			if ((clusterMostRecentMemberId < 0)||(viaPath->lastIn() > clusterMostRecentTs)) {
 #endif
 				if ((now - viaPath->lastOut()) > std::max((now - viaPath->lastIn()) * 4,(uint64_t)ZT_PATH_MIN_REACTIVATE_INTERVAL)) {
-					peer->attemptToContactAt(viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
+					peer->attemptToContactAt(tPtr,viaPath->localAddress(),viaPath->address(),now,false,viaPath->nextOutgoingCounter());
 					viaPath->sent(now);
 				}
 #ifdef ZT_ENABLE_CLUSTER
@@ -801,7 +802,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 #else
 		if (!viaPath) {
 #endif
-			peer->tryMemorizedPath(now); // periodically attempt memorized or statically defined paths, if any are known
+			peer->tryMemorizedPath(tPtr,now); // periodically attempt memorized or statically defined paths, if any are known
 			const SharedPtr<Peer> relay(RR->topology->getUpstreamPeer());
 			if ( (!relay) || (!(viaPath = relay->getBestPath(now,false))) ) {
 				if (!(viaPath = peer->getBestPath(now,true)))
@@ -816,7 +817,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 #ifdef ZT_ENABLE_CLUSTER
 		if (clusterMostRecentMemberId < 0) {
 #else
-			requestWhois(destination);
+			requestWhois(tPtr,destination);
 			return false; // if we are not in cluster mode, there is no way we can send without knowing the peer directly
 #endif
 #ifdef ZT_ENABLE_CLUSTER
@@ -844,9 +845,9 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 #endif
 
 #ifdef ZT_ENABLE_CLUSTER
-	if ( ((viaPath)&&(viaPath->send(RR,packet.data(),chunkSize,now))) || ((clusterMostRecentMemberId >= 0)&&(RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,packet.data(),chunkSize))) ) {
+	if ( ((viaPath)&&(viaPath->send(RR,tPtr,packet.data(),chunkSize,now))) || ((clusterMostRecentMemberId >= 0)&&(RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,packet.data(),chunkSize))) ) {
 #else
-	if (viaPath->send(RR,packet.data(),chunkSize,now)) {
+	if (viaPath->send(RR,tPtr,packet.data(),chunkSize,now)) {
 #endif
 		if (chunkSize < packet.size()) {
 			// Too big for one packet, fragment the rest
@@ -866,7 +867,7 @@ bool Switch::_trySend(Packet &packet,bool encrypt)
 				else if (clusterMostRecentMemberId >= 0)
 					RR->cluster->sendViaCluster(clusterMostRecentMemberId,destination,frag.data(),frag.size());
 #else
-				viaPath->send(RR,frag.data(),frag.size(),now);
+				viaPath->send(RR,tPtr,frag.data(),frag.size(),now);
 #endif
 				fragStart += chunkSize;
 				remaining -= chunkSize;

+ 14 - 8
node/Switch.hpp

@@ -59,16 +59,18 @@ public:
 	/**
 	 * 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 localAddr Local interface address
 	 * @param fromAddr Internet IP address of origin
 	 * @param data Packet data
 	 * @param len Packet length
 	 */
-	void onRemotePacket(const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
+	void onRemotePacket(void *tPtr,const InetAddress &localAddr,const InetAddress &fromAddr,const void *data,unsigned int len);
 
 	/**
 	 * Called when a packet comes from a local Ethernet tap
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param network Which network's TAP did this packet come from?
 	 * @param from Originating MAC address
 	 * @param to Destination MAC address
@@ -77,7 +79,7 @@ public:
 	 * @param data Ethernet payload
 	 * @param len Frame length
 	 */
-	void onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
+	void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len);
 
 	/**
 	 * Send a packet to a ZeroTier address (destination in packet)
@@ -91,26 +93,29 @@ public:
 	 * Needless to say, the packet's source must be this node. Otherwise it
 	 * won't be encrypted right. (This is not used for relaying.)
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param packet Packet to send (buffer may be modified)
 	 * @param encrypt Encrypt packet payload? (always true except for HELLO)
 	 */
-	void send(Packet &packet,bool encrypt);
+	void send(void *tPtr,Packet &packet,bool encrypt);
 
 	/**
 	 * Request WHOIS on a given address
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param addr Address to look up
 	 */
-	void requestWhois(const Address &addr);
+	void requestWhois(void *tPtr,const Address &addr);
 
 	/**
 	 * Run any processes that are waiting for this peer's identity
 	 *
 	 * Called when we learn of a peer's identity from HELLO, OK(WHOIS), etc.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param peer New peer
 	 */
-	void doAnythingWaitingForPeer(const SharedPtr<Peer> &peer);
+	void doAnythingWaitingForPeer(void *tPtr,const SharedPtr<Peer> &peer);
 
 	/**
 	 * Perform retries and other periodic timer tasks
@@ -118,15 +123,16 @@ public:
 	 * This can return a very long delay if there are no pending timer
 	 * tasks. The caller should cap this comparatively vs. other values.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param now Current time
 	 * @return Number of milliseconds until doTimerTasks() should be run again
 	 */
-	unsigned long doTimerTasks(uint64_t now);
+	unsigned long doTimerTasks(void *tPtr,uint64_t now);
 
 private:
 	bool _shouldUnite(const uint64_t now,const Address &source,const Address &destination);
-	Address _sendWhoisRequest(const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
-	bool _trySend(Packet &packet,bool encrypt); // packet is modified if return is true
+	Address _sendWhoisRequest(void *tPtr,const Address &addr,const Address *peersAlreadyConsulted,unsigned int numPeersAlreadyConsulted);
+	bool _trySend(void *tPtr,Packet &packet,bool encrypt); // packet is modified if return is true
 
 	const RuntimeEnvironment *const RR;
 	uint64_t _lastBeaconResponse;

+ 3 - 3
node/Tag.cpp

@@ -25,13 +25,13 @@
 
 namespace ZeroTier {
 
-int Tag::verify(const RuntimeEnvironment *RR) const
+int Tag::verify(const RuntimeEnvironment *RR,void *tPtr) const
 {
 	if ((!_signedBy)||(_signedBy != Network::controllerFor(_networkId)))
 		return -1;
-	const Identity id(RR->topology->getIdentity(_signedBy));
+	const Identity id(RR->topology->getIdentity(tPtr,_signedBy));
 	if (!id) {
-		RR->sw->requestWhois(_signedBy);
+		RR->sw->requestWhois(tPtr,_signedBy);
 		return 1;
 	}
 	try {

+ 2 - 1
node/Tag.hpp

@@ -105,9 +105,10 @@ public:
 	 * Check this tag's signature
 	 *
 	 * @param RR Runtime environment to allow identity lookup for signedBy
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @return 0 == OK, 1 == waiting for WHOIS, -1 == BAD signature or tag
 	 */
-	int verify(const RuntimeEnvironment *RR) const;
+	int verify(const RuntimeEnvironment *RR,void *tPtr) const;
 
 	template<unsigned int C>
 	inline void serialize(Buffer<C> &b,const bool forSign = false) const

File diff suppressed because it is too large
+ 0 - 0
node/Topology.cpp


+ 16 - 10
node/Topology.hpp

@@ -50,7 +50,7 @@ class RuntimeEnvironment;
 class Topology
 {
 public:
-	Topology(const RuntimeEnvironment *renv);
+	Topology(const RuntimeEnvironment *renv,void *tPtr);
 
 	/**
 	 * Add a peer to database
@@ -58,18 +58,20 @@ public:
 	 * This will not replace existing peers. In that case the existing peer
 	 * record is returned.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param peer Peer to add
 	 * @return New or existing peer (should replace 'peer')
 	 */
-	SharedPtr<Peer> addPeer(const SharedPtr<Peer> &peer);
+	SharedPtr<Peer> addPeer(void *tPtr,const SharedPtr<Peer> &peer);
 
 	/**
 	 * Get a peer from its address
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param zta ZeroTier address of peer
 	 * @return Peer or NULL if not found
 	 */
-	SharedPtr<Peer> getPeer(const Address &zta);
+	SharedPtr<Peer> getPeer(void *tPtr,const Address &zta);
 
 	/**
 	 * Get a peer only if it is presently in memory (no disk cache)
@@ -109,10 +111,11 @@ public:
 	/**
 	 * Get the identity of a peer
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param zta ZeroTier address of peer
 	 * @return Identity or NULL Identity if not found
 	 */
-	Identity getIdentity(const Address &zta);
+	Identity getIdentity(void *tPtr,const Address &zta);
 
 	/**
 	 * Cache an identity
@@ -120,9 +123,10 @@ public:
 	 * This is done automatically on addPeer(), and so is only useful for
 	 * cluster identity replication.
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param id Identity to cache
 	 */
-	void saveIdentity(const Identity &id);
+	void saveIdentity(void *tPtr,const Identity &id);
 
 	/**
 	 * Get the current best upstream peer
@@ -267,11 +271,12 @@ public:
 	/**
 	 * Validate new world and update if newer and signature is okay
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param newWorld A new or updated planet or moon to learn
 	 * @param alwaysAcceptNew If true, always accept new moons even if we're not waiting for one
 	 * @return True if it was valid and newer than current (or totally new for moons)
 	 */
-	bool addWorld(const World &newWorld,bool alwaysAcceptNew);
+	bool addWorld(void *tPtr,const World &newWorld,bool alwaysAcceptNew);
 
 	/**
 	 * Add a moon
@@ -282,14 +287,15 @@ public:
 	 * @param id Moon ID
 	 * @param seed If non-NULL, an address of any member of the moon to contact
 	 */
-	void addMoon(const uint64_t id,const Address &seed);
+	void addMoon(void *tPtr,const uint64_t id,const Address &seed);
 
 	/**
 	 * Remove a moon
 	 *
+	 * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call
 	 * @param id Moon's world ID
 	 */
-	void removeMoon(const uint64_t id);
+	void removeMoon(void *tPtr,const uint64_t id);
 
 	/**
 	 * Clean and flush database
@@ -420,8 +426,8 @@ public:
 	}
 
 private:
-	Identity _getIdentity(const Address &zta);
-	void _memoizeUpstreams();
+	Identity _getIdentity(void *tPtr,const Address &zta);
+	void _memoizeUpstreams(void *tPtr);
 
 	const RuntimeEnvironment *const RR;
 

+ 2 - 3
osdep/BSDEthernetTap.cpp

@@ -71,7 +71,7 @@ BSDEthernetTap::BSDEthernetTap(
 	unsigned int metric,
 	uint64_t nwid,
 	const char *friendlyName,
-	void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+	void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 	void *arg) :
 	_handler(handler),
 	_arg(arg),
@@ -460,8 +460,7 @@ void BSDEthernetTap::threadMain()
 						to.setTo(getBuf,6);
 						from.setTo(getBuf + 6,6);
 						unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
-						// TODO: VLAN support
-						_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+						_handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
 					}
 
 					r = 0;

+ 2 - 2
osdep/BSDEthernetTap.hpp

@@ -43,7 +43,7 @@ public:
 		unsigned int metric,
 		uint64_t nwid,
 		const char *friendlyName,
-		void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+		void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 		void *arg);
 
 	~BSDEthernetTap();
@@ -62,7 +62,7 @@ public:
 		throw();
 
 private:
-	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+	void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void *_arg;
 	uint64_t _nwid;
 	Thread _thread;

+ 2 - 2
osdep/LinuxEthernetTap.cpp

@@ -62,7 +62,7 @@ LinuxEthernetTap::LinuxEthernetTap(
 	unsigned int metric,
 	uint64_t nwid,
 	const char *friendlyName,
-	void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+	void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 	void *arg) :
 	_handler(handler),
 	_arg(arg),
@@ -470,7 +470,7 @@ void LinuxEthernetTap::threadMain()
 						from.setTo(getBuf + 6,6);
 						unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
 						// TODO: VLAN support
-						_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+						_handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
 					}
 
 					r = 0;

+ 2 - 2
osdep/LinuxEthernetTap.hpp

@@ -44,7 +44,7 @@ public:
 		unsigned int metric,
 		uint64_t nwid,
 		const char *friendlyName,
-		void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+		void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 		void *arg);
 
 	~LinuxEthernetTap();
@@ -66,7 +66,7 @@ public:
 		throw();
 
 private:
-	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+	void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void *_arg;
 	uint64_t _nwid;
 	Thread _thread;

+ 2 - 2
osdep/OSXEthernetTap.cpp

@@ -314,7 +314,7 @@ OSXEthernetTap::OSXEthernetTap(
 	unsigned int metric,
 	uint64_t nwid,
 	const char *friendlyName,
-	void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
+	void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *data,unsigned int len),
 	void *arg) :
 	_handler(handler),
 	_arg(arg),
@@ -646,7 +646,7 @@ void OSXEthernetTap::threadMain()
 						from.setTo(getBuf + 6,6);
 						unsigned int etherType = ntohs(((const uint16_t *)getBuf)[6]);
 						// TODO: VLAN support
-						_handler(_arg,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
+						_handler(_arg,(void *)0,_nwid,from,to,etherType,0,(const void *)(getBuf + 14),r - 14);
 					}
 
 					r = 0;

+ 2 - 2
osdep/OSXEthernetTap.hpp

@@ -48,7 +48,7 @@ public:
 		unsigned int metric,
 		uint64_t nwid,
 		const char *friendlyName,
-		void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+		void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 		void *arg);
 
 	~OSXEthernetTap();
@@ -67,7 +67,7 @@ public:
 		throw();
 
 private:
-	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+	void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void *_arg;
 	uint64_t _nwid;
 	Thread _thread;

+ 2 - 3
osdep/WindowsEthernetTap.cpp

@@ -456,7 +456,7 @@ WindowsEthernetTap::WindowsEthernetTap(
 	unsigned int metric,
 	uint64_t nwid,
 	const char *friendlyName,
-	void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+	void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 	void *arg) :
 	_handler(handler),
 	_arg(arg),
@@ -1058,8 +1058,7 @@ void WindowsEthernetTap::threadMain()
 							MAC from(tapReadBuf + 6,6);
 							unsigned int etherType = ((((unsigned int)tapReadBuf[12]) & 0xff) << 8) | (((unsigned int)tapReadBuf[13]) & 0xff);
 							try {
-								// TODO: decode vlans
-								_handler(_arg,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
+								_handler(_arg,(void *)0,_nwid,from,to,etherType,0,tapReadBuf + 14,bytesRead - 14);
 							} catch ( ... ) {} // handlers should not throw
 						}
 					}

+ 2 - 2
osdep/WindowsEthernetTap.hpp

@@ -87,7 +87,7 @@ public:
 		unsigned int metric,
 		uint64_t nwid,
 		const char *friendlyName,
-		void (*handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
+		void (*handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int),
 		void *arg);
 
 	~WindowsEthernetTap();
@@ -118,7 +118,7 @@ private:
 	void _setRegistryIPv4Value(const char *regKey,const std::vector<std::string> &value);
 	void _syncIps();
 
-	void (*_handler)(void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
+	void (*_handler)(void *,void *,uint64_t,const MAC &,const MAC &,unsigned int,unsigned int,const void *,unsigned int);
 	void *_arg;
 	MAC _mac;
 	uint64_t _nwid;

+ 30 - 28
service/OneService.cpp

@@ -291,21 +291,21 @@ static void _moonToJson(nlohmann::json &mj,const World &world)
 
 class OneServiceImpl;
 
-static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
-static void SnodeEventCallback(ZT_Node *node,void *uptr,enum ZT_Event event,const void *metaData);
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize);
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure);
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *addr,const void *data,unsigned int len,unsigned int ttl);
-static void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,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,uint64_t ztaddr,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr);
-static int SnodePathLookupFunction(ZT_Node *node,void *uptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
+static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf);
+static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData);
+static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize);
+static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure);
+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 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 SnodePathLookupFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t ztaddr,int family,struct sockaddr_storage *result);
 
 #ifdef ZT_ENABLE_CLUSTER
 static void SclusterSendFunction(void *uptr,unsigned int toMemberId,const void *data,unsigned int len);
 static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,int *x,int *y,int *z);
 #endif
 
-static void StapFrameHandler(void *uptr,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);
 
 static int ShttpOnMessageBegin(http_parser *parser);
 static int ShttpOnUrl(http_parser *parser,const char *ptr,size_t length);
@@ -573,7 +573,7 @@ public:
 				cb.eventCallback = SnodeEventCallback;
 				cb.pathCheckFunction = SnodePathCheckFunction;
 				cb.pathLookupFunction = SnodePathLookupFunction;
-				_node = new Node(this,&cb,OSUtils::now());
+				_node = new Node(this,(void *)0,&cb,OSUtils::now());
 			}
 
 			// Read local configuration
@@ -804,7 +804,7 @@ public:
 				for(std::vector<std::string>::iterator f(networksDotD.begin());f!=networksDotD.end();++f) {
 					std::size_t dot = f->find_last_of('.');
 					if ((dot == 16)&&(f->substr(16) == ".conf"))
-						_node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0);
+						_node->join(Utils::hexStrToU64(f->substr(0,dot).c_str()),(void *)0,(void *)0);
 				}
 			}
 			{ // Load existing moons
@@ -812,7 +812,7 @@ public:
 				for(std::vector<std::string>::iterator f(moonsDotD.begin());f!=moonsDotD.end();++f) {
 					std::size_t dot = f->find_last_of('.');
 					if ((dot == 16)&&(f->substr(16) == ".moon"))
-						_node->orbit(Utils::hexStrToU64(f->substr(0,dot).c_str()),0);
+						_node->orbit((void *)0,Utils::hexStrToU64(f->substr(0,dot).c_str()),0);
 				}
 			}
 
@@ -877,7 +877,7 @@ public:
 
 				uint64_t dl = _nextBackgroundTaskDeadline;
 				if (dl <= now) {
-					_node->processBackgroundTasks(now,&_nextBackgroundTaskDeadline);
+					_node->processBackgroundTasks((void *)0,now,&_nextBackgroundTaskDeadline);
 					dl = _nextBackgroundTaskDeadline;
 				}
 
@@ -892,7 +892,7 @@ public:
 							std::vector<MulticastGroup> added,removed;
 							n->second.tap->scanMulticastGroups(added,removed);
 							for(std::vector<MulticastGroup>::iterator m(added.begin());m!=added.end();++m)
-								_node->multicastSubscribe(n->first,m->mac().toInt(),m->adi());
+								_node->multicastSubscribe((void *)0,n->first,m->mac().toInt(),m->adi());
 							for(std::vector<MulticastGroup>::iterator m(removed.begin());m!=removed.end();++m)
 								_node->multicastUnsubscribe(n->first,m->mac().toInt(),m->adi());
 						}
@@ -1306,7 +1306,7 @@ public:
 							res["signature"] = json();
 							res["updatesMustBeSignedBy"] = json();
 							res["waiting"] = true;
-							_node->orbit(id,seed);
+							_node->orbit((void *)0,id,seed);
 							scode = 200;
 						}
 
@@ -1315,7 +1315,7 @@ public:
 					if (ps.size() == 2) {
 
 						uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
-						_node->join(wantnw,(void *)0); // does nothing if we are a member
+						_node->join(wantnw,(void *)0,(void *)0); // does nothing if we are a member
 						ZT_VirtualNetworkList *nws = _node->networks();
 						if (nws) {
 							for(unsigned long i=0;i<nws->networkCount;++i) {
@@ -1360,7 +1360,7 @@ public:
 
 				if (ps[0] == "moon") {
 					if (ps.size() == 2) {
-						_node->deorbit(Utils::hexStrToU64(ps[1].c_str()));
+						_node->deorbit((void *)0,Utils::hexStrToU64(ps[1].c_str()));
 						res["result"] = true;
 						scode = 200;
 					} // else 404
@@ -1371,7 +1371,7 @@ public:
 							uint64_t wantnw = Utils::hexStrToU64(ps[1].c_str());
 							for(unsigned long i=0;i<nws->networkCount;++i) {
 								if (nws->networks[i].nwid == wantnw) {
-									_node->leave(wantnw,(void **)0);
+									_node->leave(wantnw,(void **)0,(void *)0);
 									res["result"] = true;
 									scode = 200;
 									break;
@@ -1693,6 +1693,7 @@ public:
 			_lastDirectReceiveFromGlobal = OSUtils::now();
 
 		const ZT_ResultCode rc = _node->processWirePacket(
+			(void *)0,
 			OSUtils::now(),
 			reinterpret_cast<const struct sockaddr_storage *>(localAddr),
 			(const struct sockaddr_storage *)from, // Phy<> uses sockaddr_storage, so it'll always be that big
@@ -1845,6 +1846,7 @@ public:
 							if (from) {
 								InetAddress fakeTcpLocalInterfaceAddress((uint32_t)0xffffffff,0xffff);
 								const ZT_ResultCode rc = _node->processWirePacket(
+									(void *)0,
 									OSUtils::now(),
 									reinterpret_cast<struct sockaddr_storage *>(&fakeTcpLocalInterfaceAddress),
 									reinterpret_cast<struct sockaddr_storage *>(&from),
@@ -2255,7 +2257,7 @@ public:
 
 	inline void tapFrameHandler(uint64_t nwid,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,const void *data,unsigned int len)
 	{
-		_node->processVirtualNetworkFrame(OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
+		_node->processVirtualNetworkFrame((void *)0,OSUtils::now(),nwid,from.toInt(),to.toInt(),etherType,vlanId,data,len,&_nextBackgroundTaskDeadline);
 	}
 
 	inline void onHttpRequestToServer(TcpConnection *tc)
@@ -2426,21 +2428,21 @@ public:
 	}
 };
 
-static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf)
+static int SnodeVirtualNetworkConfigFunction(ZT_Node *node,void *uptr,void *tptr,uint64_t nwid,void **nuptr,enum ZT_VirtualNetworkConfigOperation op,const ZT_VirtualNetworkConfig *nwconf)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeVirtualNetworkConfigFunction(nwid,nuptr,op,nwconf); }
-static void SnodeEventCallback(ZT_Node *node,void *uptr,enum ZT_Event event,const void *metaData)
+static void SnodeEventCallback(ZT_Node *node,void *uptr,void *tptr,enum ZT_Event event,const void *metaData)
 { reinterpret_cast<OneServiceImpl *>(uptr)->nodeEventCallback(event,metaData); }
-static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
+static long SnodeDataStoreGetFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,void *buf,unsigned long bufSize,unsigned long readIndex,unsigned long *totalSize)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStoreGetFunction(name,buf,bufSize,readIndex,totalSize); }
-static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,const char *name,const void *data,unsigned long len,int secure)
+static int SnodeDataStorePutFunction(ZT_Node *node,void *uptr,void *tptr,const char *name,const void *data,unsigned long len,int secure)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodeDataStorePutFunction(name,data,len,secure); }
-static int SnodeWirePacketSendFunction(ZT_Node *node,void *uptr,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,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 void SnodeVirtualNetworkFrameFunction(ZT_Node *node,void *uptr,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); }
-static int SnodePathCheckFunction(ZT_Node *node,void *uptr,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,const struct sockaddr_storage *localAddr,const struct sockaddr_storage *remoteAddr)
 { return reinterpret_cast<OneServiceImpl *>(uptr)->nodePathCheckFunction(ztaddr,localAddr,remoteAddr); }
-static int SnodePathLookupFunction(ZT_Node *node,void *uptr,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); }
 
 #ifdef ZT_ENABLE_CLUSTER
@@ -2458,7 +2460,7 @@ static int SclusterGeoIpFunction(void *uptr,const struct sockaddr_storage *addr,
 }
 #endif
 
-static void StapFrameHandler(void *uptr,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)
 { reinterpret_cast<OneServiceImpl *>(uptr)->tapFrameHandler(nwid,from,to,etherType,vlanId,data,len); }
 
 static int ShttpOnMessageBegin(http_parser *parser)

+ 6 - 6
service/SoftwareUpdater.cpp

@@ -175,7 +175,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 								std::string lj;
 								lj.push_back((char)VERB_LATEST);
 								lj.append(OSUtils::jsonDump(*latest));
-								_node.sendUserMessage(origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,lj.data(),(unsigned int)lj.length());
+								_node.sendUserMessage((void *)0,origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,lj.data(),(unsigned int)lj.length());
 								if (_distLog) {
 									fprintf(_distLog,"%.10llx GET_LATEST %u.%u.%u_%u platform %u arch %u vendor %u channel %s -> LATEST %u.%u.%u_%u" ZT_EOL_S,(unsigned long long)origin,rvMaj,rvMin,rvRev,rvBld,rvPlatform,rvArch,rvVendor,rvChannel.c_str(),bestVMaj,bestVMin,bestVRev,bestVBld);
 									fflush(_distLog);
@@ -205,7 +205,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 									gd.append((uint8_t)VERB_GET_DATA);
 									gd.append(_downloadHashPrefix.data,16);
 									gd.append((uint32_t)_download.length());
-									_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+									_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
 									//printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
 								}
 							}
@@ -229,7 +229,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 						buf.append(reinterpret_cast<const uint8_t *>(data) + 1,16);
 						buf.append((uint32_t)idx);
 						buf.append(d->second.bin.data() + idx,std::min((unsigned long)ZT_SOFTWARE_UPDATE_CHUNK_SIZE,(unsigned long)(d->second.bin.length() - idx)));
-						_node.sendUserMessage(origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,buf.data(),buf.size());
+						_node.sendUserMessage((void *)0,origin,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,buf.data(),buf.size());
 						//printf(">> DATA @%u\n",(unsigned int)idx);
 					}
 				}
@@ -249,7 +249,7 @@ void SoftwareUpdater::handleSoftwareUpdateUserMessage(uint64_t origin,const void
 							gd.append((uint8_t)VERB_GET_DATA);
 							gd.append(_downloadHashPrefix.data,16);
 							gd.append((uint32_t)_download.length());
-							_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+							_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
 							//printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
 						}
 					}
@@ -296,7 +296,7 @@ bool SoftwareUpdater::check(const uint64_t now)
 			ZT_BUILD_ARCHITECTURE,
 			(int)ZT_VENDOR_ZEROTIER,
 			_channel.c_str());
-		_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len);
+		_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,tmp,len);
 		//printf(">> GET_LATEST\n");
 	}
 
@@ -343,7 +343,7 @@ bool SoftwareUpdater::check(const uint64_t now)
 			gd.append((uint8_t)VERB_GET_DATA);
 			gd.append(_downloadHashPrefix.data,16);
 			gd.append((uint32_t)_download.length());
-			_node.sendUserMessage(ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
+			_node.sendUserMessage((void *)0,ZT_SOFTWARE_UPDATE_SERVICE,ZT_SOFTWARE_UPDATE_USER_MESSAGE_TYPE,gd.data(),gd.size());
 			//printf(">> GET_DATA @%u\n",(unsigned int)_download.length());
 		}
 	}

Some files were not shown because too many files changed in this diff