From bccb86a401687ace3cea638511eeee1313c68ad5 Mon Sep 17 00:00:00 2001 From: Adam Ierymenko Date: Tue, 10 Sep 2019 16:20:28 -0700 Subject: [PATCH] More multicast work... --- node/Constants.hpp | 6 +- node/Multicaster.cpp | 331 +++++++++++++++++++++++++++++++------------ node/Multicaster.hpp | 142 +++++++++---------- node/Network.cpp | 43 +++++- node/Network.hpp | 103 ++++++-------- node/Packet.hpp | 2 +- node/Utils.hpp | 13 +- 7 files changed, 410 insertions(+), 230 deletions(-) diff --git a/node/Constants.hpp b/node/Constants.hpp index 0f4e9f662..29deb8ff5 100644 --- a/node/Constants.hpp +++ b/node/Constants.hpp @@ -258,12 +258,12 @@ /** * Period for multicast LIKE re-announcements to connected nodes */ -#define ZT_MULTICAST_ANNOUNCE_PERIOD 120000 +#define ZT_MULTICAST_ANNOUNCE_PERIOD 60000 /** - * Delay between explicit MULTICAST_GATHER requests for a given multicast channel + * Period for multicast GATHER on multicast groups */ -#define ZT_MULTICAST_EXPLICIT_GATHER_DELAY (ZT_MULTICAST_LIKE_EXPIRE / 10) +#define ZT_MULTICAST_GATHER_PERIOD ZT_MULTICAST_ANNOUNCE_PERIOD /** * Timeout for outgoing multicasts diff --git a/node/Multicaster.cpp b/node/Multicaster.cpp index 2b5766e86..21f98a3b1 100644 --- a/node/Multicaster.cpp +++ b/node/Multicaster.cpp @@ -17,6 +17,7 @@ #include "RuntimeEnvironment.hpp" #include "Multicaster.hpp" #include "Network.hpp" +#include "Membership.hpp" #include "Topology.hpp" #include "Switch.hpp" @@ -28,7 +29,7 @@ Multicaster::Multicaster(const RuntimeEnvironment *renv) : Multicaster::~Multicaster() {} -void Multicaster::send( +unsigned int Multicaster::send( void *tPtr, int64_t now, const SharedPtr &network, @@ -40,104 +41,218 @@ void Multicaster::send( const void *const data, unsigned int len) { - static const unsigned int PRIMES[16] = { 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53 }; + 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 - if (unlikely(len > ZT_MAX_MTU)) return; // sanity check + std::vector< std::pair > recipients; const NetworkConfig &config = network->config(); if (config.multicastLimit == 0) return; // multicast disabled - Address bridges[ZT_MAX_NETWORK_SPECIALISTS],multicastReplicators[ZT_MAX_NETWORK_SPECIALISTS]; - unsigned int bridgeCount = 0,multicastReplicatorCount = 0; + + 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 - std::vector< std::pair > recipients; - bool needMoar = false; - for(unsigned int i=0;i(9223372036854775807LL,bridges[i])); + int64_t lastGather = 0; + _K groupKey(network->id(),mg); { - Mutex::Lock l2(_groups_l); - _getMembersByTime(network->id(),mg,recipients); - } - std::sort(recipients.begin() + bridgeCount,recipients.end(),std::greater< std::pair >()); - recipients.erase(std::unique(recipients.begin(),recipients.end()),recipients.end()); - if (recipients.size() > config.multicastLimit) { - recipients.resize(config.multicastLimit); - } else if (recipients.size() < config.multicastLimit) { - needMoar = true; - } - - _txQueue_l.lock(); - _OM *om = &(_txQueue[_txQueuePtr++ % ZT_TX_QUEUE_SIZE]); - Mutex::Lock ql(om->lock); - _txQueue_l.unlock(); - - om->nwid = network->id(); - om->src = src; - om->mg = mg; - om->etherType = etherType; - om->dataSize = len; - memcpy(om->data,data,len); - - if (existingBloom) { - om->bloomFilterMultiplier = existingBloomMultiplier; - memcpy(om->bloomFilter,existingBloom,sizeof(om->bloomFilter)); - } else { - om->bloomFilterMultiplier = 1; - memset(om->bloomFilter,0,sizeof(om->bloomFilter)); - - if (recipients.size() > 1) { - unsigned int mult = 1; - unsigned int bestMultColl = 0xffffffff; - for(int k=0;k<16;++k) { // 16 == arbitrary limit on iterations for this search, also must be <= size of PRIMES - unsigned int 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(om->bloomFilter); - const uint8_t bit = 1 << (bfi & 7); - coll += ((om->bloomFilter[byte] & bit) != 0); - om->bloomFilter[byte] |= bit; - } - memset(om->bloomFilter,0,sizeof(om->bloomFilter)); - - if (coll <= bestMultColl) { - om->bloomFilterMultiplier = mult; - if (coll == 0) // perfect score, no need to continue searching - break; - bestMultColl = coll; - } - - mult = PRIMES[k]; + 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)); } } } - if (multicastReplicatorCount > 0) { - // SEND - return; + // 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); + } } - SharedPtr nextHops[2]; // these by definition are protocol version >= 11 - unsigned int nextHopsBestLatency[2] = { 0xffff,0xffff }; - for(std::vector< std::pair >::const_iterator r(recipients.begin());r!=recipients.end();++r) { - const unsigned int bfi = om->bloomFilterMultiplier * (unsigned int)r->second.toInt(); - const unsigned int bfbyte = (bfi >> 3) % sizeof(om->bloomFilter); - const uint8_t bfbit = 1 << (bfi & 7); - if ((om->bloomFilter[bfbyte] & bfbit) != 0) { - continue; - } else { - SharedPtr peer(RR->topology->get(r->second)); - if (peer) { - if (peer->remoteVersionProtocol() < 11) { - // SEND + if (recipients.empty()) + return 0; - om->bloomFilter[bfbyte] |= bfbit; - continue; - } else { + 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]) { @@ -151,17 +266,57 @@ void Multicaster::send( } } + // Set bits for next hops in bloom filter for(unsigned int nh=0;nh<2;++nh) { if (nextHops[nh]) { - const unsigned int bfi = om->bloomFilterMultiplier * (unsigned int)nextHops[nh]->address().toInt(); - om->bloomFilter[(bfi >> 3) % sizeof(om->bloomFilter)] |= 1 << (bfi & 7); + const unsigned int bfi = bloomMultiplier * (unsigned int)nextHops[nh]->address().toInt(); + ((uint8_t *)bloomFilter)[(bfi >> 3) % sizeof(bloomFilter)] |= 1 << (bfi & 7); + ++sentCount; } } - for(unsigned int nh=0;nh<2;++nh) { - if (nextHops[nh]) { + // 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) diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index 61a66f67d..e801fe9cd 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -30,8 +30,11 @@ #include "SharedPtr.hpp" #include "Packet.hpp" -// Size in bits -- this is pretty close to the maximum allowed by the protocol -#define ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS 16384 +// Size in bits -- do not change as this is about as large as we can support +// This leaves room for up to 10000 MTU data (max supported MTU) and header +// information in a maximum supported size packet. Note that data compression +// will practically reduce this size in transit for sparse or saturated fields. +#define ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS 50048 namespace ZeroTier { @@ -45,6 +48,44 @@ class Network; */ class Multicaster { +private: + // Composite key of network ID and multicast group + struct _K + { + uint64_t nwid; + MulticastGroup mg; + + ZT_ALWAYS_INLINE _K() : nwid(0),mg() {} + ZT_ALWAYS_INLINE _K(const uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {} + ZT_ALWAYS_INLINE bool operator==(const _K &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); } + ZT_ALWAYS_INLINE bool operator!=(const _K &k) const { return ((nwid != k.nwid)||(mg != k.mg)); } + ZT_ALWAYS_INLINE unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); } + }; + + // Multicast group info + struct _G + { + ZT_ALWAYS_INLINE _G() : lastGather(0),members(16) {} + int64_t lastGather; + Hashtable< Address,int64_t > members; + }; + + // Outbound multicast + struct _OM + { + uint64_t nwid; + MAC src; + MulticastGroup mg; + unsigned int etherType; + unsigned int dataSize; + unsigned int count; + unsigned int limit; + unsigned int bloomFilterMultiplier; + uint64_t bloomFilter[ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS / 64]; + uint8_t data[ZT_MAX_MTU]; + Mutex lock; + }; + public: Multicaster(const RuntimeEnvironment *renv); ~Multicaster(); @@ -60,7 +101,7 @@ public: ZT_ALWAYS_INLINE void add(const int64_t now,const uint64_t nwid,const MulticastGroup &mg,const Address &member) { Mutex::Lock l(_groups_l); - _groups[_K(nwid,mg)].set(member,now); + _groups[_K(nwid,mg)].members.set(member,now); } /** @@ -80,9 +121,9 @@ public: { Mutex::Lock l(_groups_l); const uint8_t *a = (const uint8_t *)addresses; - Hashtable< Address,int64_t > &members = _groups[_K(nwid,mg)]; + _G &g = _groups[_K(nwid,mg)]; while (count--) { - members.set(Address(a,ZT_ADDRESS_LENGTH),now); + g.members.set(Address(a,ZT_ADDRESS_LENGTH),now); a += ZT_ADDRESS_LENGTH; } } @@ -98,10 +139,10 @@ public: { Mutex::Lock l(_groups_l); const _K gk(nwid,mg); - Hashtable< Address,int64_t > *const members = _groups.get(gk); - if (members) { - members->erase(member); - if (members->empty()) + _G *const g = _groups.get(gk); + if (g) { + g->members.erase(member); + if (g->members.empty()) _groups.erase(gk); } } @@ -121,8 +162,21 @@ public: ZT_ALWAYS_INLINE unsigned long eachMember(const uint64_t nwid,const MulticastGroup &mg,F func) const { std::vector< std::pair > sortedByTime; - Mutex::Lock l(_groups_l); - _getMembersByTime(nwid,mg,sortedByTime); + { + Mutex::Lock l(_groups_l); + const _K gk(nwid,mg); + const _G *const g = _groups.get(gk); + if (g) { + sortedByTime.reserve(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)) + sortedByTime.push_back(std::pair(*miv,*mik)); + } + } + } std::sort(sortedByTime.begin(),sortedByTime.end()); for(std::vector< std::pair >::const_reverse_iterator i(sortedByTime.begin());i!=sortedByTime.end();++i) { if (!func(i->second)) @@ -144,8 +198,9 @@ public: * @param existingBloom Existing bloom filter or NULL if none * @param data Packet data * @param len Length of packet data + * @return Number of known recipients for multicast (including bridges and replicators) */ - void send( + unsigned int send( void *tPtr, int64_t now, const SharedPtr &network, @@ -166,74 +221,13 @@ public: void clean(int64_t now); private: - ZT_ALWAYS_INLINE void _getMembersByTime(const uint64_t nwid,const MulticastGroup &mg,std::vector< std::pair > &byTime) - { - // assumes _groups_l is locked - const _K gk(nwid,mg); - const Hashtable< Address,int64_t > *const members = _groups.get(gk); - if (members) { - byTime.reserve(members->size()); - { - Hashtable< Address,int64_t >::Iterator mi(*const_cast *>(members)); - Address *mik = nullptr; - int64_t *miv = nullptr; - while (mi.next(mik,miv)) - byTime.push_back(std::pair(*miv,*mik)); - } - } - } - - struct _K - { - uint64_t nwid; - MulticastGroup mg; - - ZT_ALWAYS_INLINE _K() : nwid(0),mg() {} - ZT_ALWAYS_INLINE _K(const uint64_t n,const MulticastGroup &g) : nwid(n),mg(g) {} - ZT_ALWAYS_INLINE bool operator==(const _K &k) const { return ((nwid == k.nwid)&&(mg == k.mg)); } - ZT_ALWAYS_INLINE bool operator!=(const _K &k) const { return ((nwid != k.nwid)||(mg != k.mg)); } - ZT_ALWAYS_INLINE unsigned long hashCode() const { return (mg.hashCode() ^ (unsigned long)(nwid ^ (nwid >> 32))); } - }; - - /* - * Multicast frame: - * <[8] 64-bit network ID> - * <[1] flags> - * [<[...] network certificate of membership (DEPRECATED)>] - * [<[4] 32-bit implicit gather limit (DEPRECATED)>] - * [<[5] ZeroTier address of originating sender (including w/0x08)>] - * [<[2] 16-bit bloom filter multiplier>] - * [<[2] 16-bit length of propagation bloom filter in bytes] - * [<[...] propagation bloom filter>] - * [<[6] source MAC>] - * <[6] destination MAC (multicast address)> - * <[4] 32-bit multicast ADI (multicast address extension)> - * <[2] 16-bit ethertype> - * <[...] ethernet payload> - * [<[2] 16-bit length of signature>] - * [<[...] signature (algorithm depends on sender identity)>] - */ - - struct _OM - { - uint64_t nwid; - MAC src; - MulticastGroup mg; - unsigned int etherType; - unsigned int dataSize; - unsigned int bloomFilterMultiplier; - uint8_t bloomFilter[ZT_MULTICAST_BLOOM_FILTER_SIZE_BITS / 8]; - uint8_t data[ZT_MAX_MTU]; - Mutex lock; - }; - const RuntimeEnvironment *const RR; _OM _txQueue[ZT_TX_QUEUE_SIZE]; unsigned int _txQueuePtr; Mutex _txQueue_l; - Hashtable< _K,Hashtable< Address,int64_t > > _groups; + Hashtable< _K,_G > _groups; Mutex _groups_l; }; diff --git a/node/Network.cpp b/node/Network.cpp index 9b97b436d..cf9edaa72 100644 --- a/node/Network.cpp +++ b/node/Network.cpp @@ -1065,6 +1065,43 @@ void Network::doPeriodicTasks(void *tPtr,const int64_t now) } } +void Network::learnBridgeRoute(const MAC &mac,const Address &addr) +{ + Mutex::Lock _l(_remoteBridgeRoutes_l); + _remoteBridgeRoutes[mac] = addr; + + // Anti-DOS circuit breaker to prevent nodes from spamming us with absurd numbers of bridge routes + while (_remoteBridgeRoutes.size() > ZT_MAX_BRIDGE_ROUTES) { + Hashtable< Address,unsigned long > counts; + Address maxAddr; + unsigned long maxCount = 0; + + MAC *m = (MAC *)0; + Address *a = (Address *)0; + + // Find the address responsible for the most entries + { + Hashtable::Iterator i(_remoteBridgeRoutes); + while (i.next(m,a)) { + const unsigned long c = ++counts[*a]; + if (c > maxCount) { + maxCount = c; + maxAddr = *a; + } + } + } + + // Kill this address from our table, since it's most likely spamming us + { + Hashtable::Iterator i(_remoteBridgeRoutes); + while (i.next(m,a)) { + if (*a == maxAddr) + _remoteBridgeRoutes.erase(*m); + } + } + } +} + Membership::AddCredentialResult Network::addCredential(void *tPtr,const Address &sentFrom,const Revocation &rev) { if (rev.networkId() != _id) @@ -1300,7 +1337,11 @@ void Network::_externalConfig(ZT_VirtualNetworkConfig *ec) const ec->type = (_config) ? (_config.isPrivate() ? ZT_NETWORK_TYPE_PRIVATE : ZT_NETWORK_TYPE_PUBLIC) : ZT_NETWORK_TYPE_PRIVATE; ec->mtu = (_config) ? _config.mtu : ZT_DEFAULT_MTU; ec->dhcp = 0; - std::vector
ab(_config.activeBridges()); + std::vector
ab; + for(unsigned int i=0;i<_config.specialistCount;++i) { + if ((_config.specialists[i] & ZT_NETWORKCONFIG_SPECIALIST_TYPE_ACTIVE_BRIDGE) != 0) + ab.push_back(Address(_config.specialists[i])); + } ec->bridge = (std::find(ab.begin(),ab.end(),RR->identity.address()) != ab.end()) ? 1 : 0; ec->broadcastEnabled = (_config) ? (_config.enableBroadcast() ? 1 : 0) : 0; ec->portError = _portError; diff --git a/node/Network.hpp b/node/Network.hpp index b3efb3cbc..e98d9393f 100644 --- a/node/Network.hpp +++ b/node/Network.hpp @@ -80,14 +80,14 @@ public: ~Network(); - inline uint64_t id() const { return _id; } - inline Address controller() const { return Address(_id >> 24); } - inline bool multicastEnabled() const { return (_config.multicastLimit > 0); } - inline bool hasConfig() const { return (_config); } - inline uint64_t lastConfigUpdate() const { return _lastConfigUpdate; } - inline ZT_VirtualNetworkStatus status() const { return _status(); } - inline const NetworkConfig &config() const { return _config; } - inline const MAC &mac() const { return _mac; } + ZT_ALWAYS_INLINE uint64_t id() const { return _id; } + ZT_ALWAYS_INLINE Address controller() const { return Address(_id >> 24); } + ZT_ALWAYS_INLINE bool multicastEnabled() const { return (_config.multicastLimit > 0); } + ZT_ALWAYS_INLINE bool hasConfig() const { return (_config); } + ZT_ALWAYS_INLINE uint64_t lastConfigUpdate() const { return _lastConfigUpdate; } + ZT_ALWAYS_INLINE ZT_VirtualNetworkStatus status() const { return _status(); } + ZT_ALWAYS_INLINE const NetworkConfig &config() const { return _config; } + ZT_ALWAYS_INLINE const MAC &mac() const { return _mac; } /** * Apply filters to an outgoing packet @@ -159,7 +159,7 @@ public: * @param includeBridgedGroups If true, also check groups we've learned via bridging * @return True if this network endpoint / peer is a member */ - inline bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const + ZT_ALWAYS_INLINE bool subscribedToMulticastGroup(const MulticastGroup &mg,const bool includeBridgedGroups) const { Mutex::Lock l(_myMulticastGroups_l); if (std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) @@ -175,7 +175,7 @@ public: * @param tPtr Thread pointer to be handed through to any callbacks called as a result of this call * @param mg New multicast group */ - inline void multicastSubscribe(void *tPtr,const MulticastGroup &mg) + ZT_ALWAYS_INLINE void multicastSubscribe(void *tPtr,const MulticastGroup &mg) { Mutex::Lock l(_myMulticastGroups_l); if (!std::binary_search(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)) { @@ -190,7 +190,7 @@ public: * * @param mg Multicast group */ - inline void multicastUnsubscribe(const MulticastGroup &mg) + ZT_ALWAYS_INLINE void multicastUnsubscribe(const MulticastGroup &mg) { Mutex::Lock l(_myMulticastGroups_l); std::vector::iterator i(std::lower_bound(_myMulticastGroups.begin(),_myMulticastGroups.end(),mg)); @@ -231,12 +231,12 @@ public: /** * Set netconf failure to 'access denied' -- called in IncomingPacket when controller reports this */ - inline void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; } + ZT_ALWAYS_INLINE void setAccessDenied() { _netconfFailure = NETCONF_FAILURE_ACCESS_DENIED; } /** * Set netconf failure to 'not found' -- called by IncomingPacket when controller reports this */ - inline void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; } + ZT_ALWAYS_INLINE void setNotFound() { _netconfFailure = NETCONF_FAILURE_NOT_FOUND; } /** * Determine whether this peer is permitted to communicate on this network @@ -257,7 +257,7 @@ public: * @param mac MAC address * @return ZeroTier address of bridge to this MAC */ - inline Address findBridgeTo(const MAC &mac) const + ZT_ALWAYS_INLINE Address findBridgeTo(const MAC &mac) const { Mutex::Lock _l(_remoteBridgeRoutes_l); const Address *const br = _remoteBridgeRoutes.get(mac); @@ -275,42 +275,7 @@ public: * @param mac MAC address of destination * @param addr Bridge this MAC is reachable behind */ - inline void learnBridgeRoute(const MAC &mac,const Address &addr) - { - Mutex::Lock _l(_remoteBridgeRoutes_l); - _remoteBridgeRoutes[mac] = addr; - - // Anti-DOS circuit breaker to prevent nodes from spamming us with absurd numbers of bridge routes - while (_remoteBridgeRoutes.size() > ZT_MAX_BRIDGE_ROUTES) { - Hashtable< Address,unsigned long > counts; - Address maxAddr; - unsigned long maxCount = 0; - - MAC *m = (MAC *)0; - Address *a = (Address *)0; - - // Find the address responsible for the most entries - { - Hashtable::Iterator i(_remoteBridgeRoutes); - while (i.next(m,a)) { - const unsigned long c = ++counts[*a]; - if (c > maxCount) { - maxCount = c; - maxAddr = *a; - } - } - } - - // Kill this address from our table, since it's most likely spamming us - { - Hashtable::Iterator i(_remoteBridgeRoutes); - while (i.next(m,a)) { - if (*a == maxAddr) - _remoteBridgeRoutes.erase(*m); - } - } - } - } + void learnBridgeRoute(const MAC &mac,const Address &addr); /** * Learn a multicast group that is bridged to our tap device @@ -319,7 +284,7 @@ public: * @param mg Multicast group * @param now Current time */ - inline void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now) + ZT_ALWAYS_INLINE void learnBridgedMulticastGroup(void *tPtr,const MulticastGroup &mg,int64_t now) { Mutex::Lock l(_myMulticastGroups_l); _multicastGroupsBehindMe.set(mg,now); @@ -328,7 +293,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com) + ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfMembership &com) { if (com.networkId() != _id) return Membership::ADD_REJECTED; @@ -339,7 +304,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - inline Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap) + ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const Capability &cap) { if (cap.networkId() != _id) return Membership::ADD_REJECTED; @@ -350,7 +315,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - inline Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag) + ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const Tag &tag) { if (tag.networkId() != _id) return Membership::ADD_REJECTED; @@ -366,7 +331,7 @@ public: /** * Validate a credential and learn it if it passes certificate and other checks */ - inline Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo) + ZT_ALWAYS_INLINE Membership::AddCredentialResult addCredential(void *tPtr,const CertificateOfOwnership &coo) { if (coo.networkId() != _id) return Membership::ADD_REJECTED; @@ -381,7 +346,7 @@ public: * @param to Destination peer address * @param now Current time */ - inline void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now) + ZT_ALWAYS_INLINE void pushCredentialsNow(void *tPtr,const Address &to,const int64_t now) { Mutex::Lock _l(_memberships_l); _memberships[to].pushCredentials(RR,tPtr,now,to,_config); @@ -394,7 +359,7 @@ public: * @param to Destination peer address * @param now Current time */ - inline void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now) + ZT_ALWAYS_INLINE void pushCredentialsIfNeeded(void *tPtr,const Address &to,const int64_t now) { const int64_t tout = std::min(_config.credentialTimeMaxDelta,(int64_t)ZT_PEER_ACTIVITY_TIMEOUT); Mutex::Lock _l(_memberships_l); @@ -409,7 +374,7 @@ public: * This sets the network to completely remove itself on delete. This also prevents the * call of the normal port shutdown event on delete. */ - inline void destroy() + ZT_ALWAYS_INLINE void destroy() { _memberships_l.lock(); _config_l.lock(); @@ -423,16 +388,34 @@ public: * * @param ec Buffer to fill with externally-visible network configuration */ - inline void externalConfig(ZT_VirtualNetworkConfig *ec) const + ZT_ALWAYS_INLINE void externalConfig(ZT_VirtualNetworkConfig *ec) const { Mutex::Lock _l(_config_l); _externalConfig(ec); } + /** + * Iterate through memberships + * + * @param f Function of (const Address,const Membership) + */ + template + ZT_ALWAYS_INLINE void eachMember(F f) + { + Mutex::Lock ml(_memberships_l); + Hashtable::Iterator i(_memberships); + const Address *a = nullptr; + const Membership *m = nullptr; + while (i.next(a,m)) { + if (!f(*a,*m)) + break; + } + } + /** * @return Externally usable pointer-to-pointer exported via the core API */ - inline void **userPtr() { return &_uPtr; } + ZT_ALWAYS_INLINE void **userPtr() { return &_uPtr; } private: void _requestConfiguration(void *tPtr); diff --git a/node/Packet.hpp b/node/Packet.hpp index 2945ea179..13c400179 100644 --- a/node/Packet.hpp +++ b/node/Packet.hpp @@ -683,7 +683,7 @@ public: /** * Request endpoints for multicast distribution: * <[8] 64-bit network ID> - * <[1] flags> + * <[1] flags (unused, must be 0)> * <[6] MAC address of multicast group being queried> * <[4] 32-bit ADI for multicast group being queried> * <[4] 32-bit requested max number of multicast peers> diff --git a/node/Utils.hpp b/node/Utils.hpp index b9568b592..04fe1ae4c 100644 --- a/node/Utils.hpp +++ b/node/Utils.hpp @@ -280,6 +280,12 @@ public: return true; } +#ifdef __GNUC__ + static ZT_ALWAYS_INLINE unsigned int countBits(const uint8_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } + static ZT_ALWAYS_INLINE unsigned int countBits(const uint16_t v) { return (unsigned int)__builtin_popcount((unsigned int)v); } + static ZT_ALWAYS_INLINE unsigned int countBits(const uint32_t v) { return (unsigned int)__builtin_popcountl((unsigned long)v); } + static ZT_ALWAYS_INLINE unsigned int countBits(const uint64_t v) { return (unsigned int)__builtin_popcountll((unsigned long long)v); } +#else /** * Count the number of bits set in an integer * @@ -287,15 +293,16 @@ public: * @return Number of bits set in this integer (0-bits in integer) */ template - static ZT_ALWAYS_INLINE uint64_t countBits(T v) + static ZT_ALWAYS_INLINE unsigned int countBits(T v) { v = v - ((v >> 1) & (T)~(T)0/3); v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); v = (v + (v >> 4)) & (T)~(T)0/255*15; - return (T)(v * ((~((T)0))/((T)255))) >> ((sizeof(T) - 1) * 8); + return (unsigned int)((v * ((~((T)0))/((T)255))) >> ((sizeof(T) - 1) * 8)); } +#endif - // Byte swappers for big/little endian conversion +// Byte swappers for big/little endian conversion #if __BYTE_ORDER == __LITTLE_ENDIAN static ZT_ALWAYS_INLINE uint8_t hton(uint8_t n) { return n; } static ZT_ALWAYS_INLINE int8_t hton(int8_t n) { return n; }