Browse Source

A bunch more cleanup, remove a ton of reinterpret_cast cruft in favor of a simple union in InetAddress.

Adam Ierymenko 5 years ago
parent
commit
78d223d709
12 changed files with 279 additions and 445 deletions
  1. 23 32
      include/ZeroTierCore.h
  2. 0 3
      node/Buf.hpp
  3. 10 10
      node/Defragmenter.hpp
  4. 1 1
      node/Dictionary.cpp
  5. 1 1
      node/Dictionary.hpp
  6. 13 25
      node/Endpoint.hpp
  7. 0 2
      node/Fingerprint.hpp
  8. 115 224
      node/InetAddress.cpp
  9. 103 97
      node/InetAddress.hpp
  10. 2 2
      node/Path.hpp
  11. 8 18
      node/Tests.cpp
  12. 3 30
      node/TriviallyCopyable.hpp

+ 23 - 32
include/ZeroTierCore.h

@@ -58,12 +58,12 @@ extern "C" {
 #define ZT_BUF_SIZE 16384
 
 /**
- * Minimum MTU allowed on virtual networks
+ * Minimum Ethernet MTU allowed on virtual (not physical) networks
  */
 #define ZT_MIN_MTU 1280
 
 /**
- * Maximum MTU allowed on virtual networks
+ * Maximum Ethernet MTU allowed on virtual (not physical) networks
  */
 #define ZT_MAX_MTU 10000
 
@@ -73,16 +73,16 @@ extern "C" {
 #define ZT_MIN_UDP_MTU 1400
 
 /**
- * Default UDP payload size (physical path MTU) not including UDP and IP overhead
+ * Default UDP payload size NOT including UDP and IP overhead
  *
  * This is small enough for PPPoE and for Google Cloud's bizarrely tiny MTUs.
- * A 2800 byte payload still fits into two packets, so this should not impact
- * real world throughput at all vs the previous default of 1444.
+ * A payload size corresponding to the default 2800 byte virtual MTU fits
+ * into two packets of less than or equal to this size.
  */
 #define ZT_DEFAULT_UDP_MTU 1432
 
 /**
- * Maximum physical UDP payload
+ * Maximum physical payload size that can ever be used
  */
 #define ZT_MAX_UDP_PHYSPAYLOAD 10100
 
@@ -265,8 +265,8 @@ extern "C" {
 enum ZT_Identity_Type
 {
 	/* These values must be the same as in Identity.hpp in the core. */
-	ZT_IDENTITY_TYPE_C25519 = 0,
-	ZT_IDENTITY_TYPE_P384 = 1
+	ZT_IDENTITY_TYPE_C25519 = 0, /* C25519/Ed25519 */
+	ZT_IDENTITY_TYPE_P384 = 1    /* Combined C25519/NIST-P-384 key */
 };
 
 /**
@@ -303,12 +303,6 @@ enum ZT_CredentialType
 	ZT_CREDENTIAL_TYPE_REVOCATION = 6
 };
 
-/* Trace events are sent and received as packed structures of a fixed size.
- * Normally we don't use this form of brittle encoding but in this case the
- * performance benefit is non-trivial.
- *
- * All integer fields larger than one byte are stored in big-endian order. */
-
 /**
  * Flag indicating that VL1 tracing should be generated
  */
@@ -391,30 +385,27 @@ enum ZT_TraceFrameDropReason
  * if possible for consistency. Not all of these are used (yet?) but they are defined
  * for possible future use and the structure is sized to support them.
  */
-enum ZT_TraceEventPathAddressType
+enum ZT_EndpointType
 {
-	ZT_TRACE_EVENT_PATH_TYPE_NIL =          0, /* none/empty */
-	ZT_TRACE_EVENT_PATH_TYPE_ZEROTIER =     1, /* 5-byte ZeroTier + 48-byte identity hash */
-	ZT_TRACE_EVENT_PATH_TYPE_ETHERNET =     2, /* 6-byte Ethernet */
-	ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V4 =  4, /* 4-byte IPv4 */
-	ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V6 =  6  /* 16-byte IPv6 */
+	ZT_ENDPOINT_TYPE_NIL =          0, /* none/empty */
+	ZT_ENDPOINT_TYPE_ZEROTIER =     1, /* 5-byte ZeroTier + 48-byte identity hash */
+	ZT_ENDPOINT_TYPE_ETHERNET =     2, /* 6-byte Ethernet */
+	ZT_ENDPOINT_TYPE_INETADDR_V4 =  4, /* 4-byte IPv4 */
+	ZT_ENDPOINT_TYPE_INETADDR_V6 =  6  /* 16-byte IPv6 */
 };
 
 /**
- * Maximum integer value of enum ZT_TraceEventPathAddressType
- */
-#define ZT_TRACE_EVENT_PATH_TYPE__MAX 6
-
-/**
- * Reasons for trying new paths
+ * Protocol bits allowed for endpoint addresses.
  */
-enum ZT_TraceTryingNewPathReason
+enum ZT_EndpointProtocol
 {
-	ZT_TRACE_TRYING_NEW_PATH_REASON_PACKET_RECEIVED_FROM_UNKNOWN_PATH = 1,
-	ZT_TRACE_TRYING_NEW_PATH_REASON_RECEIVED_PUSH_DIRECT_PATHS = 2,
-	ZT_TRACE_TRYING_NEW_PATH_REASON_RENDEZVOUS = 3,
-	ZT_TRACE_TRYING_NEW_PATH_REASON_BOOTSTRAP_ADDRESS = 4,
-	ZT_TRACE_TRYING_NEW_PATH_REASON_EXPLICITLY_SUGGESTED_ADDRESS = 5
+	ZT_ENDPOINT_PROTO_DGRAM =       0x0001,
+	ZT_ENDPOINT_PROTO_STREAM  =     0x0002,
+	ZT_ENDPOINT_PROTO_HTTP2 =       0x0004,
+	ZT_ENDPOINT_PROTO_HTTPS2 =      0x0008,
+	ZT_ENDPOINT_PROTO_WS =          0x0010,
+	ZT_ENDPOINT_PROTO_WEBRTC =      0x0020,
+	ZT_ENDPOINT_PROTO_WIREGUARD =   0x0040
 };
 
 /**

+ 0 - 3
node/Buf.hpp

@@ -21,9 +21,6 @@
 #include "TriviallyCopyable.hpp"
 #include "FCV.hpp"
 
-#include <cstdint>
-#include <cstring>
-#include <cstdlib>
 #include <stdexcept>
 #include <utility>
 #include <algorithm>

+ 10 - 10
node/Defragmenter.hpp

@@ -217,10 +217,10 @@ public:
 		if ((via)&&(!e->via)) {
 			e->via = via;
 			bool tooManyPerPath = false;
-			via->_inboundFragmentedMessages_l.lock();
+			via->m_inboundFragmentedMessages_l.lock();
 			try {
-				if (via->_inboundFragmentedMessages.size() < MFP) {
-					via->_inboundFragmentedMessages.insert(messageId);
+				if (via->m_inboundFragmentedMessages.size() < MFP) {
+					via->m_inboundFragmentedMessages.insert(messageId);
 				} else {
 					tooManyPerPath = true;
 				}
@@ -229,7 +229,7 @@ public:
 				// it as limit exceeded.
 				tooManyPerPath = true;
 			}
-			via->_inboundFragmentedMessages_l.unlock();
+			via->m_inboundFragmentedMessages_l.unlock();
 			if (tooManyPerPath)
 				return ERR_TOO_MANY_FRAGMENTS_FOR_PATH;
 		}
@@ -255,9 +255,9 @@ public:
 		if ((e->fragmentsReceived >= e->totalFragmentsExpected)&&(e->totalFragmentsExpected > 0)) {
 			// This message is done so de-register it with its path if one is associated.
 			if (e->via) {
-				e->via->_inboundFragmentedMessages_l.lock();
-				e->via->_inboundFragmentedMessages.erase(messageId);
-				e->via->_inboundFragmentedMessages_l.unlock();
+				e->via->m_inboundFragmentedMessages_l.lock();
+				e->via->m_inboundFragmentedMessages.erase(messageId);
+				e->via->m_inboundFragmentedMessages_l.unlock();
 				e->via.zero();
 			}
 
@@ -312,9 +312,9 @@ private:
 		ZT_INLINE ~p_E()
 		{
 			if (via) {
-				via->_inboundFragmentedMessages_l.lock();
-				via->_inboundFragmentedMessages.erase(id);
-				via->_inboundFragmentedMessages_l.unlock();
+				via->m_inboundFragmentedMessages_l.lock();
+				via->m_inboundFragmentedMessages.erase(id);
+				via->m_inboundFragmentedMessages_l.unlock();
 			}
 		}
 

+ 1 - 1
node/Dictionary.cpp

@@ -146,7 +146,7 @@ void Dictionary::clear()
 	m_entries.clear();
 }
 
-void Dictionary::encode(std::vector<uint8_t> &out) const
+void Dictionary::encode(Vector<uint8_t> &out) const
 {
 	uint64_t str[2] = { 0,0 }; // second entry causes all strings to be null-terminated even if 8 chars in length
 

+ 1 - 1
node/Dictionary.hpp

@@ -149,7 +149,7 @@ public:
 	 *
 	 * @param out String encoded dictionary
 	 */
-	void encode(std::vector<uint8_t> &out) const;
+	void encode(Vector<uint8_t> &out) const;
 
 	/**
 	 * Decode a string encoded dictionary

+ 13 - 25
node/Endpoint.hpp

@@ -21,20 +21,12 @@
 #include "TriviallyCopyable.hpp"
 #include "Fingerprint.hpp"
 
-#include <cstdio>
-#include <cstdlib>
-#include <cstdint>
-#include <cstring>
-
 #define ZT_ENDPOINT_MARSHAL_SIZE_MAX 64
 
 namespace ZeroTier {
 
 /**
- * Endpoint variant specifying some form of network endpoint
- *
- * This data structure supports a number of types that are not yet actually used:
- * DNSNAME, URL, and ETHERNET. These are present to reserve them for future use.
+ * Endpoint variant specifying some form of network endpoint.
  */
 class Endpoint : public TriviallyCopyable
 {
@@ -46,29 +38,25 @@ public:
 	 */
 	enum Type
 	{
-		TYPE_NIL =          ZT_TRACE_EVENT_PATH_TYPE_NIL,
-		TYPE_ZEROTIER =     ZT_TRACE_EVENT_PATH_TYPE_ZEROTIER,
-		TYPE_ETHERNET =     ZT_TRACE_EVENT_PATH_TYPE_ETHERNET,
-		TYPE_INETADDR_V4 =  ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V4,
-		TYPE_INETADDR_V6 =  ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V6
+		TYPE_NIL =          ZT_ENDPOINT_TYPE_NIL,
+		TYPE_ZEROTIER =     ZT_ENDPOINT_TYPE_ZEROTIER,
+		TYPE_ETHERNET =     ZT_ENDPOINT_TYPE_ETHERNET,
+		TYPE_INETADDR_V4 =  ZT_ENDPOINT_TYPE_INETADDR_V4,
+		TYPE_INETADDR_V6 =  ZT_ENDPOINT_TYPE_INETADDR_V6
 	};
 
 	/**
 	 * Protocol identifier bits.
-	 *
-	 * Endpoint types can support more than one of these, though it depends on the type.
-	 *
-	 * Most of these are reserved for possible future use.
 	 */
 	enum Protocol
 	{
-		PROTO_DGRAM =       0x0001, // UDP for IP or naked Ethernet frames
-		PROTO_STREAM  =     0x0002, // TCP
-		PROTO_HTTP2 =       0x0004, // HTTP2 bidirectional protocol
-		PROTO_HTTPS2 =      0x0008, // HTTP2 over SSL/TLS
-		PROTO_WS =          0x0010, // Web sockets
-		PROTO_WEBRTC =      0x0020, // WebRTC data channels
-		PROTO_WIREGUARD =   0x0040  // Wireguard as low-level transport
+		PROTO_DGRAM =       ZT_ENDPOINT_PROTO_DGRAM,
+		PROTO_STREAM  =     ZT_ENDPOINT_PROTO_STREAM,
+		PROTO_HTTP2 =       ZT_ENDPOINT_PROTO_HTTP2,
+		PROTO_HTTPS2 =      ZT_ENDPOINT_PROTO_HTTPS2,
+		PROTO_WS =          ZT_ENDPOINT_PROTO_WS,
+		PROTO_WEBRTC =      ZT_ENDPOINT_PROTO_WEBRTC,
+		PROTO_WIREGUARD =   ZT_ENDPOINT_PROTO_WIREGUARD
 	};
 
 	ZT_INLINE Endpoint() noexcept { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)

+ 0 - 2
node/Fingerprint.hpp

@@ -99,8 +99,6 @@ private:
 	ZT_Fingerprint m_cfp;
 };
 
-static_assert(sizeof(Fingerprint) == sizeof(ZT_Fingerprint),"Fingerprint should be the same size as the underlying C ZT_Fingerprint");
-
 } // namespace ZeroTier
 
 #endif

+ 115 - 224
node/InetAddress.cpp

@@ -26,10 +26,10 @@ const InetAddress InetAddress::NIL;
 
 InetAddress::IpScope InetAddress::ipScope() const noexcept
 {
-	switch(m_sockaddr.ss_family) {
+	switch(as.ss.ss_family) {
 
 		case AF_INET: {
-			const uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr);
+			const uint32_t ip = Utils::ntoh((uint32_t)as.sa_in.sin_addr.s_addr);
 			switch(ip >> 24U) {
 				case 0x00: return IP_SCOPE_NONE;                                   // 0.0.0.0/8 (reserved, never used)
 				case 0x06: return IP_SCOPE_PSEUDOPRIVATE;                          // 6.0.0.0/8 (US Army)
@@ -68,7 +68,7 @@ InetAddress::IpScope InetAddress::ipScope() const noexcept
 		}
 
 		case AF_INET6: {
-			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
+			const uint8_t *const ip = as.sa_in6.sin6_addr.s6_addr;
 			if ((ip[0] & 0xf0U) == 0xf0) {
 				if (ip[0] == 0xff) return IP_SCOPE_MULTICAST;                      // ff00::/8
 				if ((ip[0] == 0xfe)&&((ip[1] & 0xc0U) == 0x80)) {
@@ -97,32 +97,33 @@ void InetAddress::set(const void *ipBytes,unsigned int ipLen,unsigned int port)
 {
 	memoryZero(this);
 	if (ipLen == 4) {
-		uint32_t ipb[1];
-		Utils::copy<4>(ipb,ipBytes);
-		m_sockaddr.ss_family = AF_INET;
-		reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr = ipb[0];
-		reinterpret_cast<sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
+		as.sa_in.sin_family = AF_INET;
+		as.sa_in.sin_port = Utils::hton((uint16_t)port);
+		as.sa_in.sin_addr.s_addr = Utils::loadAsIsEndian<uint32_t>(ipBytes);
 	} else if (ipLen == 16) {
-		m_sockaddr.ss_family = AF_INET6;
-		Utils::copy<16>(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr,ipBytes);
-		reinterpret_cast<sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
+		as.sa_in6.sin6_family = AF_INET6;
+		as.sa_in6.sin6_port = Utils::hton((uint16_t)port);
+		Utils::copy<16>(as.sa_in6.sin6_addr.s6_addr,ipBytes);
 	}
 }
 
 bool InetAddress::isDefaultRoute() const noexcept
 {
-	switch(m_sockaddr.ss_family) {
+	switch(as.ss.ss_family) {
 		case AF_INET:
-			return ( (reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr == 0) && (reinterpret_cast<const sockaddr_in *>(this)->sin_port == 0) );
+			return ((as.sa_in.sin_port == 0)&&(as.sa_in.sin_addr.s_addr == 0));
 		case AF_INET6:
-			const uint8_t *ipb = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-			for(int i=0;i<16;++i) {
-				if (ipb[i])
-					return false;
+			if (as.sa_in6.sin6_port == 0) {
+				for (unsigned int i=0;i<16;++i) {
+					if (as.sa_in6.sin6_addr.s6_addr[i])
+						return false;
+				}
+				return true;
 			}
-			return (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port == 0);
+			return false;
+		default:
+			return false;
 	}
-	return false;
 }
 
 char *InetAddress::toString(char buf[ZT_INETADDRESS_STRING_SIZE_MAX]) const noexcept
@@ -139,22 +140,9 @@ char *InetAddress::toString(char buf[ZT_INETADDRESS_STRING_SIZE_MAX]) const noex
 char *InetAddress::toIpString(char buf[ZT_INETADDRESS_STRING_SIZE_MAX]) const noexcept
 {
 	buf[0] = (char)0;
-	switch(m_sockaddr.ss_family) {
-		case AF_INET: {
-#ifdef _WIN32
-			inet_ntop(AF_INET, (void*)&reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr, buf, INET_ADDRSTRLEN);
-#else
-			inet_ntop(AF_INET, &reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr, buf, INET_ADDRSTRLEN);
-#endif
- 		}	break;
-
-		case AF_INET6: {
-#ifdef _WIN32
-			inet_ntop(AF_INET6, (void*)reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr, buf, INET6_ADDRSTRLEN);
-#else
-			inet_ntop(AF_INET6, reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr, buf, INET6_ADDRSTRLEN);
-#endif
-		}	break;
+	switch(as.ss.ss_family) {
+		case AF_INET:  inet_ntop(AF_INET,&as.sa_in.sin_addr.s_addr,buf,INET_ADDRSTRLEN); break;
+		case AF_INET6: inet_ntop(AF_INET6,as.sa_in6.sin6_addr.s6_addr,buf,INET6_ADDRSTRLEN); break;
 	}
 	return buf;
 }
@@ -180,16 +168,14 @@ bool InetAddress::fromString(const char *ipSlashPort) noexcept
 	}
 
 	if (strchr(buf,':')) {
-		sockaddr_in6 *const in6 = reinterpret_cast<sockaddr_in6 *>(this); // NOLINT(hicpp-use-auto,modernize-use-auto)
-		inet_pton(AF_INET6, buf, &in6->sin6_addr.s6_addr);
-		in6->sin6_family = AF_INET6;
-		in6->sin6_port = Utils::hton((uint16_t)port);
+		as.sa_in6.sin6_family = AF_INET6;
+		as.sa_in6.sin6_port = Utils::hton((uint16_t)port);
+		inet_pton(AF_INET6,buf,as.sa_in6.sin6_addr.s6_addr);
 		return true;
 	} else if (strchr(buf,'.')) {
-		sockaddr_in *const in = reinterpret_cast<sockaddr_in *>(this); // NOLINT(hicpp-use-auto,modernize-use-auto)
-		inet_pton(AF_INET, buf, &in->sin_addr.s_addr);
-		in->sin_family = AF_INET;
-		in->sin_port = Utils::hton((uint16_t)port);
+		as.sa_in.sin_family = AF_INET;
+		as.sa_in.sin_port = Utils::hton((uint16_t)port);
+		inet_pton(AF_INET,buf,&as.sa_in.sin_addr.s_addr);
 		return true;
 	}
 
@@ -199,9 +185,9 @@ bool InetAddress::fromString(const char *ipSlashPort) noexcept
 InetAddress InetAddress::netmask() const noexcept
 {
 	InetAddress r(*this);
-	switch(r.m_sockaddr.ss_family) {
+	switch(r.as.ss.ss_family) {
 		case AF_INET:
-			reinterpret_cast<sockaddr_in *>(&r)->sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
+			r.as.sa_in.sin_addr.s_addr = Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
 			break;
 		case AF_INET6: {
 			uint64_t nm[2];
@@ -213,7 +199,7 @@ InetAddress InetAddress::netmask() const noexcept
 				nm[0] = 0;
 				nm[1] = 0;
 			}
-			Utils::copy<16>(reinterpret_cast<sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm);
+			Utils::copy<16>(r.as.sa_in6.sin6_addr.s6_addr,nm);
 		}	break;
 	}
 	return r;
@@ -221,7 +207,7 @@ InetAddress InetAddress::netmask() const noexcept
 
 InetAddress InetAddress::broadcast() const noexcept
 {
-	if (m_sockaddr.ss_family == AF_INET) {
+	if (as.ss.ss_family == AF_INET) {
 		InetAddress r(*this);
 		reinterpret_cast<sockaddr_in *>(&r)->sin_addr.s_addr |= Utils::hton((uint32_t)(0xffffffffU >> netmaskBits()));
 		return r;
@@ -232,9 +218,9 @@ InetAddress InetAddress::broadcast() const noexcept
 InetAddress InetAddress::network() const noexcept
 {
 	InetAddress r(*this);
-	switch(r.m_sockaddr.ss_family) {
+	switch(r.as.ss.ss_family) {
 		case AF_INET:
-			reinterpret_cast<sockaddr_in *>(&r)->sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
+			r.as.sa_in.sin_addr.s_addr &= Utils::hton((uint32_t)(0xffffffffU << (32 - netmaskBits())));
 			break;
 		case AF_INET6: {
 			uint64_t nm[2];
@@ -242,7 +228,7 @@ InetAddress InetAddress::network() const noexcept
 			Utils::copy<16>(nm,reinterpret_cast<sockaddr_in6 *>(&r)->sin6_addr.s6_addr);
 			nm[0] &= Utils::hton((uint64_t)((bits >= 64) ? 0xffffffffffffffffULL : (0xffffffffffffffffULL << (64 - bits))));
 			nm[1] &= Utils::hton((uint64_t)((bits <= 64) ? 0ULL : (0xffffffffffffffffULL << (128 - bits))));
-			Utils::copy<16>(reinterpret_cast<sockaddr_in6 *>(&r)->sin6_addr.s6_addr,nm);
+			Utils::copy<16>(r.as.sa_in6.sin6_addr.s6_addr,nm);
 		}	break;
 	}
 	return r;
@@ -250,15 +236,15 @@ InetAddress InetAddress::network() const noexcept
 
 bool InetAddress::isEqualPrefix(const InetAddress &addr) const noexcept
 {
-	if (addr.m_sockaddr.ss_family == m_sockaddr.ss_family) {
-		switch(m_sockaddr.ss_family) {
+	if (addr.as.ss.ss_family == as.ss.ss_family) {
+		switch(as.ss.ss_family) {
 			case AF_INET6: {
 				const InetAddress mask(netmask());
 				InetAddress addr_mask(addr.netmask());
-				const uint8_t *n = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(&addr_mask)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-				const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(&mask)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-				const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(&addr)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-				const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
+				const uint8_t *const n = addr_mask.as.sa_in6.sin6_addr.s6_addr;
+				const uint8_t *const m = mask.as.sa_in6.sin6_addr.s6_addr;
+				const uint8_t *const a = addr.as.sa_in6.sin6_addr.s6_addr;
+				const uint8_t *const b = as.sa_in6.sin6_addr.s6_addr;
 				for(unsigned int i=0;i<16;++i) {
 					if ((a[i] & m[i]) != (b[i] & n[i]))
 						return false;
@@ -272,22 +258,22 @@ bool InetAddress::isEqualPrefix(const InetAddress &addr) const noexcept
 
 bool InetAddress::containsAddress(const InetAddress &addr) const noexcept
 {
-	if (addr.m_sockaddr.ss_family == m_sockaddr.ss_family) {
-		switch(m_sockaddr.ss_family) {
+	if (addr.as.ss.ss_family == as.ss.ss_family) {
+		switch(as.ss.ss_family) {
 			case AF_INET: {
 				const unsigned int bits = netmaskBits();
 				if (bits == 0)
 					return true;
 				return (
-					(Utils::ntoh((uint32_t)reinterpret_cast<const sockaddr_in *>(&addr)->sin_addr.s_addr) >> (32 - bits)) ==
-					(Utils::ntoh((uint32_t)reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr) >> (32 - bits))
+					(Utils::ntoh((uint32_t)addr.as.sa_in.sin_addr.s_addr) >> (32 - bits)) ==
+					(Utils::ntoh((uint32_t)as.sa_in.sin_addr.s_addr) >> (32 - bits))
 				);
 			}
 			case AF_INET6: {
 				const InetAddress mask(netmask());
-				const uint8_t *m = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(&mask)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-				const uint8_t *a = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(&addr)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
-				const uint8_t *b = reinterpret_cast<const uint8_t *>(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
+				const uint8_t *const m = mask.as.sa_in6.sin6_addr.s6_addr;
+				const uint8_t *const a = addr.as.sa_in6.sin6_addr.s6_addr;
+				const uint8_t *const b = as.sa_in6.sin6_addr.s6_addr;
 				for(unsigned int i=0;i<16;++i) {
 					if ((a[i] & m[i]) != b[i])
 						return false;
@@ -299,42 +285,16 @@ bool InetAddress::containsAddress(const InetAddress &addr) const noexcept
 	return false;
 }
 
-void InetAddress::forTrace(ZT_TraceEventPathAddress &ta) const noexcept
-{
-	uint32_t tmp;
-	switch(m_sockaddr.ss_family) {
-		default:
-			Utils::zero<sizeof(ZT_TraceEventPathAddress)>(&ta);
-			break;
-		case AF_INET:
-			ta.type = ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V4;
-			tmp = (uint32_t)(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr);
-			ta.address[0] = reinterpret_cast<const uint8_t *>(&tmp)[0];
-			ta.address[1] = reinterpret_cast<const uint8_t *>(&tmp)[1];
-			ta.address[2] = reinterpret_cast<const uint8_t *>(&tmp)[2];
-			ta.address[3] = reinterpret_cast<const uint8_t *>(&tmp)[3];
-			Utils::zero<sizeof(ta.address) - 4>(ta.address + 4);
-			ta.port = reinterpret_cast<const sockaddr_in *>(this)->sin_port;
-			break;
-		case AF_INET6:
-			ta.type = ZT_TRACE_EVENT_PATH_TYPE_INETADDR_V6;
-			Utils::copy<16>(ta.address,reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr);
-			Utils::zero<sizeof(ta.address) - 16>(ta.address + 16);
-			ta.port = reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port;
-			break;
-	}
-}
-
 bool InetAddress::isNetwork() const noexcept
 {
-	switch(m_sockaddr.ss_family) {
+	switch(as.ss.ss_family) {
 		case AF_INET: {
 			unsigned int bits = netmaskBits();
 			if (bits <= 0)
 				return false;
 			if (bits >= 32)
 				return false;
-			uint32_t ip = Utils::ntoh((uint32_t)reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr);
+			const uint32_t ip = Utils::ntoh((uint32_t)as.sa_in.sin_addr.s_addr);
 			return ((ip & (0xffffffffU >> bits)) == 0);
 		}
 		case AF_INET6: {
@@ -343,7 +303,7 @@ bool InetAddress::isNetwork() const noexcept
 				return false;
 			if (bits >= 128)
 				return false;
-			const unsigned char *ip = reinterpret_cast<const unsigned char *>(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr); // NOLINT(hicpp-use-auto,modernize-use-auto)
+			const uint8_t *const ip = as.sa_in6.sin6_addr.s6_addr;
 			unsigned int p = bits / 8;
 			if ((ip[p++] & (0xffU >> (bits % 8))) != 0)
 				return false;
@@ -360,22 +320,21 @@ bool InetAddress::isNetwork() const noexcept
 int InetAddress::marshal(uint8_t data[ZT_INETADDRESS_MARSHAL_SIZE_MAX]) const noexcept
 {
 	unsigned int port;
-	switch(m_sockaddr.ss_family) {
+	switch(as.ss.ss_family) {
 		case AF_INET:
 			port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in *>(this)->sin_port);
 			data[0] = 4;
-			data[1] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[0];
-			data[2] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[1];
-			data[3] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[2];
-			data[4] = reinterpret_cast<const uint8_t *>(&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr))[3];
+			data[1] = reinterpret_cast<const uint8_t *>(&as.sa_in.sin_addr.s_addr)[0];
+			data[2] = reinterpret_cast<const uint8_t *>(&as.sa_in.sin_addr.s_addr)[1];
+			data[3] = reinterpret_cast<const uint8_t *>(&as.sa_in.sin_addr.s_addr)[2];
+			data[4] = reinterpret_cast<const uint8_t *>(&as.sa_in.sin_addr.s_addr)[3];
 			data[5] = (uint8_t)(port >> 8U);
 			data[6] = (uint8_t)port;
 			return 7;
 		case AF_INET6:
-			port = Utils::ntoh((uint16_t)reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port);
+			port = Utils::ntoh((uint16_t)as.sa_in6.sin6_port);
 			data[0] = 6;
-			for(int i=0;i<16;++i)
-				data[i+1] = reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr[i];
+			Utils::copy<16>(data + 1,as.sa_in6.sin6_addr.s6_addr);
 			data[17] = (uint8_t)(port >> 8U);
 			data[18] = (uint8_t)port;
 			return 19;
@@ -389,141 +348,74 @@ int InetAddress::unmarshal(const uint8_t *restrict data,const int len) noexcept
 {
 	if (len <= 0)
 		return -1;
+	memoryZero(this);
 	switch(data[0]) {
 		case 0:
 			return 1;
 		case 4:
 			if (len < 7)
 				return -1;
-			memoryZero(this);
-			reinterpret_cast<sockaddr_in *>(this)->sin_family = AF_INET;
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[0] = data[1];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[1] = data[2];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[2] = data[3];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_addr.s_addr))[3] = data[4];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_port))[sizeof(reinterpret_cast<sockaddr_in *>(this)->sin_port)-2] = data[5];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in *>(this)->sin_port))[sizeof(reinterpret_cast<sockaddr_in *>(this)->sin_port)-1] = data[6];
+			as.sa_in.sin_family = AF_INET;
+			as.sa_in.sin_port = Utils::loadAsIsEndian<uint16_t>(data + 5);
+			as.sa_in.sin_addr.s_addr = Utils::loadAsIsEndian<uint32_t>(data + 1);
 			return 7;
 		case 6:
 			if (len < 19)
 				return -1;
-			memoryZero(this);
-			reinterpret_cast<sockaddr_in6 *>(this)->sin6_family = AF_INET6;
-			for(int i=0;i<16;i++)
-				(reinterpret_cast<sockaddr_in6 *>(this)->sin6_addr.s6_addr)[i] = data[i+1];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port))[sizeof(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port)-2] = data[17];
-			reinterpret_cast<uint8_t *>(&(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port))[sizeof(reinterpret_cast<sockaddr_in6 *>(this)->sin6_port)-1] = data[18];
+			as.sa_in6.sin6_family = AF_INET6;
+			as.sa_in6.sin6_port = Utils::loadAsIsEndian<uint16_t>(data + 17);
+			Utils::copy<16>(as.sa_in6.sin6_addr.s6_addr,data + 1);
 			return 19;
 		default:
 			return -1;
 	}
 }
 
-bool InetAddress::operator==(const InetAddress &a) const noexcept
-{
-	if (m_sockaddr.ss_family == a.m_sockaddr.ss_family) {
-		switch(m_sockaddr.ss_family) {
-			case AF_INET:
-				return (
-					(reinterpret_cast<const sockaddr_in *>(this)->sin_port == reinterpret_cast<const sockaddr_in *>(&a)->sin_port)&&
-					(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const sockaddr_in *>(&a)->sin_addr.s_addr));
-			case AF_INET6:
-				return (
-					(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_port)&&
-					(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_flowinfo)&&
-					(memcmp(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0)&&
-					(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_scope_id == reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_scope_id));
-			default:
-				return (memcmp(this,&a,sizeof(InetAddress)) == 0);
-		}
-	}
-	return false;
-}
-
-bool InetAddress::operator<(const InetAddress &a) const noexcept
-{
-	if (m_sockaddr.ss_family < a.m_sockaddr.ss_family)
-		return true;
-	else if (m_sockaddr.ss_family == a.m_sockaddr.ss_family) {
-		switch(m_sockaddr.ss_family) {
-			case AF_INET:
-				if (reinterpret_cast<const sockaddr_in *>(this)->sin_port < reinterpret_cast<const sockaddr_in *>(&a)->sin_port)
-					return true;
-				else if (reinterpret_cast<const sockaddr_in *>(this)->sin_port == reinterpret_cast<const sockaddr_in *>(&a)->sin_port) {
-					if (reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr < reinterpret_cast<const sockaddr_in *>(&a)->sin_addr.s_addr)
-						return true;
-				}
-				break;
-			case AF_INET6:
-				if (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port < reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_port)
-					return true;
-				else if (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port == reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_port) {
-					if (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_flowinfo < reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_flowinfo)
-						return true;
-					else if (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_flowinfo == reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_flowinfo) {
-						if (memcmp(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) < 0)
-							return true;
-						else if (memcmp(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0) {
-							if (reinterpret_cast<const sockaddr_in6 *>(this)->sin6_scope_id < reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_scope_id)
-								return true;
-						}
-					}
-				}
-				break;
-			default:
-				return (memcmp(this,&a,sizeof(InetAddress)) < 0);
-		}
-	}
-	return false;
-}
-
 InetAddress InetAddress::makeIpv6LinkLocal(const MAC &mac) noexcept
 {
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r); // NOLINT(hicpp-use-auto,modernize-use-auto)
-	sin6->sin6_family = AF_INET6;
-	sin6->sin6_addr.s6_addr[0] = 0xfe;
-	sin6->sin6_addr.s6_addr[1] = 0x80;
-	sin6->sin6_addr.s6_addr[2] = 0x00;
-	sin6->sin6_addr.s6_addr[3] = 0x00;
-	sin6->sin6_addr.s6_addr[4] = 0x00;
-	sin6->sin6_addr.s6_addr[5] = 0x00;
-	sin6->sin6_addr.s6_addr[6] = 0x00;
-	sin6->sin6_addr.s6_addr[7] = 0x00;
-	sin6->sin6_addr.s6_addr[8] = mac[0] & 0xfdU;
-	sin6->sin6_addr.s6_addr[9] = mac[1];
-	sin6->sin6_addr.s6_addr[10] = mac[2];
-	sin6->sin6_addr.s6_addr[11] = 0xff;
-	sin6->sin6_addr.s6_addr[12] = 0xfe;
-	sin6->sin6_addr.s6_addr[13] = mac[3];
-	sin6->sin6_addr.s6_addr[14] = mac[4];
-	sin6->sin6_addr.s6_addr[15] = mac[5];
-	sin6->sin6_port = Utils::hton((uint16_t)64);
+	r.as.sa_in6.sin6_family = AF_INET6;
+	r.as.sa_in6.sin6_port = ZT_CONST_TO_BE_UINT16(64);
+	r.as.sa_in6.sin6_addr.s6_addr[0] = 0xfe;
+	r.as.sa_in6.sin6_addr.s6_addr[1] = 0x80;
+	r.as.sa_in6.sin6_addr.s6_addr[2] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[3] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[4] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[5] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[6] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[7] = 0x00;
+	r.as.sa_in6.sin6_addr.s6_addr[8] = mac[0] & 0xfdU;
+	r.as.sa_in6.sin6_addr.s6_addr[9] = mac[1];
+	r.as.sa_in6.sin6_addr.s6_addr[10] = mac[2];
+	r.as.sa_in6.sin6_addr.s6_addr[11] = 0xff;
+	r.as.sa_in6.sin6_addr.s6_addr[12] = 0xfe;
+	r.as.sa_in6.sin6_addr.s6_addr[13] = mac[3];
+	r.as.sa_in6.sin6_addr.s6_addr[14] = mac[4];
+	r.as.sa_in6.sin6_addr.s6_addr[15] = mac[5];
 	return r;
 }
 
 InetAddress InetAddress::makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress) noexcept
 {
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r); // NOLINT(hicpp-use-auto,modernize-use-auto)
-	sin6->sin6_family = AF_INET6;
-	sin6->sin6_addr.s6_addr[0] = 0xfd;
-	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56U);
-	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48U);
-	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40U);
-	sin6->sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32U);
-	sin6->sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24U);
-	sin6->sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16U);
-	sin6->sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8U);
-	sin6->sin6_addr.s6_addr[8] = (uint8_t)nwid;
-	sin6->sin6_addr.s6_addr[9] = 0x99;
-	sin6->sin6_addr.s6_addr[10] = 0x93;
-	sin6->sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32U);
-	sin6->sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24U);
-	sin6->sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16U);
-	sin6->sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8U);
-	sin6->sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
-	sin6->sin6_port = Utils::hton((uint16_t)88); // /88 includes 0xfd + network ID, discriminating by device ID below that
+	r.as.sa_in6.sin6_family = AF_INET6;
+	r.as.sa_in6.sin6_port = ZT_CONST_TO_BE_UINT16(88); // /88 includes 0xfd + network ID, discriminating by device ID below that
+	r.as.sa_in6.sin6_addr.s6_addr[0] = 0xfd;
+	r.as.sa_in6.sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 56U);
+	r.as.sa_in6.sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 48U);
+	r.as.sa_in6.sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 40U);
+	r.as.sa_in6.sin6_addr.s6_addr[4] = (uint8_t)(nwid >> 32U);
+	r.as.sa_in6.sin6_addr.s6_addr[5] = (uint8_t)(nwid >> 24U);
+	r.as.sa_in6.sin6_addr.s6_addr[6] = (uint8_t)(nwid >> 16U);
+	r.as.sa_in6.sin6_addr.s6_addr[7] = (uint8_t)(nwid >> 8U);
+	r.as.sa_in6.sin6_addr.s6_addr[8] = (uint8_t)nwid;
+	r.as.sa_in6.sin6_addr.s6_addr[9] = 0x99;
+	r.as.sa_in6.sin6_addr.s6_addr[10] = 0x93;
+	r.as.sa_in6.sin6_addr.s6_addr[11] = (uint8_t)(zeroTierAddress >> 32U);
+	r.as.sa_in6.sin6_addr.s6_addr[12] = (uint8_t)(zeroTierAddress >> 24U);
+	r.as.sa_in6.sin6_addr.s6_addr[13] = (uint8_t)(zeroTierAddress >> 16U);
+	r.as.sa_in6.sin6_addr.s6_addr[14] = (uint8_t)(zeroTierAddress >> 8U);
+	r.as.sa_in6.sin6_addr.s6_addr[15] = (uint8_t)zeroTierAddress;
 	return r;
 }
 
@@ -531,20 +423,19 @@ InetAddress InetAddress::makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress)
 {
 	nwid ^= (nwid >> 32U);
 	InetAddress r;
-	sockaddr_in6 *const sin6 = reinterpret_cast<sockaddr_in6 *>(&r); // NOLINT(hicpp-use-auto,modernize-use-auto)
-	sin6->sin6_family = AF_INET6;
-	sin6->sin6_addr.s6_addr[0] = 0xfc;
-	sin6->sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24U);
-	sin6->sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16U);
-	sin6->sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8U);
-	sin6->sin6_addr.s6_addr[4] = (uint8_t)nwid;
-	sin6->sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32U);
-	sin6->sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24U);
-	sin6->sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16U);
-	sin6->sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8U);
-	sin6->sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
-	sin6->sin6_addr.s6_addr[15] = 0x01;
-	sin6->sin6_port = Utils::hton((uint16_t)40);
+	r.as.sa_in6.sin6_family = AF_INET6;
+	r.as.sa_in6.sin6_port = ZT_CONST_TO_BE_UINT16(40);
+	r.as.sa_in6.sin6_addr.s6_addr[0] = 0xfc;
+	r.as.sa_in6.sin6_addr.s6_addr[1] = (uint8_t)(nwid >> 24U);
+	r.as.sa_in6.sin6_addr.s6_addr[2] = (uint8_t)(nwid >> 16U);
+	r.as.sa_in6.sin6_addr.s6_addr[3] = (uint8_t)(nwid >> 8U);
+	r.as.sa_in6.sin6_addr.s6_addr[4] = (uint8_t)nwid;
+	r.as.sa_in6.sin6_addr.s6_addr[5] = (uint8_t)(zeroTierAddress >> 32U);
+	r.as.sa_in6.sin6_addr.s6_addr[6] = (uint8_t)(zeroTierAddress >> 24U);
+	r.as.sa_in6.sin6_addr.s6_addr[7] = (uint8_t)(zeroTierAddress >> 16U);
+	r.as.sa_in6.sin6_addr.s6_addr[8] = (uint8_t)(zeroTierAddress >> 8U);
+	r.as.sa_in6.sin6_addr.s6_addr[9] = (uint8_t)zeroTierAddress;
+	r.as.sa_in6.sin6_addr.s6_addr[15] = 0x01;
 	return r;
 }
 

+ 103 - 97
node/InetAddress.hpp

@@ -35,16 +35,6 @@ namespace ZeroTier {
  */
 struct InetAddress : public TriviallyCopyable
 {
-private:
-	// Internal function to copy any sockaddr_X structure to this one even if it's smaller and unpadded.
-	template<typename SA>
-	ZT_INLINE void copySockaddrToThis(const SA *sa) noexcept
-	{
-		Utils::copy<sizeof(SA)>(reinterpret_cast<void *>(this),sa);
-		if (sizeof(SA) < sizeof(InetAddress))
-			Utils::zero<sizeof(InetAddress) - sizeof(SA)>(reinterpret_cast<uint8_t *>(this) + sizeof(SA));
-	}
-
 public:
 	/**
 	 * Loopback IPv4 address (no port)
@@ -83,67 +73,62 @@ public:
 	// Hasher for unordered sets and maps in C++11
 	struct Hasher { ZT_INLINE std::size_t operator()(const InetAddress &a) const noexcept { return (std::size_t)a.hashCode(); } };
 
-	ZT_INLINE InetAddress() noexcept { memoryZero(this); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	ZT_INLINE InetAddress(const InetAddress &a) noexcept { memoryCopy(this,&a); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_storage &ss) noexcept { *this = ss; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_storage *ss) noexcept { *this = ss; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr &sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr *sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_in &sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_in *sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_in6 &sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const sockaddr_in6 *sa) noexcept { *this = sa; } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	ZT_INLINE InetAddress(const void *ipBytes,unsigned int ipLen,unsigned int port) noexcept { this->set(ipBytes,ipLen,port); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	ZT_INLINE InetAddress(const uint32_t ipv4,unsigned int port) noexcept { this->set(&ipv4,4,port); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-	explicit ZT_INLINE InetAddress(const char *ipSlashPort) noexcept { this->fromString(ipSlashPort); } // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-
-	ZT_INLINE InetAddress &operator=(const InetAddress &a) noexcept
-	{
-		memoryCopy(this,a);
-		return *this;
-	}
+	ZT_INLINE InetAddress() noexcept { memoryZero(this); }
+
+	explicit ZT_INLINE InetAddress(const sockaddr_storage &ss) noexcept { *this = ss; }
+	explicit ZT_INLINE InetAddress(const sockaddr_storage *const ss) noexcept { *this = ss; }
+	explicit ZT_INLINE InetAddress(const sockaddr &sa) noexcept { *this = sa; }
+	explicit ZT_INLINE InetAddress(const sockaddr *const sa) noexcept { *this = sa; }
+	explicit ZT_INLINE InetAddress(const sockaddr_in &sa) noexcept { *this = sa; }
+	explicit ZT_INLINE InetAddress(const sockaddr_in *const sa) noexcept { *this = sa; }
+	explicit ZT_INLINE InetAddress(const sockaddr_in6 &sa) noexcept { *this = sa; }
+	explicit ZT_INLINE InetAddress(const sockaddr_in6 *const sa) noexcept { *this = sa; }
+
+	ZT_INLINE InetAddress(const void *const ipBytes,const unsigned int ipLen,const unsigned int port) noexcept { this->set(ipBytes,ipLen,port); }
+	ZT_INLINE InetAddress(const uint32_t ipv4,const unsigned int port) noexcept { this->set(&ipv4,4,port); }
+	explicit ZT_INLINE InetAddress(const char *const ipSlashPort) noexcept { this->fromString(ipSlashPort); }
+
 	ZT_INLINE InetAddress &operator=(const sockaddr_storage &ss) noexcept
 	{
-		memoryCopyUnsafe(this,&ss);
-		return *this;
+		as.ss = ss;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr_storage *ss) noexcept
 	{
 		if (ss)
-			memoryCopyUnsafe(this,ss);
+			as.ss = *ss;
 		else memoryZero(this);
 		return *this;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr_in &sa) noexcept
 	{
-		copySockaddrToThis(&sa);
+		as.sa_in = sa;
 		return *this;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr_in *sa) noexcept
 	{
 		if (sa)
-			copySockaddrToThis(sa);
+			as.sa_in = *sa;
 		else memoryZero(this);
 		return *this;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr_in6 &sa) noexcept
 	{
-		copySockaddrToThis(&sa);
+		as.sa_in6 = sa;
 		return *this;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr_in6 *sa) noexcept
 	{
 		if (sa)
-			copySockaddrToThis(sa);
+			as.sa_in6 = *sa;
 		else memoryZero(this);
 		return *this;
 	}
 	ZT_INLINE InetAddress &operator=(const sockaddr &sa) noexcept
 	{
 		if (sa.sa_family == AF_INET)
-			copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(&sa));
+			as.sa_in = *reinterpret_cast<const sockaddr_in *>(&sa);
 		else if (sa.sa_family == AF_INET6)
-			copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(&sa));
+			as.sa_in6 = *reinterpret_cast<const sockaddr_in6 *>(&sa);
 		else memoryZero(this);
 		return *this;
 	}
@@ -151,9 +136,9 @@ public:
 	{
 		if (sa) {
 			if (sa->sa_family == AF_INET)
-				copySockaddrToThis(reinterpret_cast<const sockaddr_in *>(sa));
+				as.sa_in = *reinterpret_cast<const sockaddr_in *>(sa);
 			else if (sa->sa_family == AF_INET6)
-				copySockaddrToThis(reinterpret_cast<const sockaddr_in6 *>(sa));
+				as.sa_in6 = *reinterpret_cast<const sockaddr_in6 *>(sa);
 			else memoryZero(this);
 		} else {
 			memoryZero(this);
@@ -166,7 +151,7 @@ public:
 	/**
 	 * @return Address family (ss_family in sockaddr_storage)
 	 */
-	ZT_INLINE uint8_t family() const noexcept { return m_sockaddr.ss_family; }
+	ZT_INLINE uint8_t family() const noexcept { return as.ss.ss_family; }
 
 	/**
 	 * @return IP scope classification (e.g. loopback, link-local, private, global)
@@ -189,13 +174,9 @@ public:
 	 */
 	ZT_INLINE void setPort(unsigned int port) noexcept
 	{
-		switch(m_sockaddr.ss_family) {
-			case AF_INET:
-				reinterpret_cast<sockaddr_in *>(this)->sin_port = Utils::hton((uint16_t)port);
-				break;
-			case AF_INET6:
-				reinterpret_cast<sockaddr_in6 *>(this)->sin6_port = Utils::hton((uint16_t)port);
-				break;
+		switch(as.ss.ss_family) {
+			case AF_INET:  as.sa_in.sin_port = Utils::hton((uint16_t)port); break;
+			case AF_INET6: as.sa_in6.sin6_port = Utils::hton((uint16_t)port); break;
 		}
 	}
 
@@ -227,9 +208,9 @@ public:
 	 */
 	ZT_INLINE unsigned int port() const noexcept
 	{
-		switch(m_sockaddr.ss_family) {
-			case AF_INET:  return Utils::ntoh((uint16_t)(reinterpret_cast<const sockaddr_in *>(this)->sin_port));
-			case AF_INET6: return Utils::ntoh((uint16_t)(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_port));
+		switch(as.ss.ss_family) {
+			case AF_INET:  return Utils::ntoh((uint16_t)as.sa_in.sin_port);
+			case AF_INET6: return Utils::ntoh((uint16_t)as.sa_in6.sin6_port);
 			default:       return 0;
 		}
 	}
@@ -251,8 +232,8 @@ public:
 	ZT_INLINE bool netmaskBitsValid() const noexcept
 	{
 		const unsigned int n = port();
-		switch(m_sockaddr.ss_family) {
-			case AF_INET: return (n <= 32);
+		switch(as.ss.ss_family) {
+			case AF_INET:  return (n <= 32);
 			case AF_INET6: return (n <= 128);
 		}
 		return false;
@@ -311,21 +292,21 @@ public:
 	/**
 	 * @return True if this is an IPv4 address
 	 */
-	ZT_INLINE bool isV4() const noexcept { return (family() == AF_INET); }
+	ZT_INLINE bool isV4() const noexcept { return (as.ss.ss_family == AF_INET); }
 
 	/**
 	 * @return True if this is an IPv6 address
 	 */
-	ZT_INLINE bool isV6() const noexcept { return (family() == AF_INET6); }
+	ZT_INLINE bool isV6() const noexcept { return (as.ss.ss_family == AF_INET6); }
 
 	/**
 	 * @return pointer to raw address bytes or NULL if not available
 	 */
 	ZT_INLINE const void *rawIpData() const noexcept
 	{
-		switch(m_sockaddr.ss_family) {
-			case AF_INET: return (const void *)&(reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr);
-			case AF_INET6: return (const void *)(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+		switch(as.ss.ss_family) {
+			case AF_INET: return reinterpret_cast<const void *>(&(as.sa_in.sin_addr.s_addr));
+			case AF_INET6: return reinterpret_cast<const void *>(as.sa_in6.sin6_addr.s6_addr);
 			default: return nullptr;
 		}
 	}
@@ -336,14 +317,14 @@ public:
 	ZT_INLINE InetAddress ipOnly() const noexcept
 	{
 		InetAddress r;
-		switch(m_sockaddr.ss_family) {
+		switch(as.ss.ss_family) {
 			case AF_INET:
-				reinterpret_cast<sockaddr_in *>(&r)->sin_family = AF_INET;
-				reinterpret_cast<sockaddr_in *>(&r)->sin_addr.s_addr = reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr;
+				r.as.sa_in.sin_family = AF_INET;
+				r.as.sa_in.sin_addr.s_addr = as.sa_in.sin_addr.s_addr;
 				break;
 			case AF_INET6:
-				reinterpret_cast<sockaddr_in6 *>(&r)->sin6_family = AF_INET;
-				Utils::copy<16>(reinterpret_cast<sockaddr_in6 *>(&r)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr);
+				r.as.sa_in6.sin6_family = AF_INET6;
+				Utils::copy<16>(r.as.sa_in6.sin6_addr.s6_addr,as.sa_in6.sin6_addr.s6_addr);
 				break;
 		}
 		return r;
@@ -357,13 +338,13 @@ public:
 	 */
 	ZT_INLINE bool ipsEqual(const InetAddress &a) const noexcept
 	{
-		const uint8_t f = m_sockaddr.ss_family;
-		if (f == a.m_sockaddr.ss_family) {
+		const uint8_t f = as.ss.ss_family;
+		if (f == a.as.ss.ss_family) {
 			if (f == AF_INET)
-				return (reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const sockaddr_in *>(&a)->sin_addr.s_addr);
+				return as.sa_in.sin_addr.s_addr == a.as.sa_in.sin_addr.s_addr;
 			if (f == AF_INET6)
-				return (memcmp(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
-			return (memcmp(this,&a,sizeof(InetAddress)) == 0);
+				return memcmp(as.sa_in6.sin6_addr.s6_addr,a.as.sa_in6.sin6_addr.s6_addr,16) == 0;
+			return memcmp(this,&a,sizeof(InetAddress)) == 0;
 		}
 		return false;
 	}
@@ -378,12 +359,12 @@ public:
 	 */
 	ZT_INLINE bool ipsEqual2(const InetAddress &a) const noexcept
 	{
-		const uint8_t f = m_sockaddr.ss_family;
-		if (f == a.m_sockaddr.ss_family) {
+		const uint8_t f = as.ss.ss_family;
+		if (f == a.as.ss.ss_family) {
 			if (f == AF_INET)
-				return (reinterpret_cast<const sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const sockaddr_in *>(&a)->sin_addr.s_addr);
+				return as.sa_in.sin_addr.s_addr == a.as.sa_in.sin_addr.s_addr;
 			if (f == AF_INET6)
-				return (memcmp(reinterpret_cast<const sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const sockaddr_in6 *>(&a)->sin6_addr.s6_addr,8) == 0);
+				return memcmp(as.sa_in6.sin6_addr.s6_addr,a.as.sa_in6.sin6_addr.s6_addr,8) == 0;
 			return (memcmp(this,&a,sizeof(InetAddress)) == 0);
 		}
 		return false;
@@ -391,25 +372,18 @@ public:
 
 	ZT_INLINE unsigned long hashCode() const noexcept
 	{
-		if (m_sockaddr.ss_family == AF_INET) {
-			return (unsigned long)Utils::hash32(((uint32_t)reinterpret_cast<const sockaddr_in *>(&m_sockaddr)->sin_addr.s_addr + (uint32_t)reinterpret_cast<const sockaddr_in *>(&m_sockaddr)->sin_port) ^ (uint32_t)Utils::s_mapNonce);
-		} else if (m_sockaddr.ss_family == AF_INET6) {
+		if (as.ss.ss_family == AF_INET) {
+			return (unsigned long)Utils::hash32(((uint32_t)as.sa_in.sin_addr.s_addr + (uint32_t)as.sa_in.sin_port) ^ (uint32_t)Utils::s_mapNonce);
+		} else if (as.ss.ss_family == AF_INET6) {
 			return (unsigned long)Utils::hash64(
-				(Utils::loadAsIsEndian<uint64_t>(reinterpret_cast<const sockaddr_in6 *>(&m_sockaddr)->sin6_addr.s6_addr) +
-				 Utils::loadAsIsEndian<uint64_t>(reinterpret_cast<const sockaddr_in6 *>(&m_sockaddr)->sin6_addr.s6_addr + 8) +
-				 (uint64_t)reinterpret_cast<const sockaddr_in6 *>(&m_sockaddr)->sin6_port) ^ Utils::s_mapNonce
-			);
+				(Utils::loadAsIsEndian<uint64_t>(as.sa_in6.sin6_addr.s6_addr) +
+				 Utils::loadAsIsEndian<uint64_t>(as.sa_in6.sin6_addr.s6_addr + 8) +
+				 (uint64_t)as.sa_in6.sin6_port) ^
+				Utils::s_mapNonce);
 		}
-		return Utils::fnv1a32(&m_sockaddr, sizeof(m_sockaddr));
+		return Utils::fnv1a32(this,sizeof(InetAddress));
 	}
 
-	/**
-	 * Fill out a ZT_TraceEventPathAddress from this InetAddress
-	 *
-	 * @param ta ZT_TraceEventPathAddress to fill
-	 */
-	void forTrace(ZT_TraceEventPathAddress &ta) const noexcept;
-
 	/**
 	 * Check whether this is a network/route rather than an IP assignment
 	 *
@@ -423,14 +397,44 @@ public:
 	/**
 	 * @return True if address family is non-zero
 	 */
-	explicit ZT_INLINE operator bool() const noexcept { return (family() != 0); }
+	explicit ZT_INLINE operator bool() const noexcept { return (as.ss.ss_family != 0); }
 
 	static constexpr int marshalSizeMax() noexcept { return ZT_INETADDRESS_MARSHAL_SIZE_MAX; }
 	int marshal(uint8_t data[ZT_INETADDRESS_MARSHAL_SIZE_MAX]) const noexcept;
 	int unmarshal(const uint8_t *restrict data,int len) noexcept;
 
-	bool operator==(const InetAddress &a) const noexcept;
-	bool operator<(const InetAddress &a) const noexcept;
+	ZT_INLINE bool operator==(const InetAddress &a) const noexcept
+	{
+		if (as.ss.ss_family == a.as.ss.ss_family) {
+			if (as.ss.ss_family == AF_INET)
+				return ((as.sa_in.sin_port == a.as.sa_in.sin_port)&&(as.sa_in.sin_addr.s_addr == a.as.sa_in.sin_addr.s_addr));
+			if (as.ss.ss_family == AF_INET6)
+				return ((as.sa_in6.sin6_port == a.as.sa_in6.sin6_port)&&(memcmp(as.sa_in6.sin6_addr.s6_addr,a.as.sa_in6.sin6_addr.s6_addr,16) == 0));
+			return memcmp(this,&a,sizeof(InetAddress)) == 0;
+		}
+		return false;
+	}
+	ZT_INLINE bool operator<(const InetAddress &a) const noexcept
+	{
+		if (as.ss.ss_family == a.as.ss.ss_family) {
+			if (as.ss.ss_family == AF_INET) {
+				const uint16_t p0 = Utils::ntoh((uint16_t)as.sa_in.sin_port);
+				const uint16_t p1 = Utils::ntoh((uint16_t)a.as.sa_in.sin_port);
+				if (p0 == p1)
+					return Utils::ntoh((uint32_t)as.sa_in.sin_addr.s_addr) < Utils::ntoh((uint32_t)a.as.sa_in.sin_addr.s_addr);
+				return p0 < p1;
+			}
+			if (as.ss.ss_family == AF_INET6) {
+				const uint16_t p0 = Utils::ntoh((uint16_t)as.sa_in6.sin6_port);
+				const uint16_t p1 = Utils::ntoh((uint16_t)a.as.sa_in6.sin6_port);
+				if (p0 == p1)
+					return memcmp(as.sa_in6.sin6_addr.s6_addr,a.as.sa_in6.sin6_addr.s6_addr,16) < 0;
+				return p0 < p1;
+			}
+			return memcmp(this,&a,sizeof(InetAddress)) < 0;
+		}
+		return as.ss.ss_family < a.as.ss.ss_family;
+	}
 	ZT_INLINE bool operator!=(const InetAddress &a) const noexcept { return !(*this == a); }
 	ZT_INLINE bool operator>(const InetAddress &a) const noexcept { return (a < *this); }
 	ZT_INLINE bool operator<=(const InetAddress &a) const noexcept { return !(a < *this); }
@@ -492,15 +496,17 @@ public:
 	 */
 	static InetAddress makeIpv66plane(uint64_t nwid,uint64_t zeroTierAddress) noexcept;
 
-private:
-	sockaddr_storage m_sockaddr;
+	/**
+	 * Union allowing this to be accessed as a sockaddr of any supported type.
+	 */
+	union {
+		sockaddr_storage ss;
+		sockaddr sa;
+		sockaddr_in sa_in;
+		sockaddr_in6 sa_in6;
+	} as;
 };
 
-static_assert(sizeof(sockaddr_storage) == sizeof(InetAddress),"InetAddress sizing incorrect");
-static_assert(sizeof(sockaddr_in) <= sizeof(InetAddress),"InetAddress sizing incorrect");
-static_assert(sizeof(sockaddr_in6) <= sizeof(InetAddress),"InetAddress sizing incorrect");
-static_assert(sizeof(sockaddr) <= sizeof(InetAddress),"InetAddress sizing incorrect");
-
 static ZT_INLINE InetAddress *asInetAddress(sockaddr_in *const p) noexcept { return reinterpret_cast<InetAddress *>(p); }
 static ZT_INLINE InetAddress *asInetAddress(sockaddr_in6 *const p) noexcept { return reinterpret_cast<InetAddress *>(p); }
 static ZT_INLINE InetAddress *asInetAddress(sockaddr *const p) noexcept { return reinterpret_cast<InetAddress *>(p); }

+ 2 - 2
node/Path.hpp

@@ -145,8 +145,8 @@ private:
 	// These fields belong to Defragmenter but are kept in Path for performance
 	// as it's much faster this way than having Defragmenter maintain another
 	// mapping from paths to inbound message IDs.
-	Set<uint64_t> _inboundFragmentedMessages;
-	Mutex _inboundFragmentedMessages_l;
+	Set<uint64_t> m_inboundFragmentedMessages;
+	Mutex m_inboundFragmentedMessages_l;
 
 	std::atomic<int> __refCount;
 };

+ 8 - 18
node/Tests.cpp

@@ -186,18 +186,6 @@ static const C25519TestVector C25519_TEST_VECTORS[ZT_NUM_C25519_TEST_VECTORS] =
 #define ZT_ENDIAN_S "big"
 #endif
 
-// This is basically a unit test for compiler packed structure behavior.
-ZT_PACKED_STRUCT(struct StructPackingTestSample {
-	uint8_t foo;
-	uint16_t bar;
-	uint32_t baz;
-	uint8_t lala;
-	uint64_t woop;
-	uint8_t haha;
-	uint64_t hoho[16];
-	uint8_t lol;
-});
-
 // Increments and decrements a counter based on object create/destroy
 class LifeCycleTracker
 {
@@ -264,12 +252,14 @@ extern "C" const char *ZTT_general()
 
 		{
 			ZT_T_PRINTF("[general] Checking structure sizes, alignment, and packing... ");
-			static_assert(sizeof(StructPackingTestSample) == 146,"StructPackingTestSample packed incorrectly");
-			StructPackingTestSample packtest; // NOLINT(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
-			if (((uintptr_t)&(packtest.woop) - (uintptr_t)&packtest) != 8)
-				return "structure packing check failed: incorrect index of test field in example packed struct (compiler or environment problem)";
-			if (((uintptr_t)&(packtest.hoho[5]) - (uintptr_t)&packtest) != 57)
-				return "structure packing check failed: incorrect index of test array indexed field in example packed struct (compiler or environment problem)";
+			if ((uintptr_t)&(InetAddress::LO4.as.sa_in) != (uintptr_t)&(InetAddress::LO4.as.sa_in6)) {
+				ZT_T_PRINTF("FAILED (&sa_in != &sa_in6)" ZT_EOL_S);
+				return "&sa_in != &sa_in6";
+			}
+			if ((uintptr_t)&(InetAddress::LO4.as.sa_in6.sin6_family) != (uintptr_t)&(InetAddress::LO4.as.ss.ss_family)) {
+				ZT_T_PRINTF("FAILED (&sa_in6.sin6_family != &ss.ss_family)" ZT_EOL_S);
+				return "&sa_in6.sin6_family != &ss.ss_family";
+			}
 			ZT_T_PRINTF("OK" ZT_EOL_S);
 		}
 

+ 3 - 30
node/TriviallyCopyable.hpp

@@ -24,8 +24,9 @@ namespace ZeroTier {
  *
  * It also includes some static methods to do this conveniently.
  */
-ZT_PACKED_STRUCT(struct TriviallyCopyable
+struct TriviallyCopyable
 {
+public:
 	/**
 	 * Zero a TriviallyCopyable object
 	 *
@@ -52,34 +53,6 @@ ZT_PACKED_STRUCT(struct TriviallyCopyable
 		Utils::zero<sizeof(T)>(&obj);
 	}
 
-	/**
-	 * Copy any memory over a TriviallyCopyable object
-	 *
-	 * @tparam T Automatically inferred type of destination
-	 * @param dest Any TriviallyCopyable object
-	 * @param src Source memory of same size or less than sizeof(dest)
-	 */
-	template<typename T>
-	static ZT_INLINE void memoryCopyUnsafe(T *dest,const void *src) noexcept
-	{
-		static_assert(isTriviallyCopyable(dest),"parameter is not TriviallyCopyable");
-		Utils::copy<sizeof(T)>(dest,src);
-	}
-
-	/**
-	 * Copy any memory over a TriviallyCopyable object
-	 *
-	 * @tparam T Automatically inferred type of destination
-	 * @param dest Any TriviallyCopyable object
-	 * @param src Source memory of same size or less than sizeof(dest)
-	 */
-	template<typename T>
-	static ZT_INLINE void memoryCopyUnsafe(T &dest,const void *src) noexcept
-	{
-		static_assert(isTriviallyCopyable(dest),"parameter is not TriviallyCopyable");
-		Utils::copy<sizeof(T)>(&dest,src);
-	}
-
 	/**
 	 * Copy a TriviallyCopyable object
 	 *
@@ -135,7 +108,7 @@ ZT_PACKED_STRUCT(struct TriviallyCopyable
 		static_assert(isTriviallyCopyable(dest),"parameter is not TriviallyCopyable");
 		Utils::copy<sizeof(T)>(&dest,&src);
 	}
-});
+};
 
 static constexpr bool isTriviallyCopyable(const TriviallyCopyable *) noexcept { return true; }
 static constexpr bool isTriviallyCopyable(const void *) noexcept { return false; }