Work on defining new direct broadcast multicast algorithm.

This commit is contained in:
Adam Ierymenko 2014-09-18 18:28:14 -07:00
parent d37c3ad30f
commit d9abd4d9be
13 changed files with 353 additions and 442 deletions

View File

@ -228,37 +228,20 @@
#define ZT_RELAY_MAX_HOPS 3 #define ZT_RELAY_MAX_HOPS 3
/** /**
* Size of multicast deduplication ring buffer in 64-bit ints * Expire time for multicast 'likes' and indirect multicast memberships in ms
*/ */
#define ZT_MULTICAST_DEDUP_HISTORY_LENGTH 512 #define ZT_MULTICAST_LIKE_EXPIRE 600000
/**
* Default number of bits in multicast propagation prefix
*/
#define ZT_DEFAULT_MULTICAST_PREFIX_BITS 2
/**
* Default max depth (TTL) for multicast propagation
*/
#define ZT_DEFAULT_MULTICAST_DEPTH 32
/**
* Global maximum for multicast propagation depth
*
* This is kind of an insane value, meant as a sanity check.
*/
#define ZT_MULTICAST_GLOBAL_MAX_DEPTH 500
/**
* Expire time for multicast 'likes' in ms
*/
#define ZT_MULTICAST_LIKE_EXPIRE 120000
/** /**
* Time between polls of local tap devices for multicast membership changes * Time between polls of local tap devices for multicast membership changes
*/ */
#define ZT_MULTICAST_LOCAL_POLL_PERIOD 10000 #define ZT_MULTICAST_LOCAL_POLL_PERIOD 10000
/**
* Minimum delay between attempts to gather multicast topology info if members > 0
*/
#define ZT_MULTICAST_TOPOLOGY_RESEARCH_RATE_THROTTLE 120000
/** /**
* Delay between scans of the topology active peer DB for peers that need ping * Delay between scans of the topology active peer DB for peers that need ping
*/ */

View File

@ -0,0 +1,88 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2011-2014 ZeroTier Networks LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include <algorithm>
#include "Constants.hpp"
#include "MulticastTopology.hpp"
#include "Topology.hpp"
namespace ZeroTier {
MulticastTopology::MulticastTopology()
{
}
MulticastTopology::~MulticastTopology()
{
}
void MulticastTopology::clean(const Topology &topology)
{
uint64_t now = Utils::now();
for(std::map< MulticastGroup,std::vector<MulticastGroupMember> >::iterator mm(_members.begin());mm!=_members.end();) {
std::vector<MulticastGroupMember>::iterator reader(mm->second.begin());
std::vector<MulticastGroupMember>::iterator writer(mm->second.begin());
unsigned long count = 0;
while (reader != mm->second.end()) {
if ((now - reader->timestamp) < ZT_MULTICAST_LIKE_EXPIRE) {
*writer = *reader;
/* We sort in ascending order of most recent relevant activity. For peers we've learned
* about by direct LIKEs, we do this in order of their own activity. For indirectly
* acquired peers we do this minus a constant to place these categorically below directly
* learned peers. For peers with no active Peer record, we use the time we last learned
* about them minus one day (a large constant) to put these at the bottom of the list.
* List is sorted in ascending order of rank and multicasts are sent last-to-first. */
if (writer->learnedFrom) {
SharedPtr<Peer> p(topology.getPeer(writer->learnedFrom));
if (p)
writer->rank = p->lastUnicastFrame() - ZT_MULTICAST_LIKE_EXPIRE;
else writer->rank = writer->timestamp - 86400000;
} else {
SharedPtr<Peer> p(topology.getPeer(writer->address));
if (p)
writer->rank = p->lastUnicastFrame();
else writer->rank = writer->timestamp - 86400000;
}
++writer;
++count;
}
++reader;
}
if (count) {
mm->second.resize(count);
std::sort(mm->second.begin(),mm->second.end()); // sorts in ascending order of rank
++mm;
} else _members.erase(mm++);
}
}
} // namespace ZeroTier

162
node/MulticastTopology.hpp Normal file
View File

@ -0,0 +1,162 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2011-2014 ZeroTier Networks LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_MULTICASTTOPOLOGY_HPP
#define ZT_MULTICASTTOPOLOGY_HPP
#include <stdint.h>
#include <string.h>
#include <map>
#include <vector>
#include "Constants.hpp"
#include "Address.hpp"
#include "MulticastGroup.hpp"
#include "Mutex.hpp"
#include "Utils.hpp"
namespace ZeroTier {
class Topology;
/**
* Database of known multicast peers within a network
*/
class MulticastTopology
{
private:
struct MulticastGroupMember
{
MulticastGroupMember() {}
MulticastGroupMember(const Address &a,const Address &lf,uint64_t ts) : address(a),learnedFrom(lf),timestamp(ts) {}
Address address;
Address learnedFrom; // NULL/0 for addresses directly learned from LIKE
uint64_t timestamp; // time of last LIKE or OK response to MULTICAST_LONELY
uint64_t rank; // used by sorting algorithm in clean()
// for sorting in ascending order of rank
inline bool operator<(const MulticastGroupMember &m) const throw() { return (rank < m.rank); }
};
public:
MulticastTopology();
~MulticastTopology();
/**
* Add or update a member in a multicast group
*
* @param mg Multicast group
* @param member Member to add/update
* @param learnedFrom Address from which we learned this member or NULL/0 Address if direct
*/
inline void add(const MulticastGroup &mg,const Address &member,const Address &learnedFrom)
{
Mutex::Lock _l(_members_m);
std::vector<MulticastGroupMember> &mv = _members[mg];
for(std::vector<MulticastGroupMember>::iterator m(mv.begin());m!=mv.end();++m) {
if (m->address == member) {
if (m->learnedFrom) // once a member has been seen directly, we keep its status as direct
m->learnedFrom = learnedFrom;
m->timestamp = Utils::now();
return;
}
}
mv.push_back(MulticastGroupMember(member,learnedFrom,Utils::now()));
}
/**
* Erase a member from a multicast group (if present)
*
* @param mg Multicast group
* @param member Member to erase
*/
inline void erase(const MulticastGroup &mg,const Address &member)
{
Mutex::Lock _l(_members_m);
std::map< MulticastGroup,std::vector<MulticastGroupMember> >::iterator r(_members.find(mg));
if (r != _members.end()) {
for(std::vector<MulticastGroupMember>::iterator m(r->second.begin());m!=r->second.end();++m) {
if (m->address == member) {
r->second.erase(m);
return;
}
}
}
}
/**
* @param mg Multicast group
* @return Number of known peers in group
*/
inline unsigned int memberCount(const MulticastGroup &mg) const
{
Mutex::Lock _l(_members_m);
std::map< MulticastGroup,std::vector<MulticastGroupMember> >::const_iterator r(_members.find(mg));
return ((r != _members.end()) ? (unsigned int)r->second.size() : (unsigned int)0);
}
/**
* Iterate over the known members of a multicast group
*
* @param mg Multicast group
* @param func Function to be called with multicast group and address of member
* @tparam F Function type (explicitly template on "FuncObj &" if reference instead of copy should be passed)
* @return Number of members in multicast group for which function was called
*/
template<typename F>
inline unsigned int eachMember(const MulticastGroup &mg,F func) const
{
Mutex::Lock _l(_members_m);
std::map< MulticastGroup,std::vector<MulticastGroupMember> >::const_iterator r(_members.find(mg));
if (r != _members.end()) {
// We go in reverse order because most recently learned members are pushed to the end
// of the vector. The priority resort algorithm in clean() sorts in ascending order
// of propagation priority too.
for(std::vector<MulticastGroupMember>::const_reverse_iterator m(r->second.rbegin());m!=r->second.rend();++m) {
func(mg,m->address);
}
return (unsigned int)r->second.size();
} else return 0;
}
/**
* Clean up and resort database
*
* @param topology Global peer topology
*/
void clean(const Topology &topology);
private:
std::map< MulticastGroup,std::vector<MulticastGroupMember> > _members;
Mutex _members_m;
};
} // namespace ZeroTier
#endif

View File

@ -1,105 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2011-2014 ZeroTier Networks LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#include <stdio.h>
#include "Constants.hpp"
#include "Multicaster.hpp"
#include "Utils.hpp"
namespace ZeroTier {
Multicaster::Multicaster()
{
}
Multicaster::~Multicaster()
{
}
void Multicaster::likesGroup(uint64_t nwid,const Address &a,const MulticastGroup &mg,uint64_t now)
{
Mutex::Lock _l(_lock);
_NetInfo &n = _nets[nwid];
_SubInfo &si = n.subscriptions[_Subscription(a,mg)];
if (!si.lastLike) { // on first LIKE, we must add to _proximity[mg]
std::list< Address > &p = n.proximity[mg];
p.push_front(a);
si.proximitySlot = p.begin(); // list's iterators remain valid until erase()
}
si.lastLike = now;
}
void Multicaster::bringCloser(uint64_t nwid,const Address &a)
{
Mutex::Lock _l(_lock);
std::map< uint64_t,_NetInfo >::iterator n(_nets.find(nwid));
if (n == _nets.end())
return;
/* _subscriptions contains pairs of <Address,MulticastGroup>, so we can
* easily iterate through all subscriptions for a given address by
* starting with the default all-zero MulticastGroup() as lower bound
* and stopping when we're not looking at the right address anymore.
* Then we can look up _proximity and rapidly splice() the list using
* the saved iterator in _SubInfo. */
std::map< _Subscription,_SubInfo >::iterator s(n->second.subscriptions.lower_bound(_Subscription(a,MulticastGroup())));
while ((s != n->second.subscriptions.end())&&(s->first.first == a)) {
std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(s->first.second));
if (s->second.proximitySlot != p->second.begin())
p->second.splice(p->second.begin(),p->second,s->second.proximitySlot);
++s;
}
}
void Multicaster::clean()
{
Mutex::Lock _l(_lock);
uint64_t now = Utils::now();
for(std::map< uint64_t,_NetInfo >::iterator n(_nets.begin());n!=_nets.end();) {
for(std::map< _Subscription,_SubInfo >::iterator s(n->second.subscriptions.begin());s!=n->second.subscriptions.end();) {
if ((now - s->second.lastLike) >= ZT_MULTICAST_LIKE_EXPIRE) {
std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(s->first.second));
p->second.erase(s->second.proximitySlot);
if (p->second.empty())
n->second.proximity.erase(p);
n->second.subscriptions.erase(s++);
} else ++s;
}
if (n->second.proximity.empty()&&n->second.subscriptions.empty())
_nets.erase(n++);
else ++n;
}
}
} // namespace ZeroTier

View File

@ -1,275 +0,0 @@
/*
* ZeroTier One - Global Peer to Peer Ethernet
* Copyright (C) 2011-2014 ZeroTier Networks LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* --
*
* ZeroTier may be used and distributed under the terms of the GPLv3, which
* are available at: http://www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form, please contact ZeroTier Networks
* LLC. Start here: http://www.zerotier.com/
*/
#ifndef ZT_MULTICASTER_HPP
#define ZT_MULTICASTER_HPP
#include <stdint.h>
#include <string.h>
#include <stdexcept>
#include <map>
#include <set>
#include <list>
#include <algorithm>
#include "Constants.hpp"
#include "Mutex.hpp"
#include "MulticastGroup.hpp"
#include "Topology.hpp"
#include "Address.hpp"
#include "Buffer.hpp"
namespace ZeroTier {
/**
* Multicast propagation algorithm core and database
*/
class Multicaster
{
public:
Multicaster();
~Multicaster();
/**
* Add or renew a peer's subscription to a multicast group
*
* @param nwid Network ID
* @param a Address that LIKEd
* @param mg Multicast group
* @param now Current time
*/
void likesGroup(uint64_t nwid,const Address &a,const MulticastGroup &mg,uint64_t now);
/**
* Bring a peer closer in terms of propagation priority
*
* This gets called from PacketDecoder when a unicast frame is received.
*
* @param nwid Network ID
* @param a Address to bring closer (e.g. due to unicast message)
* @param now Current time
*/
void bringCloser(uint64_t nwid,const Address &a);
/**
* Erase entries for expired LIKEs and GOT records
*/
void clean();
/**
* Multicast deduplicator
*
* This checks to see if a multicast GUID has been seen before. If not, it
* adds it to the history and returns false.
*
* @param nwid Network ID
* @param mcGuid Multicast GUID (sender address + sender unique ID)
* @return True if multicast IS a duplicate, false otherwise
*/
inline bool deduplicate(uint64_t nwid,uint64_t mcGuid)
throw()
{
Mutex::Lock _l(_lock);
_NetInfo &n = _nets[nwid];
for(unsigned int i=0;i<ZT_MULTICAST_DEDUP_HISTORY_LENGTH;++i) {
if (n.multicastHistory[i] == mcGuid)
return true;
}
n.multicastHistory[n.multicastHistoryPtr++ % ZT_MULTICAST_DEDUP_HISTORY_LENGTH] = mcGuid;
return false;
}
/**
* Pick next hops for a multicast by proximity
*
* The function or function object must return true if more hops are desired
* or false to stop finding new hops and return.
*
* @param nwid Network ID
* @param mg Multicast group
* @param nextHopFunc Function to call for each address, search stops if it returns false
* @tparam F Function to receive each next hop address
*/
template<typename F>
inline void getNextHops(uint64_t nwid,const MulticastGroup &mg,F nextHopFunc)
{
Mutex::Lock _l(_lock);
std::map< uint64_t,_NetInfo >::iterator n(_nets.find(nwid));
if (n == _nets.end())
return;
std::map< MulticastGroup,std::list< Address > >::iterator p(n->second.proximity.find(mg));
if (p == n->second.proximity.end())
return;
for(std::list< Address >::iterator a(p->second.begin());a!=p->second.end();++a) {
if (!nextHopFunc(*a))
break;
}
}
/**
* Functor to add addresses to multicast frame propagation queues
*
* This function object checks the origin, bloom filter, and restriction
* prefix for each address and if all these pass it adds the address and
* increments the pointer pointed to by ptr. It stops (returns false) when
* *ptr reaches end. It's used in PacketDecoder and Switch with getNextHops()
* to compose multicast frame headers.
*/
class AddToPropagationQueue
{
public:
/**
* @param ptr Pointer to pointer to current position in queue
* @param end End of queue
* @param bloom Bloom filter field (must be 1024 bytes in length)
* @param bloomNonce Random nonce for bloom filter randomization
* @param origin Originating address
* @param prefixBits Number of bits in propagation restriction prefix
* @param prefix Propagation restrition prefix
* @param topology Topology database
* @param now Current time
*/
AddToPropagationQueue(
unsigned char **ptr,
unsigned char *end,
unsigned char *bloom,
uint16_t bloomNonce,
const Address &origin,
unsigned int prefixBits,
uint64_t prefix,
const Topology *topology,
uint64_t now)
throw() :
_origin(origin),
_bloomNonce((uint64_t)bloomNonce),
_prefix(prefix),
_now(now),
_ptr(ptr),
_end(end),
_bloom(bloom),
_topology(topology),
_prefixBits(prefixBits) {}
/**
* @param a Address to (possibly) add
* @return True if FIFO still contains room for more possible addresses
*/
inline bool operator()(const Address &a)
throw()
{
if (*_ptr >= _end)
return false;
// Exclude original sender -- obviously they've already seen it
if (a == _origin)
return true;
// Exclude addresses not in this prefix domain
if (!a.withinMulticastPropagationPrefix(_prefix,_prefixBits))
return true;
// Exclude addresses remembered in bloom filter
uint64_t aint = a.toInt() + _bloomNonce;
const unsigned int bit = (unsigned int)(aint ^ (aint >> 13) ^ (aint >> 26) ^ (aint >> 39)) & 0x1fff;
unsigned char *const bbyte = _bloom + (bit >> 3); // note: bloom filter size == 1024 is hard-coded here
const unsigned char bmask = 1 << (bit & 7);
if ((*bbyte & bmask))
return true; // address already visited
// Exclude peers that don't appear to be online
SharedPtr<Peer> p(_topology->getPeer(a));
if ((!p)||(!p->alive(_now)))
return true;
// Remember address in bloom filter
*bbyte |= bmask;
a.copyTo(*_ptr,ZT_ADDRESS_LENGTH);
return ((*_ptr += ZT_ADDRESS_LENGTH) < _end);
}
private:
const Address _origin;
const uint64_t _bloomNonce;
const uint64_t _prefix;
const uint64_t _now;
unsigned char **const _ptr;
unsigned char *const _end;
unsigned char *const _bloom;
const Topology *const _topology;
const unsigned int _prefixBits;
};
private:
// Information about a subscription
struct _SubInfo
{
_SubInfo() :
lastLike(0),
proximitySlot() {}
// Time of last MULTICAST_LIKE for this group
uint64_t lastLike;
// Slot in corresponding list in _proximity
std::list< Address >::iterator proximitySlot;
};
// An address and multicast group tuple
typedef std::pair< Address,MulticastGroup > _Subscription;
// Multicast info for a given network
struct _NetInfo
{
_NetInfo()
throw()
{
memset(multicastHistory,0,sizeof(multicastHistory));
multicastHistoryPtr = 0;
}
// Ring buffer of most recently injected multicast packet GUIDs
uint64_t multicastHistory[ZT_MULTICAST_DEDUP_HISTORY_LENGTH];
unsigned int multicastHistoryPtr;
// Peer proximity ordering for peers subscribed to each group
std::map< MulticastGroup,std::list< Address > > proximity;
// Peer subscriptions to multicast groups
std::map< _Subscription,_SubInfo > subscriptions;
};
std::map< uint64_t,_NetInfo > _nets;
Mutex _lock;
};
} // namespace ZeroTier
#endif

View File

@ -117,23 +117,23 @@ bool Network::updateMulticastGroups()
EthernetTap *t = _tap; EthernetTap *t = _tap;
if (t) { if (t) {
// Grab current groups from the local tap // Grab current groups from the local tap
bool updated = t->updateMulticastGroups(_multicastGroups); bool updated = t->updateMulticastGroups(_myMulticastGroups);
// Merge in learned groups from any hosts bridged in behind us // Merge in learned groups from any hosts bridged in behind us
for(std::map<MulticastGroup,uint64_t>::const_iterator mg(_bridgedMulticastGroups.begin());mg!=_bridgedMulticastGroups.end();++mg) for(std::map<MulticastGroup,uint64_t>::const_iterator mg(_multicastGroupsBehindMe.begin());mg!=_multicastGroupsBehindMe.end();++mg)
_multicastGroups.insert(mg->first); _myMulticastGroups.insert(mg->first);
// Add or remove BROADCAST group based on broadcast enabled netconf flag // Add or remove BROADCAST group based on broadcast enabled netconf flag
if ((_config)&&(_config->enableBroadcast())) { if ((_config)&&(_config->enableBroadcast())) {
if (_multicastGroups.count(BROADCAST)) if (_myMulticastGroups.count(BROADCAST))
return updated; return updated;
else { else {
_multicastGroups.insert(BROADCAST); _myMulticastGroups.insert(BROADCAST);
return true; return true;
} }
} else { } else {
if (_multicastGroups.count(BROADCAST)) { if (_myMulticastGroups.count(BROADCAST)) {
_multicastGroups.erase(BROADCAST); _myMulticastGroups.erase(BROADCAST);
return true; return true;
} else return updated; } else return updated;
} }
@ -311,9 +311,9 @@ void Network::clean()
} }
// Clean learned multicast groups if we haven't heard from them in a while // Clean learned multicast groups if we haven't heard from them in a while
for(std::map<MulticastGroup,uint64_t>::iterator mg(_bridgedMulticastGroups.begin());mg!=_bridgedMulticastGroups.end();) { for(std::map<MulticastGroup,uint64_t>::iterator mg(_multicastGroupsBehindMe.begin());mg!=_multicastGroupsBehindMe.end();) {
if ((now - mg->second) > (ZT_MULTICAST_LIKE_EXPIRE * 2)) if ((now - mg->second) > (ZT_MULTICAST_LIKE_EXPIRE * 2))
_bridgedMulticastGroups.erase(mg++); _multicastGroupsBehindMe.erase(mg++);
else ++mg; else ++mg;
} }
} }
@ -419,23 +419,23 @@ void Network::threadMain()
void Network::learnBridgeRoute(const MAC &mac,const Address &addr) void Network::learnBridgeRoute(const MAC &mac,const Address &addr)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
_bridgeRoutes[mac] = addr; _remoteBridgeRoutes[mac] = addr;
// If _bridgeRoutes exceeds sanity limit, trim worst offenders until below -- denial of service circuit breaker // If _remoteBridgeRoutes exceeds sanity limit, trim worst offenders until below -- denial of service circuit breaker
while (_bridgeRoutes.size() > ZT_MAX_BRIDGE_ROUTES) { while (_remoteBridgeRoutes.size() > ZT_MAX_BRIDGE_ROUTES) {
std::map<Address,unsigned long> counts; std::map<Address,unsigned long> counts;
Address maxAddr; Address maxAddr;
unsigned long maxCount = 0; unsigned long maxCount = 0;
for(std::map<MAC,Address>::iterator br(_bridgeRoutes.begin());br!=_bridgeRoutes.end();++br) { for(std::map<MAC,Address>::iterator br(_remoteBridgeRoutes.begin());br!=_remoteBridgeRoutes.end();++br) {
unsigned long c = ++counts[br->second]; unsigned long c = ++counts[br->second];
if (c > maxCount) { if (c > maxCount) {
maxCount = c; maxCount = c;
maxAddr = br->second; maxAddr = br->second;
} }
} }
for(std::map<MAC,Address>::iterator br(_bridgeRoutes.begin());br!=_bridgeRoutes.end();) { for(std::map<MAC,Address>::iterator br(_remoteBridgeRoutes.begin());br!=_remoteBridgeRoutes.end();) {
if (br->second == maxAddr) if (br->second == maxAddr)
_bridgeRoutes.erase(br++); _remoteBridgeRoutes.erase(br++);
else ++br; else ++br;
} }
} }

View File

@ -159,7 +159,7 @@ public:
inline std::set<MulticastGroup> multicastGroups() const inline std::set<MulticastGroup> multicastGroups() const
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
return _multicastGroups; return _myMulticastGroups;
} }
/** /**
@ -378,8 +378,8 @@ public:
inline Address findBridgeTo(const MAC &mac) const inline Address findBridgeTo(const MAC &mac) const
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
std::map<MAC,Address>::const_iterator br(_bridgeRoutes.find(mac)); std::map<MAC,Address>::const_iterator br(_remoteBridgeRoutes.find(mac));
if (br == _bridgeRoutes.end()) if (br == _remoteBridgeRoutes.end())
return Address(); return Address();
return br->second; return br->second;
} }
@ -401,7 +401,7 @@ public:
inline void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now) inline void learnBridgedMulticastGroup(const MulticastGroup &mg,uint64_t now)
{ {
Mutex::Lock _l(_lock); Mutex::Lock _l(_lock);
_bridgedMulticastGroups[mg] = now; _multicastGroupsBehindMe[mg] = now;
} }
/** /**
@ -445,16 +445,20 @@ private:
EthernetTap *volatile _tap; // tap device or NULL if not initialized yet EthernetTap *volatile _tap; // tap device or NULL if not initialized yet
volatile bool _enabled; volatile bool _enabled;
std::set<MulticastGroup> _multicastGroups; std::set<MulticastGroup> _myMulticastGroups; // multicast groups that we belong to including those behind us (updated periodically)
std::map<MulticastGroup,uint64_t> _multicastGroupsBehindMe; // multicast groups bridged to us and when we last saw activity on each
std::map<MAC,Address> _remoteBridgeRoutes; // remote addresses where given MACs are reachable
// Deprecated, but will be kept around until P5_MULTICAST_FRAME is gone -- but the
// entry for us is still used by both. Eventually there will only be one BandwidthAccount,
// namely ours.
std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts; std::map< std::pair<Address,MulticastGroup>,BandwidthAccount > _multicastRateAccounts;
std::map<Address,CertificateOfMembership> _membershipCertificates; std::map<Address,CertificateOfMembership> _membershipCertificates; // Other members' certificates of membership
std::map<Address,uint64_t> _lastPushedMembershipCertificate; std::map<Address,uint64_t> _lastPushedMembershipCertificate; // When did we last push our certificate to each remote member?
std::map<MAC,Address> _bridgeRoutes; // remote addresses where given MACs are reachable SharedPtr<NetworkConfig> _config; // Most recent network configuration, which is an immutable value-object
std::map<MulticastGroup,uint64_t> _bridgedMulticastGroups; // multicast groups of interest on our side of the bridge
SharedPtr<NetworkConfig> _config;
volatile uint64_t _lastConfigUpdate; volatile uint64_t _lastConfigUpdate;
volatile bool _destroyed; volatile bool _destroyed;

View File

@ -70,7 +70,6 @@
#include "Network.hpp" #include "Network.hpp"
#include "MulticastGroup.hpp" #include "MulticastGroup.hpp"
#include "Mutex.hpp" #include "Mutex.hpp"
#include "Multicaster.hpp"
#include "Service.hpp" #include "Service.hpp"
#include "SoftwareUpdater.hpp" #include "SoftwareUpdater.hpp"
#include "Buffer.hpp" #include "Buffer.hpp"
@ -113,7 +112,6 @@ struct _NodeImpl
delete renv.topology; renv.topology = (Topology *)0; // now we no longer need routing info delete renv.topology; renv.topology = (Topology *)0; // now we no longer need routing info
delete renv.sm; renv.sm = (SocketManager *)0; // close all sockets delete renv.sm; renv.sm = (SocketManager *)0; // close all sockets
delete renv.sw; renv.sw = (Switch *)0; // order matters less from here down delete renv.sw; renv.sw = (Switch *)0; // order matters less from here down
delete renv.mc; renv.mc = (Multicaster *)0;
delete renv.antiRec; renv.antiRec = (AntiRecursion *)0; delete renv.antiRec; renv.antiRec = (AntiRecursion *)0;
delete renv.http; renv.http = (HttpClient *)0; delete renv.http; renv.http = (HttpClient *)0;
delete renv.prng; renv.prng = (CMWC4096 *)0; delete renv.prng; renv.prng = (CMWC4096 *)0;
@ -382,7 +380,6 @@ Node::ReasonForTermination Node::run()
_r->http = new HttpClient(); _r->http = new HttpClient();
_r->antiRec = new AntiRecursion(); _r->antiRec = new AntiRecursion();
_r->mc = new Multicaster();
_r->sw = new Switch(_r); _r->sw = new Switch(_r);
_r->sm = new SocketManager(impl->udpPort,impl->tcpPort,&_CBztTraffic,_r); _r->sm = new SocketManager(impl->udpPort,impl->tcpPort,&_CBztTraffic,_r);
_r->topology = new Topology(_r,Utils::fileExists((_r->homePath + ZT_PATH_SEPARATOR_S + "iddb.d").c_str())); _r->topology = new Topology(_r,Utils::fileExists((_r->homePath + ZT_PATH_SEPARATOR_S + "iddb.d").c_str()));

View File

@ -43,11 +43,13 @@ const char *Packet::verbString(Verb v)
case VERB_RENDEZVOUS: return "RENDEZVOUS"; case VERB_RENDEZVOUS: return "RENDEZVOUS";
case VERB_FRAME: return "FRAME"; case VERB_FRAME: return "FRAME";
case VERB_EXT_FRAME: return "EXT_FRAME"; case VERB_EXT_FRAME: return "EXT_FRAME";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME"; case VERB_P5_MULTICAST_FRAME: return "P5_MULTICAST_FRAME";
case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE"; case VERB_MULTICAST_LIKE: return "MULTICAST_LIKE";
case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE"; case VERB_NETWORK_MEMBERSHIP_CERTIFICATE: return "NETWORK_MEMBERSHIP_CERTIFICATE";
case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST"; case VERB_NETWORK_CONFIG_REQUEST: return "NETWORK_CONFIG_REQUEST";
case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH"; case VERB_NETWORK_CONFIG_REFRESH: return "NETWORK_CONFIG_REFRESH";
case VERB_MULTICAST_LONELY: return "MULTICAST_LONELY";
case VERB_MULTICAST_FRAME: return "MULTICAST_FRAME";
} }
return "(unknown)"; return "(unknown)";
} }
@ -64,6 +66,7 @@ const char *Packet::errorString(ErrorCode e)
case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION"; case ERROR_UNSUPPORTED_OPERATION: return "UNSUPPORTED_OPERATION";
case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE"; case ERROR_NEED_MEMBERSHIP_CERTIFICATE: return "NEED_MEMBERSHIP_CERTIFICATE";
case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED"; case ERROR_NETWORK_ACCESS_DENIED_: return "NETWORK_ACCESS_DENIED";
case ERROR_UNWANTED_MULTICAST: return "UNWANTED_MULTICAST";
} }
return "(unknown)"; return "(unknown)";
} }

View File

@ -527,7 +527,7 @@ public:
*/ */
VERB_EXT_FRAME = 7, VERB_EXT_FRAME = 7,
/* A multicast frame: /* A multicast frame [old multicast protocol, deprecated]:
* <[2] 16-bit propagation depth or 0xffff for "do not forward"> * <[2] 16-bit propagation depth or 0xffff for "do not forward">
* <[320] propagation FIFO> * <[320] propagation FIFO>
* <[1024] propagation bloom filter> * <[1024] propagation bloom filter>
@ -593,7 +593,7 @@ public:
* ERROR may be generated if a membership certificate is needed for a * ERROR may be generated if a membership certificate is needed for a
* closed network. Payload will be network ID. * closed network. Payload will be network ID.
*/ */
VERB_MULTICAST_FRAME = 8, VERB_P5_MULTICAST_FRAME = 8,
/* Announce interest in multicast group(s): /* Announce interest in multicast group(s):
* <[8] 64-bit network ID> * <[8] 64-bit network ID>
@ -657,7 +657,60 @@ public:
* It does not generate an OK or ERROR message, and is treated only as * It does not generate an OK or ERROR message, and is treated only as
* a hint to refresh now. * a hint to refresh now.
*/ */
VERB_NETWORK_CONFIG_REFRESH = 12 VERB_NETWORK_CONFIG_REFRESH = 12,
/* Request endpoints for multicast distribution:
* <[1] flags>
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[2] 16-bit (suggested) max number of multicast peers desired>
* [<[...] network membership certificate (optional)>]
*
* Flags are:
* 0x01 - network membership certificate is included
*
* This message asks a peer for additional known endpoints that have
* LIKEd a given multicast group. It's sent when the sender wishes
* to send multicast but does not have the desired number of recipient
* peers. (Hence it is "lonely." :)
*
* OK response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
* <[2] 16-bit total number of known members in this multicast group>
* <[2] 16-bit number of members enumerated in this packet>
* <[...] series of 5-byte ZeroTier addresses of enumerated members>
*
* ERROR response payload:
* <[8] 64-bit network ID>
* <[6] MAC address of multicast group being queried>
* <[4] 32-bit ADI for multicast group being queried>
*
* ERRORs are optional and are only generated if permission is denied,
* certificate of membership is out of date, etc.
*/
VERB_MULTICAST_LONELY = 13,
/* Multicast frame:
* <[1] flags (currently unused, must be 0)>
* <[4] 32-bit multicast ADI (note that this is out of order here -- it precedes MAC)>
* <[6] destination MAC or all zero for destination node>
* <[6] source MAC or all zero for node of origin>
* <[2] 16-bit ethertype>
* <[...] ethernet payload>
*
* This is similar to EXT_FRAME but carries a multicast, and is sent
* out to recipients on a multicast list.
*
* OK is not generated.
*
* ERROR response payload:
* <[6] multicast group MAC>
* <[4] 32-bit multicast group ADI>
*/
VERB_MULTICAST_FRAME = 14
}; };
/** /**
@ -687,7 +740,10 @@ public:
ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6, ERROR_NEED_MEMBERSHIP_CERTIFICATE = 6,
/* Tried to join network, but you're not a member */ /* Tried to join network, but you're not a member */
ERROR_NETWORK_ACCESS_DENIED_ = 7 /* extra _ to avoid Windows name conflict */ ERROR_NETWORK_ACCESS_DENIED_ = 7, /* extra _ to avoid Windows name conflict */
/* Multicasts to this group are not wanted */
ERROR_UNWANTED_MULTICAST = 8
}; };
/** /**

View File

@ -42,7 +42,6 @@ class Topology;
class CMWC4096; class CMWC4096;
class Service; class Service;
class Node; class Node;
class Multicaster;
class SoftwareUpdater; class SoftwareUpdater;
class SocketManager; class SocketManager;
class AntiRecursion; class AntiRecursion;
@ -79,7 +78,6 @@ public:
prng((CMWC4096 *)0), prng((CMWC4096 *)0),
http((HttpClient *)0), http((HttpClient *)0),
antiRec((AntiRecursion *)0), antiRec((AntiRecursion *)0),
mc((Multicaster *)0),
sw((Switch *)0), sw((Switch *)0),
sm((SocketManager *)0), sm((SocketManager *)0),
topology((Topology *)0), topology((Topology *)0),
@ -129,7 +127,6 @@ public:
CMWC4096 *prng; CMWC4096 *prng;
HttpClient *http; HttpClient *http;
AntiRecursion *antiRec; AntiRecursion *antiRec;
Multicaster *mc;
Switch *sw; Switch *sw;
SocketManager *sm; SocketManager *sm;
Topology *topology; Topology *topology;

View File

@ -151,6 +151,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
TRACE("%s: MULTICAST %s -> %s %s %d",network->tapDeviceName().c_str(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size()); TRACE("%s: MULTICAST %s -> %s %s %d",network->tapDeviceName().c_str(),from.toString().c_str(),mg.toString().c_str(),etherTypeName(etherType),(int)data.size());
/* old P5 multicast algorithm
const unsigned int mcid = ++_multicastIdCounter & 0xffffff; const unsigned int mcid = ++_multicastIdCounter & 0xffffff;
const uint16_t bloomNonce = (uint16_t)(_r->prng->next32() & 0xffff); // doesn't need to be cryptographically strong const uint16_t bloomNonce = (uint16_t)(_r->prng->next32() & 0xffff); // doesn't need to be cryptographically strong
unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM]; unsigned char bloom[ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM];
@ -226,6 +227,7 @@ void Switch::onLocalEthernet(const SharedPtr<Network> &network,const MAC &from,c
outp.compress(); outp.compress();
send(outp,true); send(outp,true);
} }
*/
return; return;
} }

View File

@ -44,7 +44,6 @@
#include "Array.hpp" #include "Array.hpp"
#include "Network.hpp" #include "Network.hpp"
#include "SharedPtr.hpp" #include "SharedPtr.hpp"
#include "Multicaster.hpp"
#include "PacketDecoder.hpp" #include "PacketDecoder.hpp"
#include "Socket.hpp" #include "Socket.hpp"