2014-09-19 01:28:14 +00:00
/*
2015-02-17 21:11:34 +00:00
* ZeroTier One - Network Virtualization Everywhere
2016-01-12 22:04:55 +00:00
* Copyright ( C ) 2011 - 2016 ZeroTier , Inc . https : //www.zerotier.com/
2014-09-19 01:28:14 +00:00
*
* 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/>.
*/
# include <algorithm>
# include "Constants.hpp"
2014-10-10 00:58:31 +00:00
# include "RuntimeEnvironment.hpp"
2014-09-25 22:57:43 +00:00
# include "SharedPtr.hpp"
2014-09-24 21:02:16 +00:00
# include "Multicaster.hpp"
2014-09-19 01:28:14 +00:00
# include "Topology.hpp"
2014-09-25 22:08:29 +00:00
# include "Switch.hpp"
# include "Packet.hpp"
2014-09-25 22:57:43 +00:00
# include "Peer.hpp"
2014-10-04 20:46:29 +00:00
# include "C25519.hpp"
2014-09-26 21:18:25 +00:00
# include "CertificateOfMembership.hpp"
2015-10-27 22:00:16 +00:00
# include "Node.hpp"
2014-09-19 01:28:14 +00:00
namespace ZeroTier {
2014-10-01 21:05:25 +00:00
Multicaster : : Multicaster ( const RuntimeEnvironment * renv ) :
2015-08-27 23:17:21 +00:00
RR ( renv ) ,
_groups ( 1024 ) ,
_groups_m ( )
2014-09-19 01:28:14 +00:00
{
}
2014-09-24 21:02:16 +00:00
Multicaster : : ~ Multicaster ( )
2014-09-19 01:28:14 +00:00
{
}
2014-11-21 18:50:27 +00:00
void Multicaster : : addMultiple ( uint64_t now , uint64_t nwid , const MulticastGroup & mg , const void * addresses , unsigned int count , unsigned int totalKnown )
2014-10-29 22:26:32 +00:00
{
const unsigned char * p = ( const unsigned char * ) addresses ;
const unsigned char * e = p + ( 5 * count ) ;
Mutex : : Lock _l ( _groups_m ) ;
2015-08-27 23:17:21 +00:00
MulticastGroupStatus & gs = _groups [ Multicaster : : Key ( nwid , mg ) ] ;
2014-10-29 22:26:32 +00:00
while ( p ! = e ) {
2014-11-21 18:50:27 +00:00
_add ( now , nwid , mg , gs , Address ( p , 5 ) ) ;
2014-10-29 22:26:32 +00:00
p + = 5 ;
}
}
2015-07-07 18:49:38 +00:00
void Multicaster : : remove ( uint64_t nwid , const MulticastGroup & mg , const Address & member )
{
Mutex : : Lock _l ( _groups_m ) ;
2015-08-27 23:17:21 +00:00
MulticastGroupStatus * s = _groups . get ( Multicaster : : Key ( nwid , mg ) ) ;
if ( s ) {
for ( std : : vector < MulticastGroupMember > : : iterator m ( s - > members . begin ( ) ) ; m ! = s - > members . end ( ) ; + + m ) {
2015-07-07 18:49:38 +00:00
if ( m - > address = = member ) {
2015-08-27 23:17:21 +00:00
s - > members . erase ( m ) ;
2015-07-07 18:49:38 +00:00
break ;
}
}
}
}
2015-11-09 17:45:43 +00:00
unsigned int Multicaster : : gather ( const Address & queryingPeer , uint64_t nwid , const MulticastGroup & mg , Buffer < ZT_PROTO_MAX_PACKET_LENGTH > & appendTo , unsigned int limit ) const
2014-09-30 23:28:25 +00:00
{
unsigned char * p ;
2014-10-10 01:32:05 +00:00
unsigned int added = 0 , i , k , rptr , totalKnown = 0 ;
2014-10-28 22:33:10 +00:00
uint64_t a , picked [ ( ZT_PROTO_MAX_PACKET_LENGTH / 5 ) + 2 ] ;
2014-09-30 23:28:25 +00:00
2014-10-10 01:32:05 +00:00
if ( ! limit )
2014-09-30 23:28:25 +00:00
return 0 ;
2014-10-29 23:24:19 +00:00
else if ( limit > 0xffff )
2014-10-10 01:32:05 +00:00
limit = 0xffff ;
2014-10-29 23:24:19 +00:00
const unsigned int totalAt = appendTo . size ( ) ;
appendTo . addSize ( 4 ) ; // sizeof(uint32_t)
const unsigned int addedAt = appendTo . size ( ) ;
appendTo . addSize ( 2 ) ; // sizeof(uint16_t)
2014-10-10 01:32:05 +00:00
{ // Return myself if I am a member of this group
2015-04-06 22:14:54 +00:00
SharedPtr < Network > network ( RR - > node - > network ( nwid ) ) ;
2015-04-07 02:34:36 +00:00
if ( ( network ) & & ( network - > subscribedToMulticastGroup ( mg , true ) ) ) {
2014-10-10 01:32:05 +00:00
RR - > identity . address ( ) . appendTo ( appendTo ) ;
+ + totalKnown ;
+ + added ;
}
2014-09-30 23:28:25 +00:00
}
2014-10-10 01:32:05 +00:00
Mutex : : Lock _l ( _groups_m ) ;
2014-09-30 23:28:25 +00:00
2015-08-27 23:17:21 +00:00
const MulticastGroupStatus * s = _groups . get ( Multicaster : : Key ( nwid , mg ) ) ;
if ( ( s ) & & ( ! s - > members . empty ( ) ) ) {
totalKnown + = ( unsigned int ) s - > members . size ( ) ;
2014-10-10 01:32:05 +00:00
// Members are returned in random order so that repeated gather queries
// will return different subsets of a large multicast group.
k = 0 ;
2015-08-27 23:17:21 +00:00
while ( ( added < limit ) & & ( k < s - > members . size ( ) ) & & ( ( appendTo . size ( ) + ZT_ADDRESS_LENGTH ) < = ZT_UDP_DEFAULT_PAYLOAD_MTU ) ) {
2015-07-07 17:49:50 +00:00
rptr = ( unsigned int ) RR - > node - > prng ( ) ;
2014-10-28 22:33:10 +00:00
2014-09-30 23:28:25 +00:00
restart_member_scan :
2015-08-27 23:17:21 +00:00
a = s - > members [ rptr % ( unsigned int ) s - > members . size ( ) ] . address . toInt ( ) ;
2014-10-10 01:32:05 +00:00
for ( i = 0 ; i < k ; + + i ) {
if ( picked [ i ] = = a ) {
+ + rptr ;
goto restart_member_scan ;
}
}
picked [ k + + ] = a ;
if ( queryingPeer . toInt ( ) ! = a ) { // do not return the peer that is making the request as a result
p = ( unsigned char * ) appendTo . appendField ( ZT_ADDRESS_LENGTH ) ;
* ( p + + ) = ( unsigned char ) ( ( a > > 32 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( a > > 24 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( a > > 16 ) & 0xff ) ;
* ( p + + ) = ( unsigned char ) ( ( a > > 8 ) & 0xff ) ;
* p = ( unsigned char ) ( a & 0xff ) ;
+ + added ;
2014-09-30 23:28:25 +00:00
}
2014-10-04 01:42:41 +00:00
}
2014-09-30 23:28:25 +00:00
}
2014-10-10 01:32:05 +00:00
appendTo . setAt ( totalAt , ( uint32_t ) totalKnown ) ;
appendTo . setAt ( addedAt , ( uint16_t ) added ) ;
2014-10-10 00:58:31 +00:00
//TRACE("..MC Multicaster::gather() attached %u of %u peers for %.16llx/%s (2)",n,(unsigned int)(gs->second.members.size() - skipped),nwid,mg.toString().c_str());
2014-09-30 23:28:25 +00:00
2014-10-10 01:32:05 +00:00
return added ;
2014-09-30 23:28:25 +00:00
}
2014-10-11 22:49:31 +00:00
std : : vector < Address > Multicaster : : getMembers ( uint64_t nwid , const MulticastGroup & mg , unsigned int limit ) const
2014-10-05 17:34:25 +00:00
{
std : : vector < Address > ls ;
Mutex : : Lock _l ( _groups_m ) ;
2015-08-27 23:17:21 +00:00
const MulticastGroupStatus * s = _groups . get ( Multicaster : : Key ( nwid , mg ) ) ;
if ( ! s )
2014-10-05 17:34:25 +00:00
return ls ;
2015-08-27 23:17:21 +00:00
for ( std : : vector < MulticastGroupMember > : : const_reverse_iterator m ( s - > members . rbegin ( ) ) ; m ! = s - > members . rend ( ) ; + + m ) {
2014-10-11 22:49:31 +00:00
ls . push_back ( m - > address ) ;
if ( ls . size ( ) > = limit )
break ;
2014-10-05 17:34:25 +00:00
}
return ls ;
}
2014-09-30 23:28:25 +00:00
void Multicaster : : send (
unsigned int limit ,
uint64_t now ,
uint64_t nwid ,
2014-10-04 20:15:02 +00:00
const std : : vector < Address > & alwaysSendTo ,
2014-09-30 23:28:25 +00:00
const MulticastGroup & mg ,
const MAC & src ,
unsigned int etherType ,
const void * data ,
unsigned int len )
2014-09-22 20:18:24 +00:00
{
2014-11-21 18:50:27 +00:00
unsigned long idxbuf [ 8194 ] ;
unsigned long * indexes = idxbuf ;
2015-11-02 23:15:20 +00:00
try {
Mutex : : Lock _l ( _groups_m ) ;
MulticastGroupStatus & gs = _groups [ Multicaster : : Key ( nwid , mg ) ] ;
if ( ! gs . members . empty ( ) ) {
// Allocate a memory buffer if group is monstrous
if ( gs . members . size ( ) > ( sizeof ( idxbuf ) / sizeof ( unsigned long ) ) )
indexes = new unsigned long [ gs . members . size ( ) ] ;
// Generate a random permutation of member indexes
for ( unsigned long i = 0 ; i < gs . members . size ( ) ; + + i )
indexes [ i ] = i ;
for ( unsigned long i = ( unsigned long ) gs . members . size ( ) - 1 ; i > 0 ; - - i ) {
unsigned long j = ( unsigned long ) RR - > node - > prng ( ) % ( i + 1 ) ;
unsigned long tmp = indexes [ j ] ;
indexes [ j ] = indexes [ i ] ;
indexes [ i ] = tmp ;
}
2014-11-21 18:50:27 +00:00
}
2014-10-01 21:05:25 +00:00
2015-11-02 23:15:20 +00:00
if ( gs . members . size ( ) > = limit ) {
// Skip queue if we already have enough members to complete the send operation
OutboundMulticast out ;
out . init (
RR ,
now ,
nwid ,
limit ,
1 , // we'll still gather a little from peers to keep multicast list fresh
src ,
mg ,
etherType ,
data ,
len ) ;
unsigned int count = 0 ;
for ( std : : vector < Address > : : const_iterator ast ( alwaysSendTo . begin ( ) ) ; ast ! = alwaysSendTo . end ( ) ; + + ast ) {
if ( * ast ! = RR - > identity . address ( ) ) {
out . sendOnly ( RR , * ast ) ; // optimization: don't use dedup log if it's a one-pass send
if ( + + count > = limit )
break ;
}
2015-07-28 18:43:09 +00:00
}
2014-10-04 05:30:10 +00:00
2015-11-02 23:15:20 +00:00
unsigned long idx = 0 ;
while ( ( count < limit ) & & ( idx < gs . members . size ( ) ) ) {
Address ma ( gs . members [ indexes [ idx + + ] ] . address ) ;
if ( std : : find ( alwaysSendTo . begin ( ) , alwaysSendTo . end ( ) , ma ) = = alwaysSendTo . end ( ) ) {
out . sendOnly ( RR , ma ) ; // optimization: don't use dedup log if it's a one-pass send
+ + count ;
}
2014-11-21 19:27:53 +00:00
}
2015-11-02 23:15:20 +00:00
} else {
unsigned int gatherLimit = ( limit - ( unsigned int ) gs . members . size ( ) ) + 1 ;
if ( ( gs . members . empty ( ) ) | | ( ( now - gs . lastExplicitGather ) > = ZT_MULTICAST_EXPLICIT_GATHER_DELAY ) ) {
gs . lastExplicitGather = now ;
2015-11-11 01:37:38 +00:00
SharedPtr < Peer > explicitGatherPeers [ 2 ] ;
explicitGatherPeers [ 0 ] = RR - > topology - > getBestRoot ( ) ;
2016-06-28 22:49:01 +00:00
const Address nwidc ( Network : : controllerFor ( nwid ) ) ;
if ( nwidc ! = RR - > identity . address ( ) )
explicitGatherPeers [ 1 ] = RR - > topology - > getPeer ( nwidc ) ;
2015-11-11 01:37:38 +00:00
for ( unsigned int k = 0 ; k < 2 ; + + k ) {
const SharedPtr < Peer > & p = explicitGatherPeers [ k ] ;
if ( ! p )
continue ;
//TRACE(">>MC upstream GATHER up to %u for group %.16llx/%s",gatherLimit,nwid,mg.toString().c_str());
Packet outp ( p - > address ( ) , RR - > identity . address ( ) , Packet : : VERB_MULTICAST_GATHER ) ;
2015-11-02 23:15:20 +00:00
outp . append ( nwid ) ;
2016-08-09 00:33:26 +00:00
outp . append ( ( uint8_t ) 0x00 ) ;
2015-11-02 23:15:20 +00:00
mg . mac ( ) . appendTo ( outp ) ;
outp . append ( ( uint32_t ) mg . adi ( ) ) ;
outp . append ( ( uint32_t ) gatherLimit ) ;
2016-08-09 22:45:26 +00:00
RR - > sw - > send ( outp , true ) ;
2015-11-02 23:15:20 +00:00
}
gatherLimit = 0 ;
2014-11-21 19:27:53 +00:00
}
2014-10-28 22:33:10 +00:00
2015-11-02 23:15:20 +00:00
gs . txQueue . push_back ( OutboundMulticast ( ) ) ;
OutboundMulticast & out = gs . txQueue . back ( ) ;
out . init (
RR ,
now ,
nwid ,
limit ,
gatherLimit ,
src ,
mg ,
etherType ,
data ,
len ) ;
unsigned int count = 0 ;
for ( std : : vector < Address > : : const_iterator ast ( alwaysSendTo . begin ( ) ) ; ast ! = alwaysSendTo . end ( ) ; + + ast ) {
if ( * ast ! = RR - > identity . address ( ) ) {
out . sendAndLog ( RR , * ast ) ;
if ( + + count > = limit )
break ;
}
2015-07-28 18:43:09 +00:00
}
2014-10-04 05:30:10 +00:00
2015-11-02 23:15:20 +00:00
unsigned long idx = 0 ;
while ( ( count < limit ) & & ( idx < gs . members . size ( ) ) ) {
Address ma ( gs . members [ indexes [ idx + + ] ] . address ) ;
if ( std : : find ( alwaysSendTo . begin ( ) , alwaysSendTo . end ( ) , ma ) = = alwaysSendTo . end ( ) ) {
out . sendAndLog ( RR , ma ) ;
+ + count ;
}
2014-11-21 19:27:53 +00:00
}
}
2015-11-02 23:15:20 +00:00
} catch ( . . . ) { } // this is a sanity check to catch any failures and make sure indexes[] still gets deleted
2014-10-04 20:46:29 +00:00
2014-11-26 21:14:18 +00:00
// Free allocated memory buffer if any
2014-11-21 19:27:53 +00:00
if ( indexes ! = idxbuf )
delete [ ] indexes ;
2014-09-22 20:18:24 +00:00
}
2014-10-01 21:05:25 +00:00
void Multicaster : : clean ( uint64_t now )
2014-09-22 20:18:24 +00:00
{
2014-09-23 17:26:30 +00:00
Mutex : : Lock _l ( _groups_m ) ;
2015-08-27 23:17:21 +00:00
Multicaster : : Key * k = ( Multicaster : : Key * ) 0 ;
MulticastGroupStatus * s = ( MulticastGroupStatus * ) 0 ;
Hashtable < Multicaster : : Key , MulticastGroupStatus > : : Iterator mm ( _groups ) ;
while ( mm . next ( k , s ) ) {
for ( std : : list < OutboundMulticast > : : iterator tx ( s - > txQueue . begin ( ) ) ; tx ! = s - > txQueue . end ( ) ; ) {
2015-06-26 21:26:35 +00:00
if ( ( tx - > expired ( now ) ) | | ( tx - > atLimit ( ) ) )
2015-08-27 23:17:21 +00:00
s - > txQueue . erase ( tx + + ) ;
2015-06-26 21:26:35 +00:00
else + + tx ;
2014-09-25 22:08:29 +00:00
}
2014-11-24 18:05:16 +00:00
unsigned long count = 0 ;
{
2015-08-27 23:17:21 +00:00
std : : vector < MulticastGroupMember > : : iterator reader ( s - > members . begin ( ) ) ;
2014-11-24 18:05:16 +00:00
std : : vector < MulticastGroupMember > : : iterator writer ( reader ) ;
2015-08-27 23:17:21 +00:00
while ( reader ! = s - > members . end ( ) ) {
2014-11-24 18:05:16 +00:00
if ( ( now - reader - > timestamp ) < ZT_MULTICAST_LIKE_EXPIRE ) {
* writer = * reader ;
+ + writer ;
+ + count ;
}
+ + reader ;
2014-09-19 01:28:14 +00:00
}
}
if ( count ) {
2015-08-27 23:17:21 +00:00
s - > members . resize ( count ) ;
} else if ( s - > txQueue . empty ( ) ) {
_groups . erase ( * k ) ;
2014-10-29 00:25:34 +00:00
} else {
2015-08-27 23:17:21 +00:00
s - > members . clear ( ) ;
2014-10-29 00:25:34 +00:00
}
2014-09-25 22:08:29 +00:00
}
}
2014-11-21 18:50:27 +00:00
void Multicaster : : _add ( uint64_t now , uint64_t nwid , const MulticastGroup & mg , MulticastGroupStatus & gs , const Address & member )
2014-09-25 22:08:29 +00:00
{
// assumes _groups_m is locked
2014-09-26 05:08:52 +00:00
2014-10-04 01:42:41 +00:00
// Do not add self -- even if someone else returns it
if ( member = = RR - > identity . address ( ) )
return ;
2014-09-25 22:57:43 +00:00
for ( std : : vector < MulticastGroupMember > : : iterator m ( gs . members . begin ( ) ) ; m ! = gs . members . end ( ) ; + + m ) {
if ( m - > address = = member ) {
m - > timestamp = now ;
return ;
}
2014-09-19 01:28:14 +00:00
}
2014-09-26 05:08:52 +00:00
2014-11-21 18:50:27 +00:00
gs . members . push_back ( MulticastGroupMember ( member , now ) ) ;
2014-10-01 21:05:25 +00:00
2014-10-10 01:32:05 +00:00
//TRACE("..MC %s joined multicast group %.16llx/%s via %s",member.toString().c_str(),nwid,mg.toString().c_str(),((learnedFrom) ? learnedFrom.toString().c_str() : "(direct)"));
2014-10-10 00:58:31 +00:00
2015-06-26 21:26:35 +00:00
for ( std : : list < OutboundMulticast > : : iterator tx ( gs . txQueue . begin ( ) ) ; tx ! = gs . txQueue . end ( ) ; ) {
if ( tx - > atLimit ( ) )
gs . txQueue . erase ( tx + + ) ;
else {
2014-11-26 21:39:57 +00:00
tx - > sendIfNew ( RR , member ) ;
2015-06-26 21:26:35 +00:00
if ( tx - > atLimit ( ) )
gs . txQueue . erase ( tx + + ) ;
else + + tx ;
2014-10-05 17:34:25 +00:00
}
2014-10-01 21:05:25 +00:00
}
2014-09-19 01:28:14 +00:00
}
} // namespace ZeroTier