diff --git a/controller/EmbeddedNetworkController.cpp b/controller/EmbeddedNetworkController.cpp
index 0b6c44055..f5c48228d 100644
--- a/controller/EmbeddedNetworkController.cpp
+++ b/controller/EmbeddedNetworkController.cpp
@@ -1310,20 +1310,6 @@ void EmbeddedNetworkController::_request(
nc->mtu = std::max(std::min((unsigned int)OSUtils::jsonInt(network["mtu"],ZT_DEFAULT_MTU),(unsigned int)ZT_MAX_MTU),(unsigned int)ZT_MIN_MTU);
nc->multicastLimit = (unsigned int)OSUtils::jsonInt(network["multicastLimit"],32ULL);
- std::string rtt(OSUtils::jsonString(member["remoteTraceTarget"],""));
- if (rtt.length() == 10) {
- nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
- nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(member["remoteTraceLevel"],0ULL);
- } else {
- rtt = OSUtils::jsonString(network["remoteTraceTarget"],"");
- if (rtt.length() == 10) {
- nc->remoteTraceTarget = Address(Utils::hexStrToU64(rtt.c_str()));
- } else {
- nc->remoteTraceTarget.zero();
- }
- nc->remoteTraceLevel = (Trace::Level)OSUtils::jsonInt(network["remoteTraceLevel"],0ULL);
- }
-
for(std::vector
::const_iterator ab(ns.activeBridges.begin());ab!=ns.activeBridges.end();++ab)
nc->addSpecialist(*ab,ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE);
diff --git a/node/Address.hpp b/node/Address.hpp
index 8a2936e69..f5b2c9363 100644
--- a/node/Address.hpp
+++ b/node/Address.hpp
@@ -105,7 +105,7 @@ public:
/**
* @return Hash code for use with Hashtable
*/
- ZT_ALWAYS_INLINE unsigned long hashCode() const { return (unsigned long)_a; }
+ ZT_ALWAYS_INLINE unsigned long hashCode() const { return reinterpret_cast(_a); }
/**
* @return Hexadecimal string
@@ -134,6 +134,10 @@ public:
*/
ZT_ALWAYS_INLINE uint8_t operator[](unsigned int i) const { return (uint8_t)(_a >> (32 - (i * 8))); }
+ ZT_ALWAYS_INLINE operator unsigned int() const { return reinterpret_cast(_a); }
+ ZT_ALWAYS_INLINE operator unsigned long() const { return reinterpret_cast(_a); }
+ ZT_ALWAYS_INLINE operator unsigned long long() const { return reinterpret_cast(_a); }
+
ZT_ALWAYS_INLINE void zero() { _a = 0; }
ZT_ALWAYS_INLINE bool operator==(const uint64_t &a) const { return (_a == (a & 0xffffffffffULL)); }
diff --git a/node/Buffer.hpp b/node/Buffer.hpp
index 32c5a0a61..543f5168b 100644
--- a/node/Buffer.hpp
+++ b/node/Buffer.hpp
@@ -237,7 +237,7 @@ public:
* @param n Number of times to append
* @throws std::out_of_range Attempt to append beyond capacity
*/
- inline void append(unsigned char c,unsigned int n)
+ inline void append(uint8_t c,unsigned int n)
{
if (unlikely((_l + n) > C))
throw ZT_EXCEPTION_OUT_OF_BOUNDS;
diff --git a/node/CMakeLists.txt b/node/CMakeLists.txt
index 106f84f50..9dac4bc1b 100644
--- a/node/CMakeLists.txt
+++ b/node/CMakeLists.txt
@@ -24,7 +24,6 @@ set(core_headers
Locator.hpp
MAC.hpp
Membership.hpp
- Multicaster.hpp
MulticastGroup.hpp
Mutex.hpp
Network.hpp
@@ -59,7 +58,6 @@ set(core_src
IncomingPacket.cpp
InetAddress.cpp
Membership.cpp
- Multicaster.cpp
Network.cpp
NetworkConfig.cpp
Node.cpp
diff --git a/node/Constants.hpp b/node/Constants.hpp
index 99b10f456..0f5470448 100644
--- a/node/Constants.hpp
+++ b/node/Constants.hpp
@@ -14,6 +14,10 @@
#ifndef ZT_CONSTANTS_HPP
#define ZT_CONSTANTS_HPP
+/****************************************************************************/
+/* Core includes and OS/platform setup stuff */
+/****************************************************************************/
+
#include "../include/ZeroTierCore.h"
#if __has_include("version.h")
@@ -174,6 +178,10 @@
#define ZT_INVALID_SOCKET -1
#endif
+/****************************************************************************/
+/* Internal ZeroTier constants */
+/****************************************************************************/
+
/**
* Length of a ZeroTier address in bytes
*/
@@ -288,6 +296,11 @@
*/
#define ZT_MULTICAST_GATHER_PERIOD ZT_MULTICAST_ANNOUNCE_PERIOD
+/**
+ * Period for multicast GATHER if there are no known recipients
+ */
+#define ZT_MULTICAST_GATHER_PERIOD_WHEN_NO_RECIPIENTS 2500
+
/**
* Timeout for outgoing multicasts
*
diff --git a/node/Identity.hpp b/node/Identity.hpp
index 9b5f7eb72..98a6becd8 100644
--- a/node/Identity.hpp
+++ b/node/Identity.hpp
@@ -115,26 +115,19 @@ public:
}
/**
- * Compute a 128-bit short hash of this identity's public key
- *
- * This is the first 128 bits of a SHA384 hash and is the hash used
- * in VERB_WILL_RELAY to report reachability.
- *
- * @param h 128-bit buffer to receive hash (must be 16 bytes in size)
+ * @param h Buffer to receive SHA384 of public key(s)
*/
- ZT_ALWAYS_INLINE void publicKeyHash128(void *const h) const
+ ZT_ALWAYS_INLINE bool hash(uint8_t h[48]) const
{
- uint8_t tmp[48];
switch(_type) {
case C25519:
- SHA384(tmp,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
- break;
+ SHA384(h,_pub.c25519,ZT_C25519_PUBLIC_KEY_LEN);
+ return true;
case P384:
- SHA384(tmp,&_pub,sizeof(_pub));
- break;
+ SHA384(h,&_pub,sizeof(_pub));
+ return true;
}
- for(int i=0;i<16;++i)
- ((uint8_t *)h)[i] = tmp[i];
+ return false;
}
/**
diff --git a/node/MulticastGroup.hpp b/node/MulticastGroup.hpp
index c2c130541..7d9579131 100644
--- a/node/MulticastGroup.hpp
+++ b/node/MulticastGroup.hpp
@@ -41,13 +41,8 @@ namespace ZeroTier {
class MulticastGroup
{
public:
- ZT_ALWAYS_INLINE MulticastGroup() :
- _mac(),
- _adi(0) {}
-
- ZT_ALWAYS_INLINE MulticastGroup(const MAC &m,uint32_t a) :
- _mac(m),
- _adi(a) {}
+ ZT_ALWAYS_INLINE MulticastGroup() : _mac(),_adi(0) {}
+ ZT_ALWAYS_INLINE MulticastGroup(const MAC &m,uint32_t a) : _mac(m),_adi(a) {}
/**
* Derive the multicast group used for address resolution (ARP/NDP) for an IP
@@ -74,10 +69,36 @@ public:
return MulticastGroup();
}
+ /**
+ * @return Ethernet MAC portion of multicast group
+ */
ZT_ALWAYS_INLINE const MAC &mac() const { return _mac; }
+
+ /**
+ * @return Additional distinguishing information, which is normally zero except for IPv4 ARP where it's the IPv4 address
+ */
ZT_ALWAYS_INLINE uint32_t adi() const { return _adi; }
- ZT_ALWAYS_INLINE unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
+ /**
+ * @return 32-bit hash ID of this multicast group
+ */
+ ZT_ALWAYS_INLINE uint32_t id() const
+ {
+ uint64_t m = _mac.toInt();
+ uint32_t x1 = _adi;
+ uint32_t x2 = (uint32_t)(m >> 32);
+ uint32_t x3 = (uint32_t)m;
+ x1 = ((x1 >> 16) ^ x1) * 0x45d9f3b;
+ x2 = ((x2 >> 16) ^ x2) * 0x45d9f3b;
+ x3 = ((x3 >> 16) ^ x3) * 0x45d9f3b;
+ x1 = ((x1 >> 16) ^ x1) * 0x45d9f3b;
+ x2 = ((x2 >> 16) ^ x2) * 0x45d9f3b;
+ x3 = ((x3 >> 16) ^ x3) * 0x45d9f3b;
+ x1 = (x1 >> 16) ^ x1;
+ x2 = (x2 >> 16) ^ x2;
+ x3 = (x3 >> 16) ^ x3;
+ return (x1 ^ x2 ^ x3);
+ }
ZT_ALWAYS_INLINE bool operator==(const MulticastGroup &g) const { return ((_mac == g._mac)&&(_adi == g._adi)); }
ZT_ALWAYS_INLINE bool operator!=(const MulticastGroup &g) const { return ((_mac != g._mac)||(_adi != g._adi)); }
@@ -93,6 +114,8 @@ public:
ZT_ALWAYS_INLINE bool operator<=(const MulticastGroup &g) const { return !(g < *this); }
ZT_ALWAYS_INLINE bool operator>=(const MulticastGroup &g) const { return !(*this < g); }
+ ZT_ALWAYS_INLINE unsigned long hashCode() const { return (_mac.hashCode() ^ (unsigned long)_adi); }
+
private:
MAC _mac;
uint32_t _adi;
diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp
deleted file mode 100644
index e5d09cf58..000000000
--- a/node/Multicaster.cpp
+++ /dev/null
@@ -1,326 +0,0 @@
-/*
- * Copyright (c)2019 ZeroTier, Inc.
- *
- * Use of this software is governed by the Business Source License included
- * in the LICENSE.TXT file in the project's root directory.
- *
- * Change Date: 2023-01-01
- *
- * On the date above, in accordance with the Business Source License, use
- * of this software will be governed by version 2.0 of the Apache License.
- */
-/****/
-
-#include
-
-#include "Constants.hpp"
-#include "RuntimeEnvironment.hpp"
-#include "Multicaster.hpp"
-#include "Network.hpp"
-#include "Membership.hpp"
-#include "Topology.hpp"
-#include "Switch.hpp"
-
-namespace ZeroTier {
-
-Multicaster::Multicaster(const RuntimeEnvironment *renv) :
- RR(renv),
- _groups(32) {}
-
-Multicaster::~Multicaster() {}
-
-unsigned int Multicaster::send(
- void *tPtr,
- int64_t now,
- const SharedPtr &network,
- const MulticastGroup &mg,
- const MAC &src,
- unsigned int etherType,
- const unsigned int existingBloomMultiplier,
- const uint8_t existingBloom[ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS / 8],
- const void *const data,
- unsigned int len)
-{
- static const unsigned int PRIMES[16] = { 3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59 }; // 2 is skipped as it's even
-
- std::vector< std::pair > recipients;
-
- const NetworkConfig &config = network->config();
- if (config.multicastLimit == 0) return 0; // multicast disabled
-
- Address specialists[ZT_MAX_NETWORK_SPECIALISTS],multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS];
- unsigned int specialistCount = 0,multicastReplicatorCount = 0,bridgeCount = 0;
- bool amMulticastReplicator = false;
- for(unsigned int i=0;iidentity.address() == config.specialists[i]) {
- amMulticastReplicator |= ((config.specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0);
- } else {
- specialists[specialistCount++] = config.specialists[i];
- if ((config.specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) {
- recipients.push_back(std::pair(0,config.specialists[i]));
- ++bridgeCount;
- } if ((config.specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_MULTICAST_REPLICATOR) != 0) {
- multicastReplicators[multicastReplicatorCount++] = config.specialists[i];
- }
- }
- }
- std::sort(&(specialists[0]),&(specialists[specialistCount])); // for binary search
-
- int64_t lastGather = 0;
- _K groupKey(network->id(),mg);
- {
- Mutex::Lock l(_groups_l);
- const _G *const g = _groups.get(groupKey);
- if (g) {
- lastGather = g->lastGather;
- recipients.reserve(recipients.size() + g->members.size());
- Hashtable< Address,int64_t >::Iterator mi(const_cast<_G *>(g)->members);
- Address *mik = nullptr;
- int64_t *miv = nullptr;
- while (mi.next(mik,miv)) {
- if (!std::binary_search(&(specialists[0]),&(specialists[specialistCount]),*mik))
- recipients.push_back(std::pair(*miv,*mik));
- }
- }
- }
-
- // Sort recipients, maintaining bridges first in list
- std::sort(recipients.begin() + bridgeCount,recipients.end(),std::greater< std::pair >());
-
- // Gather new recipients periodically, being more aggressive if we have none.
- if ((now - lastGather) > (recipients.empty() ? 5000 : ZT_MULTICAST_GATHER_PERIOD)) {
- {
- Mutex::Lock l(_groups_l);
- _groups[groupKey].lastGather = now;
- }
-
- Packet outp(network->controller(),RR->identity.address(),Packet::VERB_MULTICAST_GATHER);
- outp.append(network->id());
- outp.append((uint8_t)0);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint32_t)0xffffffff);
- RR->sw->send(tPtr,outp,true);
-
- for(unsigned int i=0;isw->send(tPtr,outp,true);
- }
-
- // LEGACY: roots may know about older versions' multicast subscriptions but
- // the root's role here is being phased out.
- SharedPtr root(RR->topology->root(now));
- if (root) {
- outp.newInitializationVector();
- outp.setDestination(root->address());
- outp.armor(root->key(),true);
- root->sendDirect(tPtr,outp.data(),outp.size(),now,true);
- }
- }
-
- if (recipients.empty())
- return 0;
-
- unsigned int sentCount = 0;
-
- uint64_t bloomFilter[ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS / 64];
- unsigned int bloomMultiplier;
- if (existingBloom) {
- memcpy(bloomFilter,existingBloom,sizeof(bloomFilter));
- bloomMultiplier = existingBloomMultiplier;
- } else {
- memset(bloomFilter,0,sizeof(bloomFilter));
- bloomMultiplier = 1;
-
- // Iteratively search for a bloom multiplier that results in no collisions
- // among known recipients. Usually the first iteration is good unless
- // the recipient set is quite large.
- if (recipients.size() > 1) {
- unsigned long bestMultColl = 0xffffffff;
- for(int k=0;k<16;++k) { // 16 == arbitrary limit on iterations for this search, also must be <= size of PRIMES
- const unsigned int mult = PRIMES[k];
- unsigned long coll = 0;
- for(std::vector< std::pair >::const_iterator r(recipients.begin());r!=recipients.end();++r) {
- const unsigned int bfi = mult * (unsigned int)r->second.toInt();
- const unsigned int byte = (bfi >> 3) % sizeof(bloomFilter);
- const uint8_t bit = 1 << (bfi & 7);
- coll += ((((uint8_t *)bloomFilter)[byte] & bit) != 0);
- ((uint8_t *)bloomFilter)[byte] |= bit;
- }
- memset(bloomFilter,0,sizeof(bloomFilter));
-
- if (coll <= bestMultColl) {
- bloomMultiplier = mult;
- if (coll == 0) // perfect score, no need to continue searching
- break;
- bestMultColl = coll;
- }
- }
- }
- }
-
- // See if there is a multicast replicator, trying to pick the fastest/best one.
- Address bestReplicator;
- if (multicastReplicatorCount > 0) {
- unsigned int bestReplicatorLatency = 0xffff;
- for(unsigned int i=0;i> 3) % sizeof(bloomFilter)] & (1 << (bfi & 7))) == 0) {
- SharedPtr peer(RR->topology->get(multicastReplicators[i]));
- if (peer) {
- const unsigned int lat = peer->latency(now);
- if (lat <= bestReplicatorLatency) {
- bestReplicator = peer->address();
- bestReplicatorLatency = lat;
- }
- } else if (!bestReplicator) {
- bestReplicator = multicastReplicators[i];
- }
- }
- }
- }
-
- // If this is a multicast replicator, aggressively replicate. Multicast
- // replicators are not subject to send count limits.
- if (amMulticastReplicator) {
- std::vector< std::pair< int,Address > > byLatency;
- for(std::vector< std::pair >::const_iterator r(recipients.begin());r!=recipients.end();++r) {
- const unsigned int bfi = bloomMultiplier * (unsigned int)r->second.toInt();
- if ((((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] & (1 << (bfi & 7))) == 0) {
- SharedPtr peer(RR->topology->get(r->second));
- byLatency.push_back(std::pair< int,Address >((peer) ? (int)peer->latency(now) : 0xffff,r->second));
- }
- }
- std::sort(byLatency.begin(),byLatency.end());
-
- unsigned long cnt = byLatency.size();
- if (bestReplicator)
- cnt /= 2; // send to only the best half of the latency-sorted population if there are more replicators
- for(unsigned long i=0;i> 3) % sizeof(bloomFilter)] |= 1 << (bfi & 7);
-
- Packet outp(byLatency[i].second,RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
- outp.append(network->id());
- outp.append((uint8_t)0x04);
- src.appendTo(outp);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- RR->sw->send(tPtr,outp,true);
-
- ++sentCount;
- }
- }
-
- // Forward to the next multicast replicator, if any.
- if (bestReplicator) {
- const unsigned int bfi = bloomMultiplier * (unsigned int)bestReplicator.toInt();
- ((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] |= 1 << (bfi & 7);
-
- Packet outp(bestReplicator,RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
- outp.append((uint8_t)(0x04 | 0x08));
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)bloomMultiplier);
- outp.append((uint16_t)sizeof(bloomFilter));
- outp.append(((uint8_t *)bloomFilter),sizeof(bloomFilter));
- src.appendTo(outp);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- RR->sw->send(tPtr,outp,true);
-
- ++sentCount;
- }
-
- // If this is a multicast replicator, we've already replicated.
- if (amMulticastReplicator)
- return (unsigned int)recipients.size();
-
- // Find the two best next hops (that have never seen this multicast)
- // that are newer version nodes.
- SharedPtr nextHops[2];
- unsigned int nextHopsBestLatency[2] = { 0xffff,0xffff };
- for(std::vector< std::pair >::iterator r(recipients.begin());r!=recipients.end();++r) {
- if (r->first >= 0) {
- const unsigned int bfi = bloomMultiplier * (unsigned int)r->second.toInt();
- if ((((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] & (1 << (bfi & 7))) == 0) {
- const SharedPtr peer(RR->topology->get(r->second));
- if ((peer)&&(peer->remoteVersionProtocol() >= 11)) {
- r->first = -1; // use this field now to flag as non-legacy
- const unsigned int lat = peer->latency(now);
- for(unsigned int nh=0;nh<2;++nh) {
- if (lat <= nextHopsBestLatency[nh]) {
- nextHopsBestLatency[nh] = lat;
- nextHops[nh] = peer;
- break;
- }
- }
- }
- }
- }
- }
-
- // Set bits for next hops in bloom filter
- for(unsigned int nh=0;nh<2;++nh) {
- if (nextHops[nh]) {
- const unsigned int bfi = bloomMultiplier * (unsigned int)nextHops[nh]->address().toInt();
- ((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] |= 1 << (bfi & 7);
- ++sentCount;
- }
- }
-
- // Send to legacy peers and flag these in bloom filter
- const unsigned int limit = config.multicastLimit + bridgeCount;
- for(std::vector< std::pair >::const_iterator r(recipients.begin());(r!=recipients.end())&&(sentCountfirst >= 0) {
- const unsigned int bfi = bloomMultiplier * (unsigned int)r->second.toInt();
- ((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] |= 1 << (bfi & 7);
-
- Packet outp(r->second,RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
- outp.append(network->id());
- outp.append((uint8_t)0x04);
- src.appendTo(outp);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- RR->sw->send(tPtr,outp,true);
-
- ++sentCount;
- }
- }
-
- // Send to next hops for P2P propagation
- for(unsigned int nh=0;nh<2;++nh) {
- if (nextHops[nh]) {
- Packet outp(nextHops[nh]->address(),RR->identity.address(),Packet::VERB_MULTICAST_FRAME);
- outp.append((uint8_t)(0x04 | 0x08));
- RR->identity.address().appendTo(outp);
- outp.append((uint16_t)bloomMultiplier);
- outp.append((uint16_t)sizeof(bloomFilter));
- outp.append(((uint8_t *)bloomFilter),sizeof(bloomFilter));
- src.appendTo(outp);
- mg.mac().appendTo(outp);
- outp.append(mg.adi());
- outp.append((uint16_t)etherType);
- outp.append(data,len);
- outp.compress();
- RR->sw->send(tPtr,outp,true);
- }
- }
-
- return (unsigned int)recipients.size();
-}
-
-void Multicaster::clean(int64_t now)
-{
-}
-
-} // namespace ZeroTier
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp
deleted file mode 100644
index 594608abb..000000000
--- a/node/Multicaster.hpp
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * Copyright (c)2019 ZeroTier, Inc.
- *
- * Use of this software is governed by the Business Source License included
- * in the LICENSE.TXT file in the project's root directory.
- *
- * Change Date: 2023-01-01
- *
- * On the date above, in accordance with the Business Source License, use
- * of this software will be governed by version 2.0 of the Apache License.
- */
-/****/
-
-#ifndef ZT_MULTICASTER_HPP
-#define ZT_MULTICASTER_HPP
-
-#include
-#include
-
-#include