diff options
author | Adam Ierymenko <adam.ierymenko@gmail.com> | 2013-09-21 16:46:00 -0400 |
---|---|---|
committer | Adam Ierymenko <adam.ierymenko@gmail.com> | 2013-09-21 16:46:00 -0400 |
commit | 770fbaf4b276f9d4dd616941ed7460398f70c634 (patch) | |
tree | 081ed5d25e5a96211e53e3b147f7c6e151805dba /node/Multicaster.hpp | |
parent | 64c9c2e06b03be03a62d1dd31600914fd84b0063 (diff) | |
download | infinitytier-770fbaf4b276f9d4dd616941ed7460398f70c634.tar.gz infinitytier-770fbaf4b276f9d4dd616941ed7460398f70c634.zip |
New multicast algorithm work in progress...
Diffstat (limited to 'node/Multicaster.hpp')
-rw-r--r-- | node/Multicaster.hpp | 361 |
1 files changed, 79 insertions, 282 deletions
diff --git a/node/Multicaster.hpp b/node/Multicaster.hpp index c7e34d2f..40b7b468 100644 --- a/node/Multicaster.hpp +++ b/node/Multicaster.hpp @@ -31,349 +31,146 @@ #include <stdint.h> #include <string.h> -#include <utility> -#include <algorithm> #include <stdexcept> #include <map> -#include <set> #include <vector> -#include <string> +#include <set> +#include <algorithm> #include "Constants.hpp" -#include "Buffer.hpp" -#include "Packet.hpp" +#include "Mutex.hpp" #include "MulticastGroup.hpp" #include "Utils.hpp" -#include "MAC.hpp" #include "Address.hpp" -#include "SharedPtr.hpp" -#include "BloomFilter.hpp" -#include "Identity.hpp" -#include "CMWC4096.hpp" -#include "C25519.hpp" namespace ZeroTier { /** - * Multicast propagation engine - * - * This is written as a generic class so that it can be mocked and tested - * in simulation. It also always takes 'now' as an argument, permitting - * running in simulated time. - * - * This does not handle network permission or rate limiting, only the - * propagation algorithm. + * Multicast propagation algorithm */ class Multicaster { public: - /** - * Simple bit field bloom filter included with multicast frame packets - */ - typedef BloomFilter<ZT_PROTO_VERB_MULTICAST_FRAME_BLOOM_FILTER_SIZE_BITS> MulticastBloomFilter; - - Multicaster() - throw() - { - memset(_multicastHistory,0,sizeof(_multicastHistory)); - _multicastHistoryPtr = 0; - } - - /** - * Generate a signature of a multicast packet using an identity - * - * @param id Identity to sign with (must have secret key portion) - * @param nwid Network ID - * @param from MAC address of sender - * @param to Multicast group - * @param etherType 16-bit ethernet type - * @param data Ethernet frame data - * @param len Length of frame - * @return Signature of packet data and attributes - * @throws std::runtime_error Cannot sign, e.g. identity has no private key - */ - static inline C25519::Signature signMulticastPacket(const Identity &id,uint64_t nwid,const MAC &from,const MulticastGroup &to,unsigned int etherType,const void *data,unsigned int len) - throw(std::runtime_error) - { - char tmp[65536]; - void *tmp2 = (void *)tmp; - *((uint64_t *)tmp2) = Utils::hton((uint64_t)nwid); - memcpy(tmp + 8,from.data,6); - memcpy(tmp + 14,to.mac().data,6); - *((uint32_t *)(tmp + 20)) = Utils::hton((uint32_t)to.adi()); - *((uint16_t *)(tmp + 24)) = Utils::hton((uint16_t)etherType); - memcpy(tmp + 26,data,std::min((unsigned int)(sizeof(tmp) - 26),len)); // min() is a sanity check here, no packet is that big - return id.sign(tmp,len + 26); - } - - /** - * Verify a signature from a multicast packet - * - * @param id Identity of original signer - * @param nwid Network ID - * @param from MAC address of sender - * @param to Multicast group - * @param etherType 16-bit ethernet type - * @param data Ethernet frame data - * @param len Length of frame - * @param signature Signature - * @param siglen Length of signature in bytes - * @return True if signature verification was successful - */ - static bool verifyMulticastPacket(const Identity &id,uint64_t nwid,const MAC &from,const MulticastGroup &to,unsigned int etherType,const void *data,unsigned int len,const void *signature,unsigned int siglen) - { - char tmp[65536]; - void *tmp2 = (void *)tmp; - *((uint64_t *)tmp2) = Utils::hton(nwid); - memcpy(tmp + 8,from.data,6); - memcpy(tmp + 14,to.mac().data,6); - *((uint32_t *)(tmp + 20)) = Utils::hton(to.adi()); - *((uint16_t *)(tmp + 24)) = Utils::hton((uint16_t)etherType); - memcpy(tmp + 26,data,std::min((unsigned int)(sizeof(tmp) - 26),len)); // min() is a sanity check here, no packet is that big - return id.verify(tmp,len + 26,signature,siglen); - } + Multicaster() {} /** - * Compute the CRC64 code for multicast deduplication + * Add or renew a peer's subscription to a multicast group * - * @param nwid Network ID - * @param from Sender MAC - * @param to Destination multicast group - * @param etherType Ethernet frame type - * @param payload Multicast frame data - * @param len Length of frame + * @param a Address that LIKEd + * @param mg Multicast group + * @param now Current time */ - static inline uint64_t computeMulticastDedupCrc( - uint64_t nwid, - const MAC &from, - const MulticastGroup &to, - unsigned int etherType, - const void *payload, - unsigned int len) - throw() + inline void likesGroup(const Address &a,const MulticastGroup &mg,uint64_t now) { - // This CRC is only used locally, so byte order issues and - // such don't matter. It can also be changed without protocol - // impact. - uint64_t crc = Utils::crc64(0,from.data,6); - crc = Utils::crc64(crc,to.mac().data,6); - crc ^= (uint64_t)to.adi(); - crc ^= (uint64_t)etherType; - crc = Utils::crc64(crc,payload,len); - crc ^= nwid; // also include network ID in CRC - return crc; + Mutex::Lock _l(_lock); + std::map< Address,_PeerInfo >::iterator pi(_peers.find(a)); + if (pi == _peers.end()) { + pi = _peers.insert(std::pair< Address,_PeerInfo >(a,_PeerInfo())).first; + _proximity.push_front(a); + pi->second.proximitySlot = _proximity.begin(); + } + pi->second.groups[mg] = now; } /** - * Check multicast history to see if this is a duplicate + * Bring a peer closer in terms of propagation priority * - * @param crc Multicast CRC + * @param a Address to bring closer (e.g. due to unicast message) * @param now Current time - * @return True if this appears to be a duplicate to within history expiration time */ - inline bool checkDuplicate(uint64_t crc,uint64_t now) const - throw() + inline void bringCloser(const Address &a) { - for(unsigned int i=0;i<ZT_MULTICAST_DEDUP_HISTORY_LENGTH;++i) { - if ((_multicastHistory[i][0] == crc)&&((now - _multicastHistory[i][1]) <= ZT_MULTICAST_DEDUP_HISTORY_EXPIRE)) - return true; + Mutex::Lock _l(_lock); + std::map< Address,_PeerInfo >::iterator pi(_peers.find(a)); + if (pi != _peers.end()) { + if (pi->second.proximitySlot != _proximity.begin()) + _proximity.splice(_proximity.begin(),_proximity,pi->second.proximitySlot); } - return false; } /** - * Add a multicast CRC to the multicast deduplication history + * Indicate that a peer reported that it GOT a multicast + * + * This only happens on magnet nodes for a propagation. * - * @param crc Multicast CRC + * @param mcGuid Multicast GUID + * @param peer Peer that GOT multicast * @param now Current time */ - inline void addToDedupHistory(uint64_t crc,uint64_t now) - throw() + inlien void got(const Address &peer,uint64_t mcGuid,uint64_t now) { - unsigned int mhi = ++_multicastHistoryPtr % ZT_MULTICAST_DEDUP_HISTORY_LENGTH; - _multicastHistory[mhi][0] = crc; - _multicastHistory[mhi][1] = now; + Mutex::Lock _l(_lock); + std::pair< uint64_t,std::set<Address> > &g = _got[mcGuid]; + g.first = now; + g.second.insert(peer); } /** - * Update the most recent LIKE time for an address in a given multicast group on a given network - * - * @param nwid Network ID - * @param mg Multicast group - * @param addr Address that likes group on given network - * @param now Current timestamp + * Erase entries for expired LIKEs */ - inline void likesMulticastGroup(const uint64_t nwid,const MulticastGroup &mg,const Address &addr,const uint64_t now) + inline void clean(uint64_t now) { - Mutex::Lock _l(_multicastMemberships_m); - std::vector<MulticastMembership> &memberships = _multicastMemberships[MulticastChannel(nwid,mg)]; - for(std::vector<MulticastMembership>::iterator mm(memberships.begin());mm!=memberships.end();++mm) { - if (mm->first == addr) { - mm->second = now; - return; - } + Mutex::Lock _l(_lock); + + for(std::map< uint64_t,std::pair< uint64_t,std::set<Address> > >::iterator g(_got.begin());g!=_got.end();) { + if ((now - g->second.first) > ZT_MULTICAST_MAGNET_STATE_EXPIRE) + _got.erase(g++); + else ++g; } - memberships.push_back(MulticastMembership(addr,now)); - } - /** - * Choose peers for multicast propagation via random selection - * - * @param prng Random source - * @param topology Topology object or mock thereof - * @param nwid Network ID - * @param mg Multicast group - * @param originalSubmitter Original submitter of multicast message to network - * @param upstream Address from which message originated, or null (0) address if none - * @param bf Bloom filter, updated in place with sums of addresses in chosen peers and/or decay - * @param max Maximum number of peers to pick - * @param peers Array of objects of type P to fill with up to [max] peers - * @param now Current timestamp - * @return Number of peers actually stored in peers array - * @tparam T Type of topology, which is Topology in running code or a mock in simulation - * @tparam P Type of peers, which is SharedPtr<Peer> in running code or a mock in simulation (mock must behave like a pointer type) - */ - template<typename T,typename P> - inline unsigned int pickRandomPropagationPeers( - CMWC4096 &prng, - T &topology, - uint64_t nwid, - const MulticastGroup &mg, - const Address &originalSubmitter, - const Address &upstream, - MulticastBloomFilter &bf, - unsigned int max, - P *peers, - uint64_t now) - { - unsigned int chosen = 0; - Mutex::Lock _l(_multicastMemberships_m); - std::map< MulticastChannel,std::vector<MulticastMembership> >::iterator mm(_multicastMemberships.find(MulticastChannel(nwid,mg))); - if ((mm != _multicastMemberships.end())&&(!mm->second.empty())) { - for(unsigned int stries=0,stmax=(max*10);((stries<stmax)&&(chosen < max));++stries) { - MulticastMembership &m = mm->second[prng.next32() % mm->second.size()]; - unsigned int sum = m.first.sum(); - if ( - ((now - m.second) < ZT_MULTICAST_LIKE_EXPIRE)&& /* LIKE is not expired */ - (!bf.contains(sum))&& /* Not in propagation bloom */ - (m.first != originalSubmitter)&& /* Not the original submitter */ - (m.first != upstream) ) { /* Not where the frame came from */ - P peer(topology.getPeer(m.first)); - if (peer) { - unsigned int chk = 0; - while (chk < chosen) { - if (peers[chk] == peer) - break; - ++chk; - } - if (chk == chosen) { /* not already picked */ - peers[chosen++] = peer; - bf.set(sum); - } - } - } + for(std::map< Address,_PeerInfo >::iterator pi(_peers.begin());pi!=_peers.end();) { + for(std::map< MulticastGroup,uint64_t >::iterator g(pi->second.groups.begin());g!=pi->second.groups.end();) { + if ((now - g->second) > ZT_MULTICAST_LIKE_EXPIRE) + pi->second.groups.erase(g++); + else ++g; } + if (pi->second.groups.empty()) { + _proximity.erase(pi->second.proximitySlot); + _peers.erase(pi++); + } else ++pi; } - return chosen; } /** - * Choose peers for multicast propagation via implicit social switching + * Pick next hops for a multicast by proximity * - * @param prng Random source - * @param topology Topology object or mock thereof - * @param nwid Network ID * @param mg Multicast group - * @param originalSubmitter Original submitter of multicast message to network - * @param upstream Address from which message originated, or null (0) address if none - * @param bf Bloom filter, updated in place with sums of addresses in chosen peers and/or decay - * @param max Maximum number of peers to pick - * @param peers Array of objects of type P to fill with up to [max] peers - * @param now Current timestamp - * @return Number of peers actually stored in peers array - * @tparam T Type of topology, which is Topology in running code or a mock in simulation - * @tparam P Type of peers, which is SharedPtr<Peer> in running code or a mock in simulation (mock must behave like a pointer type) + * @param mcGuid Multicast message GUID (signer and signer unique ID) + * @param nextHopFunc Function to call for each address, search stops if it returns false */ - template<typename T,typename P> - inline unsigned int pickSocialPropagationPeers( - CMWC4096 &prng, - T &topology, - uint64_t nwid, - const MulticastGroup &mg, - const Address &originalSubmitter, - const Address &upstream, - MulticastBloomFilter &bf, - unsigned int max, - P *peers, - uint64_t now) + template<typename F> + inline void getNextHops(const MulticastGroup &mg,uint64_t mcGuid,F nextHopFunc) { - typename std::set< P,_PeerPropagationPrioritySortOrder<P> > toConsider; - - /* Pick up to ZT_MULTICAST_PICK_MAX_SAMPLE_SIZE peers that meet - * our minimal criteria for this multicast group and place them - * into a set that is sorted in descending order of time of most - * recent unicast frame transfer (implicit social ordering). */ - { - Mutex::Lock _l(_multicastMemberships_m); - std::map< MulticastChannel,std::vector<MulticastMembership> >::iterator mm(_multicastMemberships.find(MulticastChannel(nwid,mg))); - if ((mm != _multicastMemberships.end())&&(!mm->second.empty())) { - for(unsigned int stries=0,stmax=(max*10);stries<stmax;++stries) { - MulticastMembership &m = mm->second[prng.next32() % mm->second.size()]; - if ( - ((now - m.second) < ZT_MULTICAST_LIKE_EXPIRE)&& /* LIKE is not expired */ - (!bf.contains(m.first.sum()))&& /* Not in propagation bloom */ - (m.first != originalSubmitter)&& /* Not the original submitter */ - (m.first != upstream) ) { /* Not where the frame came from */ - P peer(topology.getPeer(m.first)); - if (peer) - toConsider.insert(peer); /* Consider propagating to this peer */ - } - } + Mutex::Lock _l(_lock); + std::map< uint64_t,std::pair< uint64_t,std::set< Address > > > g(_got.find(mcGuid)); + for(std::list< Address >::iterator a(_proximity.begin());a!=_proximity.end();++a) { + if (((g == _got.end())||(!g->second.second.count(*a)))&&(_peers.find(*a)->second.groups.count(mg))) { + if (!nextHopFunc(*a)) + break; } } - - /* The first peers in toConsider will be the "best" */ - unsigned int chosen = 0; - for(typename std::set< P,_PeerPropagationPrioritySortOrder<P> >::iterator i(toConsider.begin());((i!=toConsider.end())&&(chosen < max));++i) - bf.set((peers[chosen++] = *i)->address().sum()); - - /* Tack on a supernode if we have no next hops */ - if (!chosen) { - Address exclude[1]; - exclude[0] = originalSubmitter; // if it came from a supernode, don't boomerang - P peer = topology.getBestSupernode(exclude,1,true); - if (peer) - peers[chosen++] = peer; - } - - return chosen; } private: - // Sort order for chosen propagation peers - template<typename P> - struct _PeerPropagationPrioritySortOrder - { - inline bool operator()(const P &p1,const P &p2) const - { - return (p1->lastUnicastFrame() > p2->lastUnicastFrame()); - } - }; + // GOTs by multicast GUID: time of last GOT, addresses that GOT + std::map< uint64_t,std::pair< uint64_t,std::set< Address > > > _got; - // ring buffer: [0] - CRC, [1] - timestamp - uint64_t _multicastHistory[ZT_MULTICAST_DEDUP_HISTORY_LENGTH][2]; - volatile unsigned int _multicastHistoryPtr; + // Peer proximity ordering + std::list< Address > _proximity; - // A multicast channel, essentially a pub/sub channel. It consists of a - // network ID and a multicast group within that network. - typedef std::pair<uint64_t,MulticastGroup> MulticastChannel; + struct _PeerInfo + { + // Groups and time of last LIKE for each group + std::map< MulticastGroup,uint64_t > groups; + + // Peer's slot in _proximity + std::list< Address >::iterator proximitySlot; + }; - // A membership in a multicast channel, an address and time of last LIKE - typedef std::pair<Address,uint64_t> MulticastMembership; + // Time of last LIKE for each address's group subscriptions + std::map< Address,_PeerInfo > _peers; - // Network : MulticastGroup -> vector<Address : time of last LIKE> - std::map< MulticastChannel,std::vector<MulticastMembership> > _multicastMemberships; - Mutex _multicastMemberships_m; + Mutex _lock; }; } // namespace ZeroTier |