summaryrefslogtreecommitdiff
path: root/node/InetAddress.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'node/InetAddress.hpp')
-rw-r--r--node/InetAddress.hpp229
1 files changed, 177 insertions, 52 deletions
diff --git a/node/InetAddress.hpp b/node/InetAddress.hpp
index e3537ce0..2573e694 100644
--- a/node/InetAddress.hpp
+++ b/node/InetAddress.hpp
@@ -38,16 +38,22 @@
#include "../include/ZeroTierOne.h"
#include "Utils.hpp"
#include "MAC.hpp"
+#include "Buffer.hpp"
namespace ZeroTier {
/**
+ * Maximum integer value of enum IpScope
+ */
+#define ZT_INETADDRESS_MAX_SCOPE 7
+
+/**
* Extends sockaddr_storage with friendly C++ methods
*
* This is basically a "mixin" for sockaddr_storage. It adds methods and
* operators, but does not modify the structure. This can be cast to/from
- * sockaddr_storage and used interchangeably. Don't change this as it's
- * used in a few places.
+ * sockaddr_storage and used interchangeably. DO NOT change this by e.g.
+ * adding non-static fields, since much code depends on this identity.
*/
struct InetAddress : public sockaddr_storage
{
@@ -65,7 +71,8 @@ struct InetAddress : public sockaddr_storage
* IP address scope
*
* Note that these values are in ascending order of path preference and
- * MUST remain that way or Path must be changed to reflect.
+ * MUST remain that way or Path must be changed to reflect. Also be sure
+ * to change ZT_INETADDRESS_MAX_SCOPE if the max changes.
*/
enum IpScope
{
@@ -99,74 +106,88 @@ struct InetAddress : public sockaddr_storage
inline InetAddress &operator=(const InetAddress &a)
throw()
{
- memcpy(this,&a,sizeof(InetAddress));
+ if (&a != this)
+ memcpy(this,&a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const InetAddress *a)
throw()
{
- memcpy(this,a,sizeof(InetAddress));
+ if (a != this)
+ memcpy(this,a,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage &ss)
throw()
{
- memcpy(this,&ss,sizeof(InetAddress));
+ if (reinterpret_cast<const InetAddress *>(&ss) != this)
+ memcpy(this,&ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_storage *ss)
throw()
{
- memcpy(this,ss,sizeof(InetAddress));
+ if (reinterpret_cast<const InetAddress *>(ss) != this)
+ memcpy(this,ss,sizeof(InetAddress));
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in &sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in));
+ if (reinterpret_cast<const InetAddress *>(&sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ memcpy(this,&sa,sizeof(struct sockaddr_in));
+ }
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in *sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in));
+ if (reinterpret_cast<const InetAddress *>(sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ memcpy(this,sa,sizeof(struct sockaddr_in));
+ }
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 &sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ if (reinterpret_cast<const InetAddress *>(&sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ }
return *this;
}
inline InetAddress &operator=(const struct sockaddr_in6 *sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- memcpy(this,sa,sizeof(struct sockaddr_in6));
+ if (reinterpret_cast<const InetAddress *>(sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ memcpy(this,sa,sizeof(struct sockaddr_in6));
+ }
return *this;
}
inline InetAddress &operator=(const struct sockaddr &sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- switch(sa.sa_family) {
- case AF_INET:
- memcpy(this,&sa,sizeof(struct sockaddr_in));
- break;
- case AF_INET6:
- memcpy(this,&sa,sizeof(struct sockaddr_in6));
- break;
+ if (reinterpret_cast<const InetAddress *>(&sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ switch(sa.sa_family) {
+ case AF_INET:
+ memcpy(this,&sa,sizeof(struct sockaddr_in));
+ break;
+ case AF_INET6:
+ memcpy(this,&sa,sizeof(struct sockaddr_in6));
+ break;
+ }
}
return *this;
}
@@ -174,14 +195,16 @@ struct InetAddress : public sockaddr_storage
inline InetAddress &operator=(const struct sockaddr *sa)
throw()
{
- memset(this,0,sizeof(InetAddress));
- switch(sa->sa_family) {
- case AF_INET:
- memcpy(this,sa,sizeof(struct sockaddr_in));
- break;
- case AF_INET6:
- memcpy(this,sa,sizeof(struct sockaddr_in6));
- break;
+ if (reinterpret_cast<const InetAddress *>(sa) != this) {
+ memset(this,0,sizeof(InetAddress));
+ switch(sa->sa_family) {
+ case AF_INET:
+ memcpy(this,sa,sizeof(struct sockaddr_in));
+ break;
+ case AF_INET6:
+ memcpy(this,sa,sizeof(struct sockaddr_in6));
+ break;
+ }
}
return *this;
}
@@ -280,17 +303,27 @@ struct InetAddress : public sockaddr_storage
/**
* Construct a full netmask as an InetAddress
+ *
+ * @return Netmask such as 255.255.255.0 if this address is /24 (port field will be unchanged)
*/
- InetAddress netmask() const
- throw();
+ InetAddress netmask() const;
/**
* Constructs a broadcast address from a network/netmask address
*
+ * This is only valid for IPv4 and will return a NULL InetAddress for other
+ * address families.
+ *
* @return Broadcast address (only IP portion is meaningful)
*/
- InetAddress broadcast() const
- throw();
+ InetAddress broadcast() const;
+
+ /**
+ * Return the network -- a.k.a. the IP ANDed with the netmask
+ *
+ * @return Network e.g. 10.0.1.0/24 from 10.0.1.200/24
+ */
+ InetAddress network() const;
/**
* @return True if this is an IPv4 address
@@ -303,7 +336,7 @@ struct InetAddress : public sockaddr_storage
inline bool isV6() const throw() { return (ss_family == AF_INET6); }
/**
- * @return pointer to raw IP address bytes
+ * @return pointer to raw address bytes or NULL if not available
*/
inline const void *rawIpData() const
throw()
@@ -316,27 +349,19 @@ struct InetAddress : public sockaddr_storage
}
/**
- * @return pointer to raw IP address bytes
- */
- inline void *rawIpData()
- throw()
- {
- switch(ss_family) {
- case AF_INET: return (void *)&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr);
- case AF_INET6: return (void *)(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr);
- default: return 0;
- }
- }
-
- /**
+ * Performs an IP-only comparison or, if that is impossible, a memcmp()
+ *
* @param a InetAddress to compare again
* @return True if only IP portions are equal (false for non-IP or null addresses)
*/
inline bool ipsEqual(const InetAddress &a) const
{
- switch(ss_family) {
- case AF_INET: return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
- case AF_INET6: return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
+ if (ss_family == a.ss_family) {
+ if (ss_family == AF_INET)
+ return (reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr == reinterpret_cast<const struct sockaddr_in *>(&a)->sin_addr.s_addr);
+ if (ss_family == AF_INET6)
+ return (memcmp(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,reinterpret_cast<const struct sockaddr_in6 *>(&a)->sin6_addr.s6_addr,16) == 0);
+ return (memcmp(this,&a,sizeof(InetAddress)) == 0);
}
return false;
}
@@ -362,6 +387,62 @@ struct InetAddress : public sockaddr_storage
*/
inline operator bool() const throw() { return (ss_family != 0); }
+ template<unsigned int C>
+ inline void serialize(Buffer<C> &b) const
+ {
+ // This is used in the protocol and must be the same as describe in places
+ // like VERB_HELLO in Packet.hpp.
+ switch(ss_family) {
+ case AF_INET:
+ b.append((uint8_t)0x04);
+ b.append(&(reinterpret_cast<const struct sockaddr_in *>(this)->sin_addr.s_addr),4);
+ b.append((uint16_t)port()); // just in case sin_port != uint16_t
+ return;
+ case AF_INET6:
+ b.append((uint8_t)0x06);
+ b.append(reinterpret_cast<const struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,16);
+ b.append((uint16_t)port()); // just in case sin_port != uint16_t
+ return;
+ default:
+ b.append((uint8_t)0);
+ return;
+ }
+ }
+
+ template<unsigned int C>
+ inline unsigned int deserialize(const Buffer<C> &b,unsigned int startAt = 0)
+ {
+ memset(this,0,sizeof(InetAddress));
+ unsigned int p = startAt;
+ switch(b[p++]) {
+ case 0:
+ return 1;
+ case 0x01:
+ // TODO: Ethernet address (but accept for forward compatibility)
+ return 7;
+ case 0x02:
+ // TODO: Bluetooth address (but accept for forward compatibility)
+ return 7;
+ case 0x03:
+ // TODO: Other address types (but accept for forward compatibility)
+ // These could be extended/optional things like AF_UNIX, LTE Direct, shared memory, etc.
+ return (unsigned int)(b.template at<uint16_t>(p) + 3); // other addresses begin with 16-bit non-inclusive length
+ case 0x04:
+ ss_family = AF_INET;
+ memcpy(&(reinterpret_cast<struct sockaddr_in *>(this)->sin_addr.s_addr),b.field(p,4),4); p += 4;
+ reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
+ break;
+ case 0x06:
+ ss_family = AF_INET6;
+ memcpy(reinterpret_cast<struct sockaddr_in6 *>(this)->sin6_addr.s6_addr,b.field(p,16),16); p += 16;
+ reinterpret_cast<struct sockaddr_in *>(this)->sin_port = Utils::hton(b.template at<uint16_t>(p)); p += 2;
+ break;
+ default:
+ throw std::invalid_argument("invalid serialized InetAddress");
+ }
+ return (p - startAt);
+ }
+
bool operator==(const InetAddress &a) const throw();
bool operator<(const InetAddress &a) const throw();
inline bool operator!=(const InetAddress &a) const throw() { return !(*this == a); }
@@ -375,6 +456,50 @@ struct InetAddress : public sockaddr_storage
*/
static InetAddress makeIpv6LinkLocal(const MAC &mac)
throw();
+
+ /**
+ * Compute private IPv6 unicast address from network ID and ZeroTier address
+ *
+ * This generates a private unicast IPv6 address that is mostly compliant
+ * with the letter of RFC4193 and certainly compliant in spirit.
+ *
+ * RFC4193 specifies a format of:
+ *
+ * | 7 bits |1| 40 bits | 16 bits | 64 bits |
+ * | Prefix |L| Global ID | Subnet ID | Interface ID |
+ *
+ * The 'L' bit is set to 1, yielding an address beginning with 0xfd. Then
+ * the network ID is filled into the global ID, subnet ID, and first byte
+ * of the "interface ID" field. Since the first 40 bits of the network ID
+ * is the unique ZeroTier address of its controller, this makes a very
+ * good random global ID. Since network IDs have 24 more bits, we let it
+ * overflow into the interface ID.
+ *
+ * After that we pad with two bytes: 0x99, 0x93, namely the default ZeroTier
+ * port in hex.
+ *
+ * Finally we fill the remaining 40 bits of the interface ID field with
+ * the 40-bit unique ZeroTier device ID of the network member.
+ *
+ * This yields a valid RFC4193 address with a random global ID, a
+ * meaningful subnet ID, and a unique interface ID, all mappable back onto
+ * ZeroTier space.
+ *
+ * This in turn could allow us, on networks numbered this way, to emulate
+ * IPv6 NDP and eliminate all multicast. This could be beneficial for
+ * small devices and huge networks, e.g. IoT applications.
+ *
+ * The returned address is given an odd prefix length of /88, since within
+ * a given network only the last 40 bits (device ID) are variable. This
+ * is a bit unusual but as far as we know should not cause any problems with
+ * any non-braindead IPv6 stack.
+ *
+ * @param nwid 64-bit network ID
+ * @param zeroTierAddress 40-bit device address (in least significant 40 bits, highest 24 bits ignored)
+ * @return IPv6 private unicast address with /88 netmask
+ */
+ static InetAddress makeIpv6rfc4193(uint64_t nwid,uint64_t zeroTierAddress)
+ throw();
};
} // namespace ZeroTier