Browse Source

Yet more work in progress... mostly cleanup and re-implementation without depending on struture padding.

Adam Ierymenko 5 years ago
parent
commit
664a128e9e
12 changed files with 188 additions and 159 deletions
  1. 7 16
      go/native/GoGlue.cpp
  2. 0 34
      go/native/GoGlue.h
  3. 2 0
      node/Containers.hpp
  4. 5 0
      node/OS.hpp
  5. 21 1
      node/Peer.hpp
  6. 29 1
      node/Protocol.hpp
  7. 1 1
      node/Topology.cpp
  8. 1 1
      node/Topology.hpp
  9. 92 77
      node/VL1.cpp
  10. 11 9
      node/VL1.hpp
  11. 10 10
      node/VL2.cpp
  12. 9 9
      node/VL2.hpp

+ 7 - 16
go/native/GoGlue.cpp

@@ -13,16 +13,13 @@
 
 
 #include "GoGlue.h"
 #include "GoGlue.h"
 
 
-#include <cstring>
-#include <cstdlib>
-#include <cerrno>
-
 #include "../../node/Constants.hpp"
 #include "../../node/Constants.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/InetAddress.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Node.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/Utils.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/MAC.hpp"
 #include "../../node/Address.hpp"
 #include "../../node/Address.hpp"
+#include "../../node/Containers.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/OSUtils.hpp"
 #include "../../osdep/EthernetTap.hpp"
 #include "../../osdep/EthernetTap.hpp"
 
 
@@ -32,6 +29,7 @@
 #include <sys/un.h>
 #include <sys/un.h>
 #include <arpa/inet.h>
 #include <arpa/inet.h>
 #include <netinet/in.h>
 #include <netinet/in.h>
+#include <errno.h>
 #ifdef __BSD__
 #ifdef __BSD__
 #include <net/if.h>
 #include <net/if.h>
 #endif
 #endif
@@ -81,13 +79,13 @@ struct ZT_GoNode_Impl
 	Node *node;
 	Node *node;
 	volatile int64_t nextBackgroundTaskDeadline;
 	volatile int64_t nextBackgroundTaskDeadline;
 
 
-	std::string path;
+	String path;
 	std::atomic<bool> run;
 	std::atomic<bool> run;
 
 
-	std::map< ZT_SOCKET,ZT_GoNodeThread > threads;
-	std::mutex threads_l;
+	Map< ZT_SOCKET,ZT_GoNodeThread > threads;
+	Map< uint64_t,std::shared_ptr<EthernetTap> > taps;
 
 
-	std::map< uint64_t,std::shared_ptr<EthernetTap> > taps;
+	std::mutex threads_l;
 	std::mutex taps_l;
 	std::mutex taps_l;
 
 
 	std::thread backgroundTaskThread;
 	std::thread backgroundTaskThread;
@@ -96,9 +94,7 @@ struct ZT_GoNode_Impl
 static const std::string defaultHomePath(OSUtils::platformDefaultHomePath());
 static const std::string defaultHomePath(OSUtils::platformDefaultHomePath());
 const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str();
 const char *ZT_PLATFORM_DEFAULT_HOMEPATH = defaultHomePath.c_str();
 
 
-// --------------------------------------------------------------------------------------------------------------------
-
-/* These functions are implemented in Go in pkg/zerotier/node.go */
+// These are implemented in Go code.
 extern "C" int goPathCheckFunc(void *,const ZT_Identity *,int,const void *,int);
 extern "C" int goPathCheckFunc(void *,const ZT_Identity *,int,const void *,int);
 extern "C" int goPathLookupFunc(void *,uint64_t,int,const ZT_Identity *,int *,uint8_t [16],int *);
 extern "C" int goPathLookupFunc(void *,uint64_t,int,const ZT_Identity *,int *,uint8_t [16],int *);
 extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int);
 extern "C" void goStateObjectPutFunc(void *,int,const uint64_t [2],const void *,int);
@@ -300,8 +296,6 @@ static int ZT_GoNode_PathLookupFunction(
 	return 0;
 	return 0;
 }
 }
 
 
-// --------------------------------------------------------------------------------------------------------------------
-
 extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
 extern "C" ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr)
 {
 {
 	try {
 	try {
@@ -388,7 +382,6 @@ extern "C" ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn)
 	return gn->node;
 	return gn->node;
 }
 }
 
 
-// Sets flags and socket options common to both IPv4 and IPv6 UDP sockets
 static void setCommonUdpSocketSettings(ZT_SOCKET udpSock,const char *dev)
 static void setCommonUdpSocketSettings(ZT_SOCKET udpSock,const char *dev)
 {
 {
 	int bufSize = 1048576;
 	int bufSize = 1048576;
@@ -623,8 +616,6 @@ extern "C" void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid)
 	}
 	}
 }
 }
 
 
-/****************************************************************************/
-
 extern "C" void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled)
 extern "C" void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled)
 {
 {
 	reinterpret_cast<EthernetTap *>(tap)->setEnabled(enabled != 0);
 	reinterpret_cast<EthernetTap *>(tap)->setEnabled(enabled != 0);

+ 0 - 34
go/native/GoGlue.h

@@ -14,15 +14,9 @@
 #ifndef ZT_GONODE_H
 #ifndef ZT_GONODE_H
 #define ZT_GONODE_H
 #define ZT_GONODE_H
 
 
-#ifdef __cplusplus
-#include <cstdint>
-#include <cstdlib>
-#include <cstring>
-#else
 #include <stdint.h>
 #include <stdint.h>
 #include <stdlib.h>
 #include <stdlib.h>
 #include <string.h>
 #include <string.h>
-#endif
 
 
 #include "../../include/ZeroTierCore.h"
 #include "../../include/ZeroTierCore.h"
 #include "../../node/Constants.hpp"
 #include "../../node/Constants.hpp"
@@ -31,56 +25,28 @@
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 
-/* A pointer to an instance of EthernetTap */
 typedef void ZT_GoTap;
 typedef void ZT_GoTap;
-
-/* ZT_GoNode is a C struct and functions that wraps ZT_Node for use via cgo. It
- * performs UDP and other direct I/O in C for performance but otherwise lets
- * the Go code control the node's behavior. */
 struct ZT_GoNode_Impl;
 struct ZT_GoNode_Impl;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 typedef struct ZT_GoNode_Impl ZT_GoNode;
 
 
 extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH;
 extern const char *ZT_PLATFORM_DEFAULT_HOMEPATH;
 
 
 ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr);
 ZT_GoNode *ZT_GoNode_new(const char *workingPath,uintptr_t userPtr);
-
 void ZT_GoNode_delete(ZT_GoNode *gn);
 void ZT_GoNode_delete(ZT_GoNode *gn);
-
 ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn);
 ZT_Node *ZT_GoNode_getNode(ZT_GoNode *gn);
-
-/* This can be called more than once to start multiple listener threads */
 int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,int port,int primary);
 int ZT_GoNode_phyStartListen(ZT_GoNode *gn,const char *dev,const char *ip,int port,int primary);
-
-/* Close all listener threads for a given local IP and port */
 int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,int port);
 int ZT_GoNode_phyStopListen(ZT_GoNode *gn,const char *dev,const char *ip,int port);
-
 ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint);
 ZT_GoTap *ZT_GoNode_join(ZT_GoNode *gn,uint64_t nwid,const ZT_Fingerprint *controllerFingerprint);
-
 void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid);
 void ZT_GoNode_leave(ZT_GoNode *gn,uint64_t nwid);
 
 
 void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled);
 void ZT_GoTap_setEnabled(ZT_GoTap *tap,int enabled);
-
 int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
 int ZT_GoTap_addIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
-
 int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
 int ZT_GoTap_removeIp(ZT_GoTap *tap,int af,const void *ip,int netmaskBits);
-
-/* The buf buffer is filled with tuplies of:
- *   uint8_t family
- *   uint8_t ip[4 or 16]
- *   uint8_t netmask bits (up to 32 for ipv4, 128 for ipv6)
- *
- * This function returns the number of such tuples in the result.
- * If the buffer isn't big enough results are incomplete.
- */
 int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize);
 int ZT_GoTap_ips(ZT_GoTap *tap,void *buf,unsigned int bufSize);
-
 void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]);
 void ZT_GoTap_deviceName(ZT_GoTap *tap,char nbuf[256]);
-
 void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName);
 void ZT_GoTap_setFriendlyName(ZT_GoTap *tap,const char *friendlyName);
-
 void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
 void ZT_GoTap_setMtu(ZT_GoTap *tap,unsigned int mtu);
 
 
-/* Core self-tests that output results to stdout and return non-zero on failure. */
 int ZT_TestCrypto();
 int ZT_TestCrypto();
 int ZT_TestIdentity();
 int ZT_TestIdentity();
 int ZT_TestOther();
 int ZT_TestOther();

+ 2 - 0
node/Containers.hpp

@@ -152,7 +152,9 @@ class Set : public std::set< V,std::less<V>,Utils::Mallocator<V> >
 class String : public std::basic_string< char,std::char_traits<char>,Utils::Mallocator<char> >
 class String : public std::basic_string< char,std::char_traits<char>,Utils::Mallocator<char> >
 {
 {
 public:
 public:
+	ZT_INLINE String() {}
 	explicit ZT_INLINE String(const char *const s) { assign(s); }
 	explicit ZT_INLINE String(const char *const s) { assign(s); }
+	ZT_INLINE &operator=(const char *const s) { assign(s); }
 };
 };
 
 
 } // ZeroTier
 } // ZeroTier

+ 5 - 0
node/OS.hpp

@@ -195,6 +195,11 @@ typedef unsigned uint128_t __attribute__((mode(TI)));
 #endif
 #endif
 
 
 // Macro to print very verbose tracing information to standard error.
 // Macro to print very verbose tracing information to standard error.
+#define ZT_DEBUG_SPEW
+#ifdef ZT_DEBUG_SPEW
 #define ZT_SPEW(f,...) fprintf(stderr,"%s:%d(%s): " f ZT_EOL_S,__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__)
 #define ZT_SPEW(f,...) fprintf(stderr,"%s:%d(%s): " f ZT_EOL_S,__FILE__,__LINE__,__FUNCTION__,__VA_ARGS__)
+#else
+#define ZT_SPEW(f,...)
+#endif
 
 
 #endif
 #endif

+ 21 - 1
node/Peer.hpp

@@ -32,9 +32,11 @@
 #include "SymmetricKey.hpp"
 #include "SymmetricKey.hpp"
 #include "Containers.hpp"
 #include "Containers.hpp"
 
 
-// version, identity, locator, bootstrap, version info, length of any additional fields
 #define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_ADDRESS_LENGTH + ZT_SYMMETRIC_KEY_SIZE + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + 1 + (ZT_MAX_PEER_NETWORK_PATHS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + (2*4) + 2)
 #define ZT_PEER_MARSHAL_SIZE_MAX (1 + ZT_ADDRESS_LENGTH + ZT_SYMMETRIC_KEY_SIZE + ZT_IDENTITY_MARSHAL_SIZE_MAX + ZT_LOCATOR_MARSHAL_SIZE_MAX + 1 + (ZT_MAX_PEER_NETWORK_PATHS * ZT_ENDPOINT_MARSHAL_SIZE_MAX) + (2*4) + 2)
 
 
+#define ZT_PEER_DEDUP_BUFFER_SIZE 1024
+#define ZT_PEER_DEDUP_BUFFER_MASK 1023U
+
 namespace ZeroTier {
 namespace ZeroTier {
 
 
 class Topology;
 class Topology;
@@ -444,6 +446,21 @@ public:
 		return false;
 		return false;
 	}
 	}
 
 
+	/**
+	 * Packet deduplication filter for incoming packets
+	 * 
+	 * This flags a packet ID and returns true if the same packet ID was already
+	 * flagged. This is done in an atomic operation if supported.
+	 * 
+	 * @param packetId Packet ID to check/flag
+	 * @return True if this is a duplicate
+	 */
+	ZT_INLINE bool deduplicateIncomingPacket(const uint64_t packetId) noexcept
+	{
+		// TODO: should take instance ID into account too, but this isn't fully wired.
+		return m_dedup[Utils::hash32((uint32_t)packetId) & ZT_PEER_DEDUP_BUFFER_MASK].exchange(packetId) == packetId;
+	}
+
 private:
 private:
 	void m_prioritizePaths(int64_t now);
 	void m_prioritizePaths(int64_t now);
 	unsigned int m_sendProbe(void *tPtr,int64_t localSocket,const InetAddress &atAddress,int64_t now);
 	unsigned int m_sendProbe(void *tPtr,int64_t localSocket,const InetAddress &atAddress,int64_t now);
@@ -498,6 +515,9 @@ private:
 	// The last time we got a probe from this peer.
 	// The last time we got a probe from this peer.
 	std::atomic<int64_t> m_lastProbeReceived;
 	std::atomic<int64_t> m_lastProbeReceived;
 
 
+	// Deduplication buffer
+	std::atomic<uint64_t> m_dedup[ZT_PEER_DEDUP_BUFFER_SIZE];
+
 	// Meters measuring actual bandwidth in, out, and relayed via this peer (mostly if this is a root).
 	// Meters measuring actual bandwidth in, out, and relayed via this peer (mostly if this is a root).
 	Meter<> m_inMeter;
 	Meter<> m_inMeter;
 	Meter<> m_outMeter;
 	Meter<> m_outMeter;

+ 29 - 1
node/Protocol.hpp

@@ -693,6 +693,34 @@ enum Verb
 	// protocol max: 0x1f
 	// protocol max: 0x1f
 };
 };
 
 
+#ifdef ZT_DEBUG_SPEW
+static ZT_INLINE const char *verbName(const Verb v) noexcept
+{
+	switch(v) {
+		case VERB_NOP:                        return "NOP";
+		case VERB_HELLO:                      return "HELLO";
+		case VERB_ERROR:                      return "ERROR";
+		case VERB_OK:                         return "OK";
+		case VERB_WHOIS:                      return "WHOIS";
+		case VERB_RENDEZVOUS:                 return "RENDEZVOUS";
+		case VERB_FRAME:                      return "FRAME";
+		case VERB_EXT_FRAME:                  return "EXT_FRAME";
+		case VERB_ECHO:                       return "ECHO";
+		case VERB_MULTICAST_LIKE:             return "MULTICAST_LIKE";
+		case VERB_NETWORK_CREDENTIALS:        return "NETWORK_CREDENTIALS";
+		case VERB_NETWORK_CONFIG_REQUEST:     return "NETWORK_CONFIG_REQUEST";
+		case VERB_NETWORK_CONFIG:             return "NETWORK_CONFIG";
+		case VERB_MULTICAST_GATHER:           return "MULTICAST_GATHER";
+		case VERB_MULTICAST_FRAME_deprecated: return "MULTICAST_FRAME_deprecated";
+		case VERB_PUSH_DIRECT_PATHS:          return "PUSH_DIRECT_PATHS";
+		case VERB_USER_MESSAGE:               return "USER_MESSAGE";
+		case VERB_MULTICAST:                  return "MULTICAST";
+		case VERB_ENCAP:                      return "ENCAP";
+		default:                              return "(unknown)";
+	}
+}
+#endif
+
 /**
 /**
  * Error codes used in ERROR packets.
  * Error codes used in ERROR packets.
  */
  */
@@ -825,7 +853,7 @@ static ZT_INLINE int newPacket(uint8_t pkt[28],const uint64_t packetId,const Add
 	destination.copyTo(pkt + ZT_PROTO_PACKET_DESTINATION_INDEX);
 	destination.copyTo(pkt + ZT_PROTO_PACKET_DESTINATION_INDEX);
 	source.copyTo(pkt + ZT_PROTO_PACKET_SOURCE_INDEX);
 	source.copyTo(pkt + ZT_PROTO_PACKET_SOURCE_INDEX);
 	pkt[ZT_PROTO_PACKET_FLAGS_INDEX] = 0;
 	pkt[ZT_PROTO_PACKET_FLAGS_INDEX] = 0;
-	// mac is left undefined as it's filled out by armor()
+	Utils::storeAsIsEndian<uint64_t>(pkt + ZT_PROTO_PACKET_MAC_INDEX,0);
 	pkt[ZT_PROTO_PACKET_VERB_INDEX] = (uint8_t)verb;
 	pkt[ZT_PROTO_PACKET_VERB_INDEX] = (uint8_t)verb;
 	return ZT_PROTO_PACKET_VERB_INDEX + 1;
 	return ZT_PROTO_PACKET_VERB_INDEX + 1;
 }
 }

+ 1 - 1
node/Topology.cpp

@@ -31,7 +31,7 @@ Topology::Topology(const RuntimeEnvironment *renv,void *tPtr) :
 				m_roots.insert(id);
 				m_roots.insert(id);
 				dptr += l;
 				dptr += l;
 				drem -= l;
 				drem -= l;
-				ZT_SPEW("recalled root %s",id.address().toString().c_str());
+				ZT_SPEW("loaded root %s",id.address().toString().c_str());
 			}
 			}
 		}
 		}
 	}
 	}

+ 1 - 1
node/Topology.hpp

@@ -297,7 +297,7 @@ private:
 	void m_writeRootList(void *tPtr);
 	void m_writeRootList(void *tPtr);
 
 
 	// This gets an integer key from an InetAddress for looking up paths.
 	// This gets an integer key from an InetAddress for looking up paths.
-	static ZT_INLINE uint64_t s_getPathKey(const int64_t l,const InetAddress &r)
+	static ZT_INLINE uint64_t s_getPathKey(const int64_t l,const InetAddress &r) noexcept
 	{
 	{
 		if (r.family() == AF_INET) {
 		if (r.family() == AF_INET) {
 			return ((uint64_t)(reinterpret_cast<const sockaddr_in *>(&r)->sin_addr.s_addr) << 24U) +
 			return ((uint64_t)(reinterpret_cast<const sockaddr_in *>(&r)->sin_addr.s_addr) << 24U) +

+ 92 - 77
node/VL1.cpp

@@ -103,13 +103,12 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 	 */
 	 */
 
 
 	try {
 	try {
-		// If this is too short to be a packet or fragment, check if it's a probe and
-		// if not simply drop it.
+		// If this is too short to be a packet or fragment, check if it's a probe and if not simply drop it.
 		if (unlikely(len < ZT_PROTO_MIN_FRAGMENT_LENGTH)) {
 		if (unlikely(len < ZT_PROTO_MIN_FRAGMENT_LENGTH)) {
 			if (len == ZT_PROTO_PROBE_LENGTH) {
 			if (len == ZT_PROTO_PROBE_LENGTH) {
 				const uint32_t probeToken = data->lI32(0);
 				const uint32_t probeToken = data->lI32(0);
 				PeerList peers(RR->topology->peersByProbeToken(probeToken));
 				PeerList peers(RR->topology->peersByProbeToken(probeToken));
-				ZT_SPEW("probe %.8x matches %u peers",(unsigned long)probeToken,peers.size());
+				ZT_SPEW("probe %.8lx matches %u peers",(unsigned long)probeToken,peers.size());
 				for(unsigned int pi=0;pi<peers.size();++pi) {
 				for(unsigned int pi=0;pi<peers.size();++pi) {
 					if (peers[pi]->rateGateProbeRequest(now)) {
 					if (peers[pi]->rateGateProbeRequest(now)) {
 						ZT_SPEW("HELLO -> %s(%s)",peers[pi]->address().toString().c_str(),fromAddr.toString().c_str());
 						ZT_SPEW("HELLO -> %s(%s)",peers[pi]->address().toString().c_str(),fromAddr.toString().c_str());
@@ -120,6 +119,9 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 			return;
 			return;
 		}
 		}
 
 
+		static_assert((ZT_PROTO_PACKET_ID_INDEX + sizeof(uint64_t)) < ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
+		const uint64_t packetId = Utils::loadAsIsEndian<uint64_t>(data->unsafeData + ZT_PROTO_PACKET_ID_INDEX);
+
 		static_assert((ZT_PROTO_PACKET_DESTINATION_INDEX + ZT_ADDRESS_LENGTH) < ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
 		static_assert((ZT_PROTO_PACKET_DESTINATION_INDEX + ZT_ADDRESS_LENGTH) < ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
 		Address destination(data->unsafeData + ZT_PROTO_PACKET_DESTINATION_INDEX);
 		Address destination(data->unsafeData + ZT_PROTO_PACKET_DESTINATION_INDEX);
 		if (destination != RR->identity.address()) {
 		if (destination != RR->identity.address()) {
@@ -131,9 +133,6 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 		// If we made it this far, the packet is at least MIN_FRAGMENT_LENGTH and is addressed to this node's ZT address
 		// If we made it this far, the packet is at least MIN_FRAGMENT_LENGTH and is addressed to this node's ZT address
 		// ----------------------------------------------------------------------------------------------------------------
 		// ----------------------------------------------------------------------------------------------------------------
 
 
-		static_assert((ZT_PROTO_PACKET_ID_INDEX + sizeof(uint64_t)) < ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
-		const uint64_t packetId = Utils::loadAsIsEndian<uint64_t>(data->unsafeData + ZT_PROTO_PACKET_ID_INDEX);
-
 		Buf::PacketVector pktv;
 		Buf::PacketVector pktv;
 
 
 		static_assert(ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX <= ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
 		static_assert(ZT_PROTO_PACKET_FRAGMENT_INDICATOR_INDEX <= ZT_PROTO_MIN_FRAGMENT_LENGTH,"overflow");
@@ -225,10 +224,6 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 			return;
 			return;
 		}
 		}
 
 
-		// ----------------------------------------------------------------------------------------------------------------
-		// Making it this far means the packet is not a plaintext HELLO, so do normal AEAD decrypt and packet handling.
-		// ----------------------------------------------------------------------------------------------------------------
-
 		// This remains zero if authentication fails. Otherwise it gets set to a bit mask
 		// This remains zero if authentication fails. Otherwise it gets set to a bit mask
 		// indicating authentication and other security flags like encryption and forward
 		// indicating authentication and other security flags like encryption and forward
 		// secrecy status.
 		// secrecy status.
@@ -302,9 +297,16 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 			// If authentication was successful go on and process the packet.
 			// If authentication was successful go on and process the packet.
 
 
 			if (unlikely(pktSize < ZT_PROTO_MIN_PACKET_LENGTH)) {
 			if (unlikely(pktSize < ZT_PROTO_MIN_PACKET_LENGTH)) {
-				ZT_SPEW("discarding packet %.16llx from %s(%s): assembled packet size: %d",packetId,source.toString().c_str(),fromAddr.toString().c_str(),pktSize);
+				ZT_SPEW("discarding packet %.16llx from %s(%s): assembled packet size %d is smaller than minimum packet length",packetId,source.toString().c_str(),fromAddr.toString().c_str(),pktSize);
 				return;
 				return;
 			}
 			}
+
+			// TODO: should take instance ID into account here once that is fully implemented.
+			if (unlikely(peer->deduplicateIncomingPacket(packetId))) {
+				ZT_SPEW("discarding packet %.16llx from %s(%s): duplicate!",packetId,source.toString().c_str(),fromAddr.toString().c_str());
+				return;
+			}
+
 			static_assert(ZT_PROTO_PACKET_VERB_INDEX < ZT_PROTO_MIN_PACKET_LENGTH,"overflow");
 			static_assert(ZT_PROTO_PACKET_VERB_INDEX < ZT_PROTO_MIN_PACKET_LENGTH,"overflow");
 			const uint8_t verbFlags = pkt->unsafeData[ZT_PROTO_PACKET_VERB_INDEX];
 			const uint8_t verbFlags = pkt->unsafeData[ZT_PROTO_PACKET_VERB_INDEX];
 			const Protocol::Verb verb = (Protocol::Verb)(verbFlags & ZT_PROTO_VERB_MASK);
 			const Protocol::Verb verb = (Protocol::Verb)(verbFlags & ZT_PROTO_VERB_MASK);
@@ -320,8 +322,7 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 					reinterpret_cast<char *>(dec->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),
 					reinterpret_cast<char *>(dec->unsafeData + ZT_PROTO_PACKET_PAYLOAD_START),
 					pktSize - ZT_PROTO_PACKET_PAYLOAD_START,
 					pktSize - ZT_PROTO_PACKET_PAYLOAD_START,
 					ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START);
 					ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START);
-
-				if (likely((uncompressedLen > 0)&&(uncompressedLen <= (ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START)))) {
+				if (likely((uncompressedLen >= 0)&&(uncompressedLen <= (ZT_BUF_MEM_SIZE - ZT_PROTO_PACKET_PAYLOAD_START)))) {
 					pkt.swap(dec);
 					pkt.swap(dec);
 					ZT_SPEW("decompressed packet: %d -> %d",pktSize,ZT_PROTO_PACKET_PAYLOAD_START + uncompressedLen);
 					ZT_SPEW("decompressed packet: %d -> %d",pktSize,ZT_PROTO_PACKET_PAYLOAD_START + uncompressedLen);
 					pktSize = ZT_PROTO_PACKET_PAYLOAD_START + uncompressedLen;
 					pktSize = ZT_PROTO_PACKET_PAYLOAD_START + uncompressedLen;
@@ -331,6 +332,8 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 				}
 				}
 			}
 			}
 
 
+			ZT_SPEW("%s from %s(%s) (%d bytes)",Protocol::verbName(verb),source.toString().c_str(),fromAddr.toString().c_str(),pktSize);
+
 			// NOTE: HELLO is normally sent in the clear (in terms of our usual AEAD modes) and is handled
 			// NOTE: HELLO is normally sent in the clear (in terms of our usual AEAD modes) and is handled
 			// above. We will try to process it here, but if so it'll still get re-authenticated via HELLO's
 			// above. We will try to process it here, but if so it'll still get re-authenticated via HELLO's
 			// own internal authentication logic as usual. It would be abnormal to make it here with HELLO
 			// own internal authentication logic as usual. It would be abnormal to make it here with HELLO
@@ -341,23 +344,23 @@ void VL1::onRemotePacket(void *const tPtr,const int64_t localSocket,const InetAd
 			switch(verb) {
 			switch(verb) {
 				case Protocol::VERB_NOP:                        break;
 				case Protocol::VERB_NOP:                        break;
 				case Protocol::VERB_HELLO:                      ok = (bool)(m_HELLO(tPtr, path, *pkt, pktSize)); break;
 				case Protocol::VERB_HELLO:                      ok = (bool)(m_HELLO(tPtr, path, *pkt, pktSize)); break;
-				case Protocol::VERB_ERROR:                      ok = m_ERROR(tPtr, auth, path, peer, *pkt, pktSize, inReVerb); break;
-				case Protocol::VERB_OK:                         ok = m_OK(tPtr, auth, path, peer, *pkt, pktSize, inReVerb); break;
-				case Protocol::VERB_WHOIS:                      ok = m_WHOIS(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_RENDEZVOUS:                 ok = m_RENDEZVOUS(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_FRAME:                      ok = RR->vl2->m_FRAME(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_EXT_FRAME:                  ok = RR->vl2->m_EXT_FRAME(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_ECHO:                       ok = m_ECHO(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_MULTICAST_LIKE:             ok = RR->vl2->m_MULTICAST_LIKE(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_NETWORK_CREDENTIALS:        ok = RR->vl2->m_NETWORK_CREDENTIALS(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_NETWORK_CONFIG_REQUEST:     ok = RR->vl2->m_NETWORK_CONFIG_REQUEST(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_NETWORK_CONFIG:             ok = RR->vl2->m_NETWORK_CONFIG(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_MULTICAST_GATHER:           ok = RR->vl2->m_MULTICAST_GATHER(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_MULTICAST_FRAME_deprecated: ok = RR->vl2->m_MULTICAST_FRAME_deprecated(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_PUSH_DIRECT_PATHS:          ok = m_PUSH_DIRECT_PATHS(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_USER_MESSAGE:               ok = m_USER_MESSAGE(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_MULTICAST:                  ok = RR->vl2->m_MULTICAST(tPtr, auth, path, peer, *pkt, pktSize); break;
-				case Protocol::VERB_ENCAP:                      ok = m_ENCAP(tPtr, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_ERROR:                      ok = m_ERROR(tPtr, packetId, auth, path, peer, *pkt, pktSize, inReVerb); break;
+				case Protocol::VERB_OK:                         ok = m_OK(tPtr, packetId, auth, path, peer, *pkt, pktSize, inReVerb); break;
+				case Protocol::VERB_WHOIS:                      ok = m_WHOIS(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_RENDEZVOUS:                 ok = m_RENDEZVOUS(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_FRAME:                      ok = RR->vl2->m_FRAME(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_EXT_FRAME:                  ok = RR->vl2->m_EXT_FRAME(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_ECHO:                       ok = m_ECHO(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_MULTICAST_LIKE:             ok = RR->vl2->m_MULTICAST_LIKE(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_NETWORK_CREDENTIALS:        ok = RR->vl2->m_NETWORK_CREDENTIALS(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_NETWORK_CONFIG_REQUEST:     ok = RR->vl2->m_NETWORK_CONFIG_REQUEST(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_NETWORK_CONFIG:             ok = RR->vl2->m_NETWORK_CONFIG(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_MULTICAST_GATHER:           ok = RR->vl2->m_MULTICAST_GATHER(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_MULTICAST_FRAME_deprecated: ok = RR->vl2->m_MULTICAST_FRAME_deprecated(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_PUSH_DIRECT_PATHS:          ok = m_PUSH_DIRECT_PATHS(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_USER_MESSAGE:               ok = m_USER_MESSAGE(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_MULTICAST:                  ok = RR->vl2->m_MULTICAST(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
+				case Protocol::VERB_ENCAP:                      ok = m_ENCAP(tPtr, packetId, auth, path, peer, *pkt, pktSize); break;
 
 
 				default:
 				default:
 					RR->t->incomingPacketDropped(tPtr,0xeeeeeff0,packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB);
 					RR->t->incomingPacketDropped(tPtr,0xeeeeeff0,packetId,0,identityFromPeerPtr(peer),path->address(),hops,verb,ZT_TRACE_PACKET_DROP_REASON_UNRECOGNIZED_VERB);
@@ -437,9 +440,6 @@ void VL1::m_sendPendingWhois(void *tPtr, int64_t now)
 
 
 SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt, int packetSize)
 SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt, int packetSize)
 {
 {
-	// SECURITY: we know if we made it this far the packet's header is valid and
-	// packetSize is at least the size of a header.
-
 	const uint64_t packetId = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + ZT_PROTO_PACKET_ID_INDEX);
 	const uint64_t packetId = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + ZT_PROTO_PACKET_ID_INDEX);
 	const uint64_t mac = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + ZT_PROTO_PACKET_MAC_INDEX);
 	const uint64_t mac = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + ZT_PROTO_PACKET_MAC_INDEX);
 	const uint8_t hops = pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK;
 	const uint8_t hops = pkt.unsafeData[ZT_PROTO_PACKET_FLAGS_INDEX] & ZT_PROTO_FLAG_FIELD_HOPS_MASK;
@@ -449,11 +449,12 @@ SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt,
 	unsigned int versionMinor = pkt.lI8<ZT_PROTO_PACKET_PAYLOAD_START + 2>(); // LEGACY
 	unsigned int versionMinor = pkt.lI8<ZT_PROTO_PACKET_PAYLOAD_START + 2>(); // LEGACY
 	unsigned int versionRev = pkt.lI16<ZT_PROTO_PACKET_PAYLOAD_START + 3>(); // LEGACY
 	unsigned int versionRev = pkt.lI16<ZT_PROTO_PACKET_PAYLOAD_START + 3>(); // LEGACY
 	const uint64_t timestamp = pkt.lI64<ZT_PROTO_PACKET_PAYLOAD_START + 5>();
 	const uint64_t timestamp = pkt.lI64<ZT_PROTO_PACKET_PAYLOAD_START + 5>();
-	int p = ZT_PROTO_PACKET_PAYLOAD_START + 13;
+
+	int ii = ZT_PROTO_PACKET_PAYLOAD_START + 13;
 
 
 	// Get identity and verify that it matches the sending address in the packet.
 	// Get identity and verify that it matches the sending address in the packet.
 	Identity id;
 	Identity id;
-	if (unlikely(pkt.rO(p,id) < 0)) {
+	if (unlikely(pkt.rO(ii,id) < 0)) {
 		RR->t->incomingPacketDropped(tPtr,0x707a9810,packetId,0,Identity::NIL,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 		RR->t->incomingPacketDropped(tPtr,0x707a9810,packetId,0,Identity::NIL,path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 		return SharedPtr<Peer>();
 		return SharedPtr<Peer>();
 	}
 	}
@@ -469,6 +470,10 @@ SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt,
 			RR->t->incomingPacketDropped(tPtr,0x707a9891,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			RR->t->incomingPacketDropped(tPtr,0x707a9891,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_MAC_FAILED);
 			return SharedPtr<Peer>();
 			return SharedPtr<Peer>();
 		}
 		}
+		if (peer->deduplicateIncomingPacket(packetId)) {
+			ZT_SPEW("discarding packet %.16llx from %s(%s): duplicate!",packetId,id.address().toString().c_str(),path->address().toString().c_str());
+			return SharedPtr<Peer>();
+		}
 	} else {
 	} else {
 		if (unlikely(!id.locallyValidate())) {
 		if (unlikely(!id.locallyValidate())) {
 			RR->t->incomingPacketDropped(tPtr,0x707a9892,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 			RR->t->incomingPacketDropped(tPtr,0x707a9892,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
@@ -530,7 +535,7 @@ SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt,
 
 
 	// LEGACY: this is superseded by the sent-to field in the meta-data dictionary if present.
 	// LEGACY: this is superseded by the sent-to field in the meta-data dictionary if present.
 	InetAddress sentTo;
 	InetAddress sentTo;
-	if (unlikely(pkt.rO(p,sentTo) < 0)) {
+	if (unlikely(pkt.rO(ii,sentTo) < 0)) {
 		RR->t->incomingPacketDropped(tPtr,0x707a9811,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 		RR->t->incomingPacketDropped(tPtr,0x707a9811,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 		return SharedPtr<Peer>();
 		return SharedPtr<Peer>();
 	}
 	}
@@ -539,27 +544,27 @@ SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt,
 
 
 	if (protoVersion >= 11) {
 	if (protoVersion >= 11) {
 		// V2.x and newer supports an encrypted section and has a new OK format.
 		// V2.x and newer supports an encrypted section and has a new OK format.
-		if ((p + 12) < packetSize) {
+		if (likely((ii + 12) < packetSize)) {
 			uint64_t ctrNonce[2];
 			uint64_t ctrNonce[2];
-			ctrNonce[0] = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + p);
+			ctrNonce[0] = Utils::loadAsIsEndian<uint64_t>(pkt.unsafeData + ii);
 #if __BYTE_ORDER == __BIG_ENDIAN
 #if __BYTE_ORDER == __BIG_ENDIAN
-			ctrNonce[1] = ((uint64_t)Utils::loadAsIsEndian<uint32_t>(pkt.unsafeData + p + 8)) << 32U;
+			ctrNonce[1] = ((uint64_t)Utils::loadAsIsEndian<uint32_t>(pkt.unsafeData + ii + 8)) << 32U;
 #else
 #else
-			ctrNonce[1] = Utils::loadAsIsEndian<uint32_t>(pkt.unsafeData + p + 8);
+			ctrNonce[1] = Utils::loadAsIsEndian<uint32_t>(pkt.unsafeData + ii + 8);
 #endif
 #endif
-			p += 12;
+			ii += 12;
 			AES::CTR ctr(peer->identityHelloDictionaryEncryptionCipher());
 			AES::CTR ctr(peer->identityHelloDictionaryEncryptionCipher());
-			ctr.init(reinterpret_cast<uint8_t *>(ctrNonce),pkt.unsafeData + p);
-			ctr.crypt(pkt.unsafeData + p,packetSize - p);
+			ctr.init(reinterpret_cast<uint8_t *>(ctrNonce),pkt.unsafeData + ii);
+			ctr.crypt(pkt.unsafeData + ii,packetSize - ii);
 			ctr.finish();
 			ctr.finish();
 
 
-			const unsigned int dictSize = pkt.rI16(p);
-			if (unlikely((p + dictSize) > packetSize)) {
+			const unsigned int dictSize = pkt.rI16(ii);
+			if (unlikely((ii + dictSize) > packetSize)) {
 				RR->t->incomingPacketDropped(tPtr,0x707a9815,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 				RR->t->incomingPacketDropped(tPtr,0x707a9815,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 				return peer;
 				return peer;
 			}
 			}
 			Dictionary md;
 			Dictionary md;
-			if (!md.decode(pkt.unsafeData + p,dictSize)) {
+			if (!md.decode(pkt.unsafeData + ii,dictSize)) {
 				RR->t->incomingPacketDropped(tPtr,0x707a9816,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 				RR->t->incomingPacketDropped(tPtr,0x707a9816,packetId,0,identityFromPeerPtr(peer),path->address(),hops,Protocol::VERB_HELLO,ZT_TRACE_PACKET_DROP_REASON_INVALID_OBJECT);
 				return peer;
 				return peer;
 			}
 			}
@@ -581,30 +586,35 @@ SharedPtr<Peer> VL1::m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt,
 		}
 		}
 
 
 		Protocol::newPacket(pkt,key->nextMessage(RR->identity.address(),peer->address()),peer->address(),RR->identity.address(),Protocol::VERB_OK);
 		Protocol::newPacket(pkt,key->nextMessage(RR->identity.address(),peer->address()),peer->address(),RR->identity.address(),Protocol::VERB_OK);
-		p = ZT_PROTO_PACKET_PAYLOAD_START;
-		pkt.wI8(p,Protocol::VERB_HELLO);
-		pkt.wI64(p,packetId);
-		pkt.wI64(p,timestamp);
-		pkt.wI8(p,(uint8_t)protoVersion);
+		ii = ZT_PROTO_PACKET_PAYLOAD_START;
+		pkt.wI8(ii,Protocol::VERB_HELLO);
+		pkt.wI64(ii,packetId);
+		pkt.wI64(ii,timestamp);
+		pkt.wI8(ii,(uint8_t)protoVersion);
+
+		FCV<uint8_t,1024> okmd;
+		pkt.wI16(ii,(uint16_t)okmd.size());
+		pkt.wB(ii,okmd.data(),okmd.size());
 	} else {
 	} else {
 		// V1.x has nothing more for this version to parse, and has an older OK format.
 		// V1.x has nothing more for this version to parse, and has an older OK format.
 		Protocol::newPacket(pkt,key->nextMessage(RR->identity.address(),peer->address()),peer->address(),RR->identity.address(),Protocol::VERB_OK);
 		Protocol::newPacket(pkt,key->nextMessage(RR->identity.address(),peer->address()),peer->address(),RR->identity.address(),Protocol::VERB_OK);
-		p = ZT_PROTO_PACKET_PAYLOAD_START;
-		pkt.wI8(p,Protocol::VERB_HELLO);
-		pkt.wI64(p,packetId);
-		pkt.wI64(p,timestamp);
-		pkt.wI8(p,(uint8_t)protoVersion);
-		pkt.wI8(p,(uint8_t)versionMajor);
-		pkt.wI8(p,(uint8_t)versionMinor);
-		pkt.wI16(p,(uint16_t)versionRev);
-		pkt.wO(p,path->address());
-		pkt.wI16(p,0);
+		ii = ZT_PROTO_PACKET_PAYLOAD_START;
+		pkt.wI8(ii,Protocol::VERB_HELLO);
+		pkt.wI64(ii,packetId);
+		pkt.wI64(ii,timestamp);
+		pkt.wI8(ii,(uint8_t)protoVersion);
+		pkt.wI8(ii,(uint8_t)versionMajor);
+		pkt.wI8(ii,(uint8_t)versionMinor);
+		pkt.wI16(ii,(uint16_t)versionRev);
+		pkt.wO(ii,path->address());
+		pkt.wI16(ii,0);
 	}
 	}
 
 
 	peer->setRemoteVersion(protoVersion,versionMajor,versionMinor,versionRev);
 	peer->setRemoteVersion(protoVersion,versionMajor,versionMinor,versionRev);
+	peer->send(tPtr,RR->node->now(),pkt.unsafeData,ii,path);
 }
 }
 
 
-bool VL1::m_ERROR(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb)
+bool VL1::m_ERROR(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb)
 {
 {
 #if 0
 #if 0
 	if (packetSize < (int)sizeof(Protocol::ERROR::Header)) {
 	if (packetSize < (int)sizeof(Protocol::ERROR::Header)) {
@@ -648,24 +658,29 @@ bool VL1::m_ERROR(void *tPtr,const unsigned int auth, const SharedPtr<Path> &pat
 
 
 	}
 	}
 	return true;
 	return true;
+#endif
 }
 }
 
 
-bool VL1::m_OK(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb)
+bool VL1::m_OK(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb)
 {
 {
-	if (packetSize < (int)sizeof(Protocol::OK::Header)) {
-		RR->t->incomingPacketDropped(tPtr,0x4c1f1ff7,0,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_OK,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
+	int ii = ZT_PROTO_PACKET_PAYLOAD_START + 13;
+
+	inReVerb = (Protocol::Verb)pkt.rI8(ii);
+	const uint64_t inRePacketId = pkt.rI64(ii);
+	if (unlikely(Buf::readOverflow(ii,packetSize))) {
+		RR->t->incomingPacketDropped(tPtr,0x4c1f1ff7,packetId,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_OK,ZT_TRACE_PACKET_DROP_REASON_MALFORMED_PACKET);
 		return false;
 		return false;
 	}
 	}
-	Protocol::OK::Header &oh = pkt.as<Protocol::OK::Header>();
-	inReVerb = (Protocol::Verb)oh.inReVerb;
 
 
 	const int64_t now = RR->node->now();
 	const int64_t now = RR->node->now();
-	if (!RR->expect->expecting(oh.inRePacketId,now)) {
-		RR->t->incomingPacketDropped(tPtr,0x4c1f1ff7,0,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_OK,ZT_TRACE_PACKET_DROP_REASON_REPLY_NOT_EXPECTED);
+	if (!RR->expect->expecting(inRePacketId,now)) {
+		RR->t->incomingPacketDropped(tPtr,0x4c1f1ff8,packetId,0,identityFromPeerPtr(peer),path->address(),0,Protocol::VERB_OK,ZT_TRACE_PACKET_DROP_REASON_REPLY_NOT_EXPECTED);
 		return false;
 		return false;
 	}
 	}
 
 
-	switch(oh.inReVerb) {
+	ZT_SPEW("got OK in-re %s (packet ID %.16llx) from %s(%s)",Protocol::verbName(inReVerb),inRePacketId,peer->address().toString().c_str(),path->address().toString().c_str());
+
+	switch(inReVerb) {
 
 
 		case Protocol::VERB_HELLO:
 		case Protocol::VERB_HELLO:
 			break;
 			break;
@@ -680,11 +695,11 @@ bool VL1::m_OK(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path,
 			break;
 			break;
 
 
 	}
 	}
+
 	return true;
 	return true;
-#endif
 }
 }
 
 
-bool VL1::m_WHOIS(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_WHOIS(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 #if 0
 #if 0
 	if (packetSize < (int)sizeof(Protocol::OK::Header)) {
 	if (packetSize < (int)sizeof(Protocol::OK::Header)) {
@@ -738,7 +753,7 @@ bool VL1::m_WHOIS(void *tPtr,const unsigned int auth, const SharedPtr<Path> &pat
 #endif
 #endif
 }
 }
 
 
-bool VL1::m_RENDEZVOUS(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_RENDEZVOUS(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 #if 0
 #if 0
 	if (RR->topology->isRoot(peer->identity())) {
 	if (RR->topology->isRoot(peer->identity())) {
@@ -786,7 +801,7 @@ bool VL1::m_RENDEZVOUS(void *tPtr,const unsigned int auth, const SharedPtr<Path>
 #endif
 #endif
 }
 }
 
 
-bool VL1::m_ECHO(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_ECHO(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 #if 0
 #if 0
 	const uint64_t packetId = Protocol::packetId(pkt,packetSize);
 	const uint64_t packetId = Protocol::packetId(pkt,packetSize);
@@ -824,7 +839,7 @@ bool VL1::m_ECHO(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path
 #endif
 #endif
 }
 }
 
 
-bool VL1::m_PUSH_DIRECT_PATHS(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_PUSH_DIRECT_PATHS(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 #if 0
 #if 0
 	if (packetSize < (int)sizeof(Protocol::PUSH_DIRECT_PATHS)) {
 	if (packetSize < (int)sizeof(Protocol::PUSH_DIRECT_PATHS)) {
@@ -915,13 +930,13 @@ bool VL1::m_PUSH_DIRECT_PATHS(void *tPtr,const unsigned int auth, const SharedPt
 #endif
 #endif
 }
 }
 
 
-bool VL1::m_USER_MESSAGE(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_USER_MESSAGE(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 	// TODO
 	// TODO
 	return true;
 	return true;
 }
 }
 
 
-bool VL1::m_ENCAP(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL1::m_ENCAP(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 	// TODO: not implemented yet
 	// TODO: not implemented yet
 	return true;
 	return true;

+ 11 - 9
node/VL1.hpp

@@ -70,17 +70,19 @@ private:
 
 
 	SharedPtr<Peer> m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt, int packetSize);
 	SharedPtr<Peer> m_HELLO(void *tPtr, const SharedPtr<Path> &path, Buf &pkt, int packetSize);
 
 
-	bool m_ERROR(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb);
-	bool m_OK(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb);
-	bool m_WHOIS(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_RENDEZVOUS(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_ECHO(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_PUSH_DIRECT_PATHS(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_USER_MESSAGE(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_ENCAP(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-
+	bool m_ERROR(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb);
+	bool m_OK(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize, Protocol::Verb &inReVerb);
+	bool m_WHOIS(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_RENDEZVOUS(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_ECHO(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_PUSH_DIRECT_PATHS(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_USER_MESSAGE(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_ENCAP(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, const SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+
+	// Defragmentation engine for handling inbound packets with more than one fragment.
 	Defragmenter<ZT_MAX_PACKET_FRAGMENTS> m_inputPacketAssembler;
 	Defragmenter<ZT_MAX_PACKET_FRAGMENTS> m_inputPacketAssembler;
 
 
+	// Queue of outbound WHOIS reqeusts and packets waiting on them.
 	struct p_WhoisQueueItem
 	struct p_WhoisQueueItem
 	{
 	{
 		ZT_INLINE p_WhoisQueueItem() : lastRetry(0),retries(0),waitingPacketCount(0) {}
 		ZT_INLINE p_WhoisQueueItem() : lastRetry(0),retries(0),waitingPacketCount(0) {}

+ 10 - 10
node/VL2.cpp

@@ -27,43 +27,43 @@ VL2::VL2(const RuntimeEnvironment *renv)
 {
 {
 }
 }
 
 
-void VL2::onLocalEthernet(void *const tPtr,const unsigned int auth,const SharedPtr<Network> &network,const MAC &from,const MAC &to,const unsigned int etherType,unsigned int vlanId,SharedPtr<Buf> &data,unsigned int len)
+void VL2::onLocalEthernet(void *const tPtr,const uint64_t packetId,const unsigned int auth,const SharedPtr<Network> &network,const MAC &from,const MAC &to,const unsigned int etherType,unsigned int vlanId,SharedPtr<Buf> &data,unsigned int len)
 {
 {
 }
 }
 
 
-bool VL2::m_FRAME(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_FRAME(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_EXT_FRAME(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_EXT_FRAME(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_MULTICAST_LIKE(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_MULTICAST_LIKE(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_NETWORK_CREDENTIALS(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_NETWORK_CREDENTIALS(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_NETWORK_CONFIG_REQUEST(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_NETWORK_CONFIG_REQUEST(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_NETWORK_CONFIG(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_NETWORK_CONFIG(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_MULTICAST_GATHER(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_MULTICAST_GATHER(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_MULTICAST_FRAME_deprecated(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_MULTICAST_FRAME_deprecated(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 
-bool VL2::m_MULTICAST(void *tPtr,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
+bool VL2::m_MULTICAST(void *tPtr,const uint64_t packetId,const unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize)
 {
 {
 }
 }
 
 

+ 9 - 9
node/VL2.hpp

@@ -53,15 +53,15 @@ public:
 	void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,SharedPtr<Buf> &data,unsigned int len);
 	void onLocalEthernet(void *tPtr,const SharedPtr<Network> &network,const MAC &from,const MAC &to,unsigned int etherType,unsigned int vlanId,SharedPtr<Buf> &data,unsigned int len);
 
 
 protected:
 protected:
-	bool m_FRAME(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_EXT_FRAME(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_MULTICAST_LIKE(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_NETWORK_CREDENTIALS(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_NETWORK_CONFIG_REQUEST(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_NETWORK_CONFIG(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_MULTICAST_GATHER(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_MULTICAST_FRAME_deprecated(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
-	bool m_MULTICAST(void *tPtr, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_FRAME(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_EXT_FRAME(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_MULTICAST_LIKE(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_NETWORK_CREDENTIALS(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_NETWORK_CONFIG_REQUEST(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_NETWORK_CONFIG(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_MULTICAST_GATHER(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_MULTICAST_FRAME_deprecated(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
+	bool m_MULTICAST(void *tPtr, uint64_t packetId, unsigned int auth, const SharedPtr<Path> &path, SharedPtr<Peer> &peer, Buf &pkt, int packetSize);
 
 
 private:
 private:
 };
 };