2013-07-04 20:56:19 +00:00
/*
* ZeroTier One - Global Peer to Peer Ethernet
2014-02-16 20:40:22 +00:00
* Copyright ( C ) 2011 - 2014 ZeroTier Networks LLC
2013-07-04 20:56:19 +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/>.
*
* - -
*
* 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 <stdlib.h>
2013-08-12 20:57:34 +00:00
2013-07-04 20:56:19 +00:00
# include <algorithm>
# include <utility>
# include <stdexcept>
2013-08-13 01:25:36 +00:00
# include "Constants.hpp"
# ifdef __WINDOWS__
# include <WinSock2.h>
# include <Windows.h>
# endif
2013-07-04 20:56:19 +00:00
# include "Switch.hpp"
# include "Node.hpp"
# include "EthernetTap.hpp"
# include "InetAddress.hpp"
# include "Topology.hpp"
# include "RuntimeEnvironment.hpp"
# include "Peer.hpp"
# include "NodeConfig.hpp"
2013-09-27 20:03:13 +00:00
# include "CMWC4096.hpp"
2014-04-10 21:22:25 +00:00
# include "AntiRecursion.hpp"
2013-07-04 20:56:19 +00:00
# include "../version.h"
namespace ZeroTier {
Switch : : Switch ( const RuntimeEnvironment * renv ) :
2013-09-27 20:03:13 +00:00
_r ( renv ) ,
2014-04-10 23:30:15 +00:00
_lastBeacon ( 0 ) ,
2013-09-27 20:03:13 +00:00
_multicastIdCounter ( ( unsigned int ) renv - > prng - > next32 ( ) ) // start a random spot to minimize possible collisions on startup
2013-07-04 20:56:19 +00:00
{
}
Switch : : ~ Switch ( )
{
}
2014-03-19 20:56:48 +00:00
void Switch : : onRemotePacket ( const SharedPtr < Socket > & fromSock , const InetAddress & fromAddr , Buffer < ZT_SOCKET_MAX_MESSAGE_LEN > & data )
2013-07-04 20:56:19 +00:00
{
try {
2014-04-10 23:30:15 +00:00
if ( data . size ( ) = = ZT_PROTO_BEACON_LENGTH ) {
_handleBeacon ( fromSock , fromAddr , data ) ;
} else if ( data . size ( ) > ZT_PROTO_MIN_FRAGMENT_LENGTH ) {
2013-07-11 21:52:04 +00:00
if ( data [ ZT_PACKET_FRAGMENT_IDX_FRAGMENT_INDICATOR ] = = ZT_PACKET_FRAGMENT_INDICATOR )
2014-03-19 20:56:48 +00:00
_handleRemotePacketFragment ( fromSock , fromAddr , data ) ;
2014-01-30 06:04:23 +00:00
else if ( data . size ( ) > = ZT_PROTO_MIN_PACKET_LENGTH )
2014-03-19 20:56:48 +00:00
_handleRemotePacketHead ( fromSock , fromAddr , data ) ;
2013-07-04 20:56:19 +00:00
}
} catch ( std : : exception & ex ) {
2013-07-13 18:45:39 +00:00
TRACE ( " dropped packet from %s: unexpected exception: %s " , fromAddr . toString ( ) . c_str ( ) , ex . what ( ) ) ;
2013-07-04 20:56:19 +00:00
} catch ( . . . ) {
2013-07-13 18:45:39 +00:00
TRACE ( " dropped packet from %s: unexpected exception: (unknown) " , fromAddr . toString ( ) . c_str ( ) ) ;
2013-07-04 20:56:19 +00:00
}
}
void Switch : : onLocalEthernet ( const SharedPtr < Network > & network , const MAC & from , const MAC & to , unsigned int etherType , const Buffer < 4096 > & data )
{
2013-10-18 17:20:34 +00:00
SharedPtr < NetworkConfig > nconf ( network - > config2 ( ) ) ;
if ( ! nconf )
return ;
2014-06-14 04:06:34 +00:00
// Sanity check -- bridge loop? OS problem?
if ( to = = network - > mac ( ) )
2014-04-10 21:22:25 +00:00
return ;
2014-06-21 19:19:10 +00:00
/* Check anti-recursion module to ensure that this is not ZeroTier talking over its own links.
* Note : even when we introduce a more purposeful binding of the main UDP port , this can
* still happen because Windows likes to send broadcasts over interfaces that have little
* to do with their intended target audience . : P */
2014-06-14 00:49:33 +00:00
if ( ! _r - > antiRec - > checkEthernetFrame ( data . data ( ) , data . size ( ) ) ) {
TRACE ( " %s: rejected recursively addressed ZeroTier packet by tail match (type %s, length: %u) " , network - > tapDeviceName ( ) . c_str ( ) , etherTypeName ( etherType ) , data . size ( ) ) ;
2013-07-04 20:56:19 +00:00
return ;
}
2014-06-14 00:49:33 +00:00
// Check to make sure this protocol is allowed on this network
2013-10-18 17:20:34 +00:00
if ( ! nconf - > permitsEtherType ( etherType ) ) {
2014-06-17 20:52:55 +00:00
TRACE ( " %s: ignored tap: %s -> %s: ethertype %s not allowed on network %.16llx " , network - > tapDeviceName ( ) . c_str ( ) , from . toString ( ) . c_str ( ) , to . toString ( ) . c_str ( ) , etherTypeName ( etherType ) , ( unsigned long long ) network - > id ( ) ) ;
2013-07-04 20:56:19 +00:00
return ;
}
2014-06-14 00:49:33 +00:00
// Check if this packet is from someone other than the tap -- i.e. bridged in
bool fromBridged = false ;
if ( from ! = network - > mac ( ) ) {
if ( ! network - > permitsBridging ( _r - > identity . address ( ) ) ) {
2014-06-14 04:06:34 +00:00
LOG ( " %s: %s -> %s %s not forwarded, bridging disabled on %.16llx or this peer not a bridge " , network - > tapDeviceName ( ) . c_str ( ) , from . toString ( ) . c_str ( ) , to . toString ( ) . c_str ( ) , etherTypeName ( etherType ) , network - > id ( ) ) ;
2014-06-14 00:49:33 +00:00
return ;
}
fromBridged = true ;
}
if ( to . isMulticast ( ) ) {
2014-06-21 19:19:10 +00:00
// Destination is a multicast address (including broadcast)
2014-06-27 01:13:48 +00:00
uint64_t now = Utils : : now ( ) ;
2014-06-14 00:49:33 +00:00
MulticastGroup mg ( to , 0 ) ;
2014-03-26 04:38:54 +00:00
2014-06-14 00:49:33 +00:00
if ( to . isBroadcast ( ) ) {
2014-06-21 15:36:23 +00:00
if ( ( etherType = = ZT_ETHERTYPE_ARP ) & & ( data . size ( ) > = 28 ) & & ( data [ 2 ] = = 0x08 ) & & ( data [ 3 ] = = 0x00 ) & & ( data [ 4 ] = = 6 ) & & ( data [ 5 ] = = 4 ) & & ( data [ 7 ] = = 0x01 ) ) {
2014-06-14 00:49:33 +00:00
// Cram IPv4 IP into ADI field to make IPv4 ARP broadcast channel specific and scalable
// Also: enableBroadcast() does not apply to ARP since it's required for IPv4
mg = MulticastGroup : : deriveMulticastGroupForAddressResolution ( InetAddress ( data . field ( 24 , 4 ) , 4 , 0 ) ) ;
} else if ( ! nconf - > enableBroadcast ( ) ) {
// Don't transmit broadcasts if this network doesn't want them
TRACE ( " %s: dropped broadcast since ff:ff:ff:ff:ff:ff is not enabled on network %.16llx " , network - > tapDeviceName ( ) . c_str ( ) , network - > id ( ) ) ;
2014-05-23 21:32:31 +00:00
return ;
2013-09-27 20:03:13 +00:00
}
2014-06-14 00:49:33 +00:00
}
2013-09-12 16:11:21 +00:00
2014-06-21 19:19:10 +00:00
/* Learn multicast groups for bridged-in hosts.
* Note that some OSes , most notably Linux , do this for you by learning
* multicast addresses on bridge interfaces and subscribing each slave .
* But in that case this does no harm , as the sets are just merged . */
2014-06-14 00:49:33 +00:00
if ( fromBridged )
2014-06-27 01:13:48 +00:00
network - > learnBridgedMulticastGroup ( mg , now ) ;
2014-05-23 21:32:31 +00:00
2014-06-21 19:19:10 +00:00
// Check multicast/broadcast bandwidth quotas and reject if quota exceeded
2014-06-14 00:49:33 +00:00
if ( ! network - > updateAndCheckMulticastBalance ( _r - > identity . address ( ) , mg , data . size ( ) ) ) {
TRACE ( " %s: didn't multicast %d bytes, quota exceeded for multicast group %s " , network - > tapDeviceName ( ) . c_str ( ) , ( int ) data . size ( ) , mg . toString ( ) . c_str ( ) ) ;
return ;
}
2014-05-23 21:32:31 +00:00
2014-06-18 15:25:30 +00:00
TRACE ( " %s: MULTICAST %s -> %s %s %d " , network - > tapDeviceName ( ) . c_str ( ) , from . toString ( ) . c_str ( ) , mg . toString ( ) . c_str ( ) , etherTypeName ( etherType ) , ( int ) data . size ( ) ) ;
2014-06-14 00:49:33 +00:00
const unsigned int mcid = + + _multicastIdCounter & 0xffffff ;
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 fifo [ ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO + ZT_ADDRESS_LENGTH ] ; // extra ZT_ADDRESS_LENGTH is for first hop, not put in packet but serves as destination for packet
unsigned char * const fifoEnd = fifo + sizeof ( fifo ) ;
const unsigned int signedPartLen = ( ZT_PROTO_VERB_MULTICAST_FRAME_IDX_FRAME - ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION ) + data . size ( ) ;
const SharedPtr < Peer > supernode ( _r - > topology - > getBestSupernode ( ) ) ;
// For each bit prefix send a packet to a list of destinations within it
for ( unsigned int prefix = 0 , np = ( ( unsigned int ) 2 < < ( nconf - > multicastPrefixBits ( ) - 1 ) ) ; prefix < np ; + + prefix ) {
memset ( bloom , 0 , sizeof ( bloom ) ) ;
unsigned char * fifoPtr = fifo ;
2014-05-23 21:32:31 +00:00
2014-06-14 00:49:33 +00:00
// All multicasts visit all active bridges first -- this is one of the two active/passive bridge differences
for ( std : : set < Address > : : const_iterator ab ( nconf - > activeBridges ( ) . begin ( ) ) ; ab ! = nconf - > activeBridges ( ) . end ( ) ; + + ab ) {
if ( ( * ab ! = _r - > identity . address ( ) ) & & ( ab - > withinMulticastPropagationPrefix ( prefix , nconf - > multicastPrefixBits ( ) ) ) ) {
2014-06-27 01:13:48 +00:00
SharedPtr < Peer > abPeer ( _r - > topology - > getPeer ( * ab ) ) ;
if ( ( abPeer ) & & ( abPeer - > hasActiveDirectPath ( now ) ) ) {
ab - > copyTo ( fifoPtr , ZT_ADDRESS_LENGTH ) ;
if ( ( fifoPtr + = ZT_ADDRESS_LENGTH ) = = fifoEnd )
break ;
}
2014-05-23 21:32:31 +00:00
}
2014-06-14 00:49:33 +00:00
}
2014-05-23 21:32:31 +00:00
2014-06-14 00:49:33 +00:00
// Then visit next hops according to multicaster (if there's room... almost certainly will be)
if ( fifoPtr ! = fifoEnd ) {
_r - > mc - > getNextHops ( network - > id ( ) , mg , Multicaster : : AddToPropagationQueue ( & fifoPtr , fifoEnd , bloom , bloomNonce , _r - > identity . address ( ) , nconf - > multicastPrefixBits ( ) , prefix ) ) ;
2014-06-14 04:06:34 +00:00
// Pad remainder of FIFO with zeroes
2014-06-14 00:49:33 +00:00
while ( fifoPtr ! = fifoEnd )
* ( fifoPtr + + ) = ( unsigned char ) 0 ;
2014-05-23 21:32:31 +00:00
}
2014-06-14 00:49:33 +00:00
// First element in FIFO is first hop, rest of FIFO is sent in packet *to* first hop
Address firstHop ( fifo , ZT_ADDRESS_LENGTH ) ;
if ( ! firstHop ) {
if ( supernode )
firstHop = supernode - > address ( ) ;
2014-06-14 04:06:34 +00:00
else continue ; // nowhere to go
2014-05-23 21:32:31 +00:00
}
2014-06-14 00:49:33 +00:00
Packet outp ( firstHop , _r - > identity . address ( ) , Packet : : VERB_MULTICAST_FRAME ) ;
outp . append ( ( uint16_t ) 0 ) ;
outp . append ( fifo + ZT_ADDRESS_LENGTH , ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_FIFO ) ; // remainder of fifo is loaded into packet
outp . append ( bloom , ZT_PROTO_VERB_MULTICAST_FRAME_LEN_PROPAGATION_BLOOM ) ;
outp . append ( ( nconf - > com ( ) ) ? ( unsigned char ) ZT_PROTO_VERB_MULTICAST_FRAME_FLAGS_HAS_MEMBERSHIP_CERTIFICATE : ( unsigned char ) 0 ) ;
outp . append ( network - > id ( ) ) ;
outp . append ( bloomNonce ) ;
outp . append ( ( unsigned char ) nconf - > multicastPrefixBits ( ) ) ;
outp . append ( ( unsigned char ) prefix ) ;
2014-06-21 19:19:10 +00:00
_r - > identity . address ( ) . appendTo ( outp ) ; // lower 40 bits of MCID are my address
2014-06-14 00:49:33 +00:00
outp . append ( ( unsigned char ) ( ( mcid > > 16 ) & 0xff ) ) ;
outp . append ( ( unsigned char ) ( ( mcid > > 8 ) & 0xff ) ) ;
2014-06-21 19:19:10 +00:00
outp . append ( ( unsigned char ) ( mcid & 0xff ) ) ; // upper 24 bits of MCID are from our counter
2014-06-14 00:49:33 +00:00
from . appendTo ( outp ) ;
2014-06-14 04:06:34 +00:00
mg . mac ( ) . appendTo ( outp ) ;
2014-06-14 00:49:33 +00:00
outp . append ( mg . adi ( ) ) ;
outp . append ( ( uint16_t ) etherType ) ;
outp . append ( ( uint16_t ) data . size ( ) ) ;
outp . append ( data ) ;
C25519 : : Signature sig ( _r - > identity . sign ( outp . field ( ZT_PROTO_VERB_MULTICAST_FRAME_IDX__START_OF_SIGNED_PORTION , signedPartLen ) , signedPartLen ) ) ;
outp . append ( ( uint16_t ) sig . size ( ) ) ;
outp . append ( sig . data , ( unsigned int ) sig . size ( ) ) ;
// FIXME: now we send the netconf cert with every single multicast,
// which pretty much ensures everyone has it ahead of time but adds
// some redundant payload. Maybe think abouut this in the future.
if ( nconf - > com ( ) )
nconf - > com ( ) . serialize ( outp ) ;
outp . compress ( ) ;
send ( outp , true ) ;
}
2014-06-14 04:06:34 +00:00
return ;
}
2014-06-21 15:59:08 +00:00
if ( to [ 0 ] = = MAC : : firstOctetForNetwork ( network - > id ( ) ) ) {
2014-06-21 19:19:10 +00:00
// Destination is another ZeroTier node
2014-06-14 00:49:33 +00:00
Address toZT ( to . toAddress ( network - > id ( ) ) ) ;
if ( network - > isAllowed ( toZT ) ) {
network - > pushMembershipCertificate ( toZT , false , Utils : : now ( ) ) ;
2014-06-21 16:01:26 +00:00
2014-06-21 15:59:08 +00:00
if ( fromBridged ) {
// Must use EXT_FRAME if source is not myself
Packet outp ( toZT , _r - > identity . address ( ) , Packet : : VERB_EXT_FRAME ) ;
outp . append ( network - > id ( ) ) ;
outp . append ( ( unsigned char ) 0 ) ;
to . appendTo ( outp ) ;
from . appendTo ( outp ) ;
outp . append ( ( uint16_t ) etherType ) ;
outp . append ( data ) ;
outp . compress ( ) ;
send ( outp , true ) ;
} else {
// VERB_FRAME is really just lighter weight EXT_FRAME, can use for direct-to-direct (before bridging this was the only unicast method)
Packet outp ( toZT , _r - > identity . address ( ) , Packet : : VERB_FRAME ) ;
outp . append ( network - > id ( ) ) ;
outp . append ( ( uint16_t ) etherType ) ;
outp . append ( data ) ;
outp . compress ( ) ;
send ( outp , true ) ;
}
2013-07-04 20:56:19 +00:00
} else {
2014-06-14 00:49:33 +00:00
TRACE ( " %s: UNICAST: %s -> %s %s dropped, destination not a member of closed network %.16llx " , network - > tapDeviceName ( ) . c_str ( ) , from . toString ( ) . c_str ( ) , to . toString ( ) . c_str ( ) , etherTypeName ( etherType ) , network - > id ( ) ) ;
2013-07-04 20:56:19 +00:00
}
2014-06-21 16:01:26 +00:00
return ;
2014-06-14 04:06:34 +00:00
}
2014-06-21 19:19:10 +00:00
{
// Destination is behind another bridge
Address bridges [ ZT_MAX_BRIDGE_SPAM ] ;
unsigned int numBridges = 0 ;
bridges [ 0 ] = network - > findBridgeTo ( to ) ;
if ( ( bridges [ 0 ] ) & & ( bridges [ 0 ] ! = _r - > identity . address ( ) ) & & ( network - > isAllowed ( bridges [ 0 ] ) ) & & ( network - > permitsBridging ( bridges [ 0 ] ) ) ) {
// We have a known bridge route for this MAC.
+ + numBridges ;
} else if ( ! nconf - > activeBridges ( ) . empty ( ) ) {
/* If there is no known route, spam to up to ZT_MAX_BRIDGE_SPAM active
* bridges . This is similar to what many switches do - - if they do not
* know which port corresponds to a MAC , they send it to all ports . If
* there aren ' t any active bridges , numBridges will stay 0 and packet
* is dropped . */
std : : set < Address > : : const_iterator ab ( nconf - > activeBridges ( ) . begin ( ) ) ;
if ( nconf - > activeBridges ( ) . size ( ) < = ZT_MAX_BRIDGE_SPAM ) {
// If there are <= ZT_MAX_BRIDGE_SPAM active bridges, spam them all
while ( ab ! = nconf - > activeBridges ( ) . end ( ) ) {
if ( network - > isAllowed ( * ab ) ) // config sanity check
bridges [ numBridges + + ] = * ab ;
+ + ab ;
}
} else {
// Otherwise pick a random set of them
while ( numBridges < ZT_MAX_BRIDGE_SPAM ) {
if ( ab = = nconf - > activeBridges ( ) . end ( ) )
ab = nconf - > activeBridges ( ) . begin ( ) ;
if ( ( ( unsigned long ) _r - > prng - > next32 ( ) % ( unsigned long ) nconf - > activeBridges ( ) . size ( ) ) = = 0 ) {
if ( network - > isAllowed ( * ab ) ) // config sanity check
bridges [ numBridges + + ] = * ab ;
+ + ab ;
} else + + ab ;
}
2014-06-14 00:49:33 +00:00
}
}
2014-06-21 19:19:10 +00:00
for ( unsigned int b = 0 ; b < numBridges ; + + b ) {
2014-06-21 15:59:08 +00:00
Packet outp ( bridges [ b ] , _r - > identity . address ( ) , Packet : : VERB_EXT_FRAME ) ;
outp . append ( network - > id ( ) ) ;
outp . append ( ( unsigned char ) 0 ) ;
to . appendTo ( outp ) ;
from . appendTo ( outp ) ;
outp . append ( ( uint16_t ) etherType ) ;
outp . append ( data ) ;
outp . compress ( ) ;
send ( outp , true ) ;
}
2013-07-04 20:56:19 +00:00
}
}
void Switch : : send ( const Packet & packet , bool encrypt )
{
2013-07-13 02:07:48 +00:00
if ( packet . destination ( ) = = _r - > identity . address ( ) ) {
TRACE ( " BUG: caught attempt to send() to self, ignored " ) ;
return ;
}
2013-07-11 20:19:06 +00:00
if ( ! _trySend ( packet , encrypt ) ) {
Mutex : : Lock _l ( _txQueue_m ) ;
2013-07-11 21:52:04 +00:00
_txQueue . insert ( std : : pair < Address , TXQueueEntry > ( packet . destination ( ) , TXQueueEntry ( Utils : : now ( ) , packet , encrypt ) ) ) ;
2013-07-04 20:56:19 +00:00
}
}
void Switch : : sendHELLO ( const Address & dest )
{
Packet outp ( dest , _r - > identity . address ( ) , Packet : : VERB_HELLO ) ;
outp . append ( ( unsigned char ) ZT_PROTO_VERSION ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MAJOR ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MINOR ) ;
outp . append ( ( uint16_t ) ZEROTIER_ONE_VERSION_REVISION ) ;
outp . append ( Utils : : now ( ) ) ;
_r - > identity . serialize ( outp , false ) ;
send ( outp , false ) ;
}
2014-04-01 05:23:55 +00:00
bool Switch : : sendHELLO ( const SharedPtr < Peer > & dest , const Path & path )
2013-07-06 20:20:35 +00:00
{
2013-07-13 18:28:26 +00:00
uint64_t now = Utils : : now ( ) ;
2013-07-06 20:20:35 +00:00
Packet outp ( dest - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_HELLO ) ;
outp . append ( ( unsigned char ) ZT_PROTO_VERSION ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MAJOR ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MINOR ) ;
outp . append ( ( uint16_t ) ZEROTIER_ONE_VERSION_REVISION ) ;
2013-07-13 18:28:26 +00:00
outp . append ( now ) ;
2013-07-06 20:20:35 +00:00
_r - > identity . serialize ( outp , false ) ;
2013-09-27 20:03:13 +00:00
outp . armor ( dest - > key ( ) , false ) ;
2014-04-10 23:30:15 +00:00
_r - > antiRec - > logOutgoingZT ( outp . data ( ) , outp . size ( ) ) ;
return _r - > sm - > send ( path . address ( ) , path . tcp ( ) , path . type ( ) = = Path : : PATH_TYPE_TCP_OUT , outp . data ( ) , outp . size ( ) ) ;
2014-03-19 20:56:48 +00:00
}
2014-04-01 05:23:55 +00:00
bool Switch : : sendHELLO ( const SharedPtr < Peer > & dest , const InetAddress & destUdp )
2014-03-19 20:56:48 +00:00
{
uint64_t now = Utils : : now ( ) ;
Packet outp ( dest - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_HELLO ) ;
outp . append ( ( unsigned char ) ZT_PROTO_VERSION ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MAJOR ) ;
outp . append ( ( unsigned char ) ZEROTIER_ONE_VERSION_MINOR ) ;
outp . append ( ( uint16_t ) ZEROTIER_ONE_VERSION_REVISION ) ;
outp . append ( now ) ;
_r - > identity . serialize ( outp , false ) ;
outp . armor ( dest - > key ( ) , false ) ;
2014-04-10 23:30:15 +00:00
_r - > antiRec - > logOutgoingZT ( outp . data ( ) , outp . size ( ) ) ;
return _r - > sm - > send ( destUdp , false , false , outp . data ( ) , outp . size ( ) ) ;
2013-07-06 20:20:35 +00:00
}
2013-07-04 20:56:19 +00:00
bool Switch : : unite ( const Address & p1 , const Address & p2 , bool force )
{
2013-07-13 02:07:48 +00:00
if ( ( p1 = = _r - > identity . address ( ) ) | | ( p2 = = _r - > identity . address ( ) ) )
return false ;
2013-07-04 20:56:19 +00:00
SharedPtr < Peer > p1p = _r - > topology - > getPeer ( p1 ) ;
if ( ! p1p )
return false ;
SharedPtr < Peer > p2p = _r - > topology - > getPeer ( p2 ) ;
if ( ! p2p )
return false ;
uint64_t now = Utils : : now ( ) ;
std : : pair < InetAddress , InetAddress > cg ( Peer : : findCommonGround ( * p1p , * p2p , now ) ) ;
if ( ! ( cg . first ) )
return false ;
// Addresses are sorted in key for last unite attempt map for order
// invariant lookup: (p1,p2) == (p2,p1)
Array < Address , 2 > uniteKey ;
if ( p1 > = p2 ) {
uniteKey [ 0 ] = p2 ;
uniteKey [ 1 ] = p1 ;
} else {
uniteKey [ 0 ] = p1 ;
uniteKey [ 1 ] = p2 ;
}
{
Mutex : : Lock _l ( _lastUniteAttempt_m ) ;
std : : map < Array < Address , 2 > , uint64_t > : : const_iterator e ( _lastUniteAttempt . find ( uniteKey ) ) ;
if ( ( ! force ) & & ( e ! = _lastUniteAttempt . end ( ) ) & & ( ( now - e - > second ) < ZT_MIN_UNITE_INTERVAL ) )
return false ;
else _lastUniteAttempt [ uniteKey ] = now ;
}
TRACE ( " unite: %s(%s) <> %s(%s) " , p1 . toString ( ) . c_str ( ) , cg . second . toString ( ) . c_str ( ) , p2 . toString ( ) . c_str ( ) , cg . first . toString ( ) . c_str ( ) ) ;
2014-01-29 20:11:01 +00:00
/* Tell P1 where to find P2 and vice versa, sending the packets to P1 and
* P2 in randomized order in terms of which gets sent first . This is done
* since in a few cases NAT - t can be sensitive to slight timing differences
* in terms of when the two peers initiate . Normally this is accounted for
* by the nearly - simultaneous RENDEZVOUS kickoff from the supernode , but
* given that supernodes are hosted on cloud providers this can in some
* cases have a few ms of latency between packet departures . By randomizing
* the order we make each attempted NAT - t favor one or the other going
* first , meaning if it doesn ' t succeed the first time it might the second
* and so forth . */
unsigned int alt = _r - > prng - > next32 ( ) & 1 ;
unsigned int completed = alt + 2 ;
while ( alt ! = completed ) {
if ( ( alt & 1 ) = = 0 ) {
// Tell p1 where to find p2.
Packet outp ( p1 , _r - > identity . address ( ) , Packet : : VERB_RENDEZVOUS ) ;
outp . append ( ( unsigned char ) 0 ) ;
p2 . appendTo ( outp ) ;
outp . append ( ( uint16_t ) cg . first . port ( ) ) ;
if ( cg . first . isV6 ( ) ) {
outp . append ( ( unsigned char ) 16 ) ;
outp . append ( cg . first . rawIpData ( ) , 16 ) ;
} else {
outp . append ( ( unsigned char ) 4 ) ;
outp . append ( cg . first . rawIpData ( ) , 4 ) ;
}
outp . armor ( p1p - > key ( ) , true ) ;
p1p - > send ( _r , outp . data ( ) , outp . size ( ) , now ) ;
2013-07-04 20:56:19 +00:00
} else {
2014-01-29 20:11:01 +00:00
// Tell p2 where to find p1.
Packet outp ( p2 , _r - > identity . address ( ) , Packet : : VERB_RENDEZVOUS ) ;
outp . append ( ( unsigned char ) 0 ) ;
p1 . appendTo ( outp ) ;
outp . append ( ( uint16_t ) cg . second . port ( ) ) ;
if ( cg . second . isV6 ( ) ) {
outp . append ( ( unsigned char ) 16 ) ;
outp . append ( cg . second . rawIpData ( ) , 16 ) ;
} else {
outp . append ( ( unsigned char ) 4 ) ;
outp . append ( cg . second . rawIpData ( ) , 4 ) ;
}
outp . armor ( p2p - > key ( ) , true ) ;
p2p - > send ( _r , outp . data ( ) , outp . size ( ) , now ) ;
2013-07-04 20:56:19 +00:00
}
2014-01-29 20:11:01 +00:00
+ + alt ; // counts up and also flips LSB
2013-07-04 20:56:19 +00:00
}
return true ;
}
2013-07-12 02:25:12 +00:00
void Switch : : contact ( const SharedPtr < Peer > & peer , const InetAddress & atAddr )
{
2014-03-19 20:56:48 +00:00
_r - > sm - > sendFirewallOpener ( atAddr , ZT_FIREWALL_OPENER_HOPS ) ;
2013-07-13 02:07:48 +00:00
{
Mutex : : Lock _l ( _contactQueue_m ) ;
2014-03-19 20:56:48 +00:00
_contactQueue . push_back ( ContactQueueEntry ( peer , Utils : : now ( ) + ZT_RENDEZVOUS_NAT_T_DELAY , atAddr ) ) ;
2013-07-13 02:07:48 +00:00
}
// Kick main loop out of wait so that it can pick up this
// change to our scheduled timer tasks.
2014-03-18 19:21:22 +00:00
_r - > sm - > whack ( ) ;
2013-07-12 02:25:12 +00:00
}
2013-07-04 20:56:19 +00:00
unsigned long Switch : : doTimerTasks ( )
{
unsigned long nextDelay = ~ ( ( unsigned long ) 0 ) ; // big number, caller will cap return value
uint64_t now = Utils : : now ( ) ;
{
2013-07-12 02:25:12 +00:00
Mutex : : Lock _l ( _contactQueue_m ) ;
for ( std : : list < ContactQueueEntry > : : iterator qi ( _contactQueue . begin ( ) ) ; qi ! = _contactQueue . end ( ) ; ) {
if ( now > = qi - > fireAtTime ) {
TRACE ( " sending NAT-T HELLO to %s(%s) " , qi - > peer - > address ( ) . toString ( ) . c_str ( ) , qi - > inaddr . toString ( ) . c_str ( ) ) ;
2014-04-01 05:23:55 +00:00
sendHELLO ( qi - > peer , qi - > inaddr ) ;
2013-07-12 02:25:12 +00:00
_contactQueue . erase ( qi + + ) ;
2013-07-04 20:56:19 +00:00
} else {
2013-07-12 02:25:12 +00:00
nextDelay = std : : min ( nextDelay , ( unsigned long ) ( qi - > fireAtTime - now ) ) ;
+ + qi ;
2013-07-04 20:56:19 +00:00
}
}
}
{
Mutex : : Lock _l ( _outstandingWhoisRequests_m ) ;
for ( std : : map < Address , WhoisRequest > : : iterator i ( _outstandingWhoisRequests . begin ( ) ) ; i ! = _outstandingWhoisRequests . end ( ) ; ) {
unsigned long since = ( unsigned long ) ( now - i - > second . lastSent ) ;
if ( since > = ZT_WHOIS_RETRY_DELAY ) {
if ( i - > second . retries > = ZT_MAX_WHOIS_RETRIES ) {
TRACE ( " WHOIS %s timed out " , i - > first . toString ( ) . c_str ( ) ) ;
_outstandingWhoisRequests . erase ( i + + ) ;
continue ;
} else {
i - > second . lastSent = now ;
i - > second . peersConsulted [ i - > second . retries ] = _sendWhoisRequest ( i - > first , i - > second . peersConsulted , i - > second . retries ) ;
+ + i - > second . retries ;
TRACE ( " WHOIS %s (retry %u) " , i - > first . toString ( ) . c_str ( ) , i - > second . retries ) ;
nextDelay = std : : min ( nextDelay , ( unsigned long ) ZT_WHOIS_RETRY_DELAY ) ;
}
} else nextDelay = std : : min ( nextDelay , ZT_WHOIS_RETRY_DELAY - since ) ;
+ + i ;
}
}
{
Mutex : : Lock _l ( _txQueue_m ) ;
2013-07-11 21:52:04 +00:00
for ( std : : multimap < Address , TXQueueEntry > : : iterator i ( _txQueue . begin ( ) ) ; i ! = _txQueue . end ( ) ; ) {
2013-07-11 20:19:06 +00:00
if ( _trySend ( i - > second . packet , i - > second . encrypt ) )
2013-07-04 20:56:19 +00:00
_txQueue . erase ( i + + ) ;
else if ( ( now - i - > second . creationTime ) > ZT_TRANSMIT_QUEUE_TIMEOUT ) {
TRACE ( " TX %s -> %s timed out " , i - > second . packet . source ( ) . toString ( ) . c_str ( ) , i - > second . packet . destination ( ) . toString ( ) . c_str ( ) ) ;
_txQueue . erase ( i + + ) ;
} else + + i ;
}
}
2013-07-11 21:52:04 +00:00
2013-07-04 20:56:19 +00:00
{
Mutex : : Lock _l ( _rxQueue_m ) ;
2013-07-12 02:06:25 +00:00
for ( std : : list < SharedPtr < PacketDecoder > > : : iterator i ( _rxQueue . begin ( ) ) ; i ! = _rxQueue . end ( ) ; ) {
2013-07-12 02:25:12 +00:00
if ( ( now - ( * i ) - > receiveTime ( ) ) > ZT_RECEIVE_QUEUE_TIMEOUT ) {
TRACE ( " RX %s -> %s timed out " , ( * i ) - > source ( ) . toString ( ) . c_str ( ) , ( * i ) - > destination ( ) . toString ( ) . c_str ( ) ) ;
2013-07-04 20:56:19 +00:00
_rxQueue . erase ( i + + ) ;
} else + + i ;
}
}
{
Mutex : : Lock _l ( _defragQueue_m ) ;
for ( std : : map < uint64_t , DefragQueueEntry > : : iterator i ( _defragQueue . begin ( ) ) ; i ! = _defragQueue . end ( ) ; ) {
if ( ( now - i - > second . creationTime ) > ZT_FRAGMENTED_PACKET_RECEIVE_TIMEOUT ) {
TRACE ( " incomplete fragmented packet %.16llx timed out, fragments discarded " , i - > first ) ;
_defragQueue . erase ( i + + ) ;
} else + + i ;
}
}
2013-07-11 21:52:04 +00:00
return std : : max ( nextDelay , ( unsigned long ) 10 ) ; // minimum delay
2013-07-04 20:56:19 +00:00
}
void Switch : : announceMulticastGroups ( const std : : map < SharedPtr < Network > , std : : set < MulticastGroup > > & allMemberships )
{
std : : vector < SharedPtr < Peer > > directPeers ;
2013-10-02 20:12:10 +00:00
_r - > topology - > eachPeer ( Topology : : CollectPeersWithActiveDirectPath ( directPeers , Utils : : now ( ) ) ) ;
2013-07-04 20:56:19 +00:00
# ifdef ZT_TRACE
unsigned int totalMulticastGroups = 0 ;
for ( std : : map < SharedPtr < Network > , std : : set < MulticastGroup > > : : const_iterator i ( allMemberships . begin ( ) ) ; i ! = allMemberships . end ( ) ; + + i )
totalMulticastGroups + = ( unsigned int ) i - > second . size ( ) ;
TRACE ( " announcing %u multicast groups for %u networks to %u peers " , totalMulticastGroups , ( unsigned int ) allMemberships . size ( ) , ( unsigned int ) directPeers . size ( ) ) ;
# endif
2013-10-07 21:00:53 +00:00
uint64_t now = Utils : : now ( ) ;
2013-07-04 20:56:19 +00:00
for ( std : : vector < SharedPtr < Peer > > : : iterator p ( directPeers . begin ( ) ) ; p ! = directPeers . end ( ) ; + + p ) {
Packet outp ( ( * p ) - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
for ( std : : map < SharedPtr < Network > , std : : set < MulticastGroup > > : : const_iterator nwmgs ( allMemberships . begin ( ) ) ; nwmgs ! = allMemberships . end ( ) ; + + nwmgs ) {
2013-10-07 21:00:53 +00:00
nwmgs - > first - > pushMembershipCertificate ( ( * p ) - > address ( ) , false , now ) ;
2013-07-29 21:11:00 +00:00
if ( ( _r - > topology - > isSupernode ( ( * p ) - > address ( ) ) ) | | ( nwmgs - > first - > isAllowed ( ( * p ) - > address ( ) ) ) ) {
2013-07-04 20:56:19 +00:00
for ( std : : set < MulticastGroup > : : iterator mg ( nwmgs - > second . begin ( ) ) ; mg ! = nwmgs - > second . end ( ) ; + + mg ) {
if ( ( outp . size ( ) + 18 ) > ZT_UDP_DEFAULT_PAYLOAD_MTU ) {
send ( outp , true ) ;
outp . reset ( ( * p ) - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
}
2013-10-01 20:01:36 +00:00
// network ID, MAC, ADI
2013-07-04 20:56:19 +00:00
outp . append ( ( uint64_t ) nwmgs - > first - > id ( ) ) ;
2014-05-23 21:32:31 +00:00
mg - > mac ( ) . appendTo ( outp ) ;
2013-07-04 20:56:19 +00:00
outp . append ( ( uint32_t ) mg - > adi ( ) ) ;
}
}
}
if ( outp . size ( ) > ZT_PROTO_MIN_PACKET_LENGTH )
send ( outp , true ) ;
}
}
2013-10-01 20:01:36 +00:00
void Switch : : announceMulticastGroups ( const SharedPtr < Peer > & peer )
{
Packet outp ( peer - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
std : : vector < SharedPtr < Network > > networks ( _r - > nc - > networks ( ) ) ;
2013-10-07 21:00:53 +00:00
uint64_t now = Utils : : now ( ) ;
2013-10-01 20:01:36 +00:00
for ( std : : vector < SharedPtr < Network > > : : iterator n ( networks . begin ( ) ) ; n ! = networks . end ( ) ; + + n ) {
if ( ( ( * n ) - > isAllowed ( peer - > address ( ) ) ) | | ( _r - > topology - > isSupernode ( peer - > address ( ) ) ) ) {
2013-10-07 21:00:53 +00:00
( * n ) - > pushMembershipCertificate ( peer - > address ( ) , false , now ) ;
2013-10-01 20:01:36 +00:00
std : : set < MulticastGroup > mgs ( ( * n ) - > multicastGroups ( ) ) ;
for ( std : : set < MulticastGroup > : : iterator mg ( mgs . begin ( ) ) ; mg ! = mgs . end ( ) ; + + mg ) {
if ( ( outp . size ( ) + 18 ) > ZT_UDP_DEFAULT_PAYLOAD_MTU ) {
send ( outp , true ) ;
outp . reset ( peer - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_MULTICAST_LIKE ) ;
}
// network ID, MAC, ADI
outp . append ( ( uint64_t ) ( * n ) - > id ( ) ) ;
2014-05-23 21:32:31 +00:00
mg - > mac ( ) . appendTo ( outp ) ;
2013-10-01 20:01:36 +00:00
outp . append ( ( uint32_t ) mg - > adi ( ) ) ;
}
}
}
if ( outp . size ( ) > ZT_PROTO_MIN_PACKET_LENGTH )
send ( outp , true ) ;
}
2013-07-11 21:52:04 +00:00
void Switch : : requestWhois ( const Address & addr )
2013-07-11 20:19:06 +00:00
{
2013-10-03 18:38:07 +00:00
//TRACE("requesting WHOIS for %s",addr.toString().c_str());
bool inserted = false ;
2013-07-11 21:52:04 +00:00
{
Mutex : : Lock _l ( _outstandingWhoisRequests_m ) ;
std : : pair < std : : map < Address , WhoisRequest > : : iterator , bool > entry ( _outstandingWhoisRequests . insert ( std : : pair < Address , WhoisRequest > ( addr , WhoisRequest ( ) ) ) ) ;
2013-10-03 18:38:07 +00:00
if ( ( inserted = entry . second ) )
entry . first - > second . lastSent = Utils : : now ( ) ;
2013-07-11 21:52:04 +00:00
entry . first - > second . retries = 0 ; // reset retry count if entry already existed
}
2013-10-03 18:38:07 +00:00
if ( inserted )
_sendWhoisRequest ( addr , ( const Address * ) 0 , 0 ) ;
2013-07-11 20:19:06 +00:00
}
2013-10-16 21:47:26 +00:00
void Switch : : cancelWhoisRequest ( const Address & addr )
{
Mutex : : Lock _l ( _outstandingWhoisRequests_m ) ;
_outstandingWhoisRequests . erase ( addr ) ;
}
2013-07-12 02:06:25 +00:00
void Switch : : doAnythingWaitingForPeer ( const SharedPtr < Peer > & peer )
2013-07-04 20:56:19 +00:00
{
2013-07-11 21:52:04 +00:00
{
Mutex : : Lock _l ( _outstandingWhoisRequests_m ) ;
_outstandingWhoisRequests . erase ( peer - > address ( ) ) ;
}
{
Mutex : : Lock _l ( _rxQueue_m ) ;
2013-07-12 02:25:12 +00:00
for ( std : : list < SharedPtr < PacketDecoder > > : : iterator rxi ( _rxQueue . begin ( ) ) ; rxi ! = _rxQueue . end ( ) ; ) {
if ( ( * rxi ) - > tryDecode ( _r ) )
2013-07-11 21:52:04 +00:00
_rxQueue . erase ( rxi + + ) ;
else + + rxi ;
}
}
{
Mutex : : Lock _l ( _txQueue_m ) ;
std : : pair < std : : multimap < Address , TXQueueEntry > : : iterator , std : : multimap < Address , TXQueueEntry > : : iterator > waitingTxQueueItems ( _txQueue . equal_range ( peer - > address ( ) ) ) ;
for ( std : : multimap < Address , TXQueueEntry > : : iterator txi ( waitingTxQueueItems . first ) ; txi ! = waitingTxQueueItems . second ; ) {
if ( _trySend ( txi - > second . packet , txi - > second . encrypt ) )
_txQueue . erase ( txi + + ) ;
else + + txi ;
2013-07-11 20:19:06 +00:00
}
}
}
2013-07-04 20:56:19 +00:00
2013-10-17 17:07:53 +00:00
const char * Switch : : etherTypeName ( const unsigned int etherType )
throw ( )
{
switch ( etherType ) {
case ZT_ETHERTYPE_IPV4 : return " IPV4 " ;
case ZT_ETHERTYPE_ARP : return " ARP " ;
case ZT_ETHERTYPE_RARP : return " RARP " ;
case ZT_ETHERTYPE_ATALK : return " ATALK " ;
case ZT_ETHERTYPE_AARP : return " AARP " ;
case ZT_ETHERTYPE_IPX_A : return " IPX_A " ;
case ZT_ETHERTYPE_IPX_B : return " IPX_B " ;
case ZT_ETHERTYPE_IPV6 : return " IPV6 " ;
}
return " UNKNOWN " ;
}
2014-03-19 20:56:48 +00:00
void Switch : : _handleRemotePacketFragment ( const SharedPtr < Socket > & fromSock , const InetAddress & fromAddr , const Buffer < 4096 > & data )
2013-07-11 20:19:06 +00:00
{
Packet : : Fragment fragment ( data ) ;
Address destination ( fragment . destination ( ) ) ;
2013-07-13 02:07:48 +00:00
2013-07-11 20:19:06 +00:00
if ( destination ! = _r - > identity . address ( ) ) {
// Fragment is not for us, so try to relay it
if ( fragment . hops ( ) < ZT_RELAY_MAX_HOPS ) {
fragment . incrementHops ( ) ;
2013-07-13 18:45:39 +00:00
2013-07-11 20:19:06 +00:00
SharedPtr < Peer > relayTo = _r - > topology - > getPeer ( destination ) ;
2014-04-10 18:17:54 +00:00
if ( ( ! relayTo ) | | ( relayTo - > send ( _r , fragment . data ( ) , fragment . size ( ) , Utils : : now ( ) ) = = Path : : PATH_TYPE_NULL ) ) {
2013-07-11 20:19:06 +00:00
relayTo = _r - > topology - > getBestSupernode ( ) ;
if ( relayTo )
2013-07-13 18:28:26 +00:00
relayTo - > send ( _r , fragment . data ( ) , fragment . size ( ) , Utils : : now ( ) ) ;
2013-07-11 20:19:06 +00:00
}
} else {
TRACE ( " dropped relay [fragment](%s) -> %s, max hops exceeded " , fromAddr . toString ( ) . c_str ( ) , destination . toString ( ) . c_str ( ) ) ;
}
} else {
// Fragment looks like ours
uint64_t pid = fragment . packetId ( ) ;
unsigned int fno = fragment . fragmentNumber ( ) ;
unsigned int tf = fragment . totalFragments ( ) ;
if ( ( tf < = ZT_MAX_PACKET_FRAGMENTS ) & & ( fno < ZT_MAX_PACKET_FRAGMENTS ) & & ( fno > 0 ) & & ( tf > 1 ) ) {
// Fragment appears basically sane. Its fragment number must be
// 1 or more, since a Packet with fragmented bit set is fragment 0.
// Total fragments must be more than 1, otherwise why are we
// seeing a Packet::Fragment?
Mutex : : Lock _l ( _defragQueue_m ) ;
std : : map < uint64_t , DefragQueueEntry > : : iterator dqe ( _defragQueue . find ( pid ) ) ;
if ( dqe = = _defragQueue . end ( ) ) {
// We received a Packet::Fragment without its head, so queue it and wait
DefragQueueEntry & dq = _defragQueue [ pid ] ;
dq . creationTime = Utils : : now ( ) ;
dq . frags [ fno - 1 ] = fragment ;
dq . totalFragments = tf ; // total fragment count is known
dq . haveFragments = 1 < < fno ; // we have only this fragment
//TRACE("fragment (%u/%u) of %.16llx from %s",fno + 1,tf,pid,fromAddr.toString().c_str());
} else if ( ! ( dqe - > second . haveFragments & ( 1 < < fno ) ) ) {
// We have other fragments and maybe the head, so add this one and check
dqe - > second . frags [ fno - 1 ] = fragment ;
dqe - > second . totalFragments = tf ;
//TRACE("fragment (%u/%u) of %.16llx from %s",fno + 1,tf,pid,fromAddr.toString().c_str());
if ( Utils : : countBits ( dqe - > second . haveFragments | = ( 1 < < fno ) ) = = tf ) {
// We have all fragments -- assemble and process full Packet
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
2013-07-11 21:52:04 +00:00
SharedPtr < PacketDecoder > packet ( dqe - > second . frag0 ) ;
2013-07-11 20:19:06 +00:00
for ( unsigned int f = 1 ; f < tf ; + + f )
2013-07-11 21:52:04 +00:00
packet - > append ( dqe - > second . frags [ f - 1 ] . payload ( ) , dqe - > second . frags [ f - 1 ] . payloadLength ( ) ) ;
2013-07-11 20:19:06 +00:00
_defragQueue . erase ( dqe ) ;
2013-07-11 21:52:04 +00:00
if ( ! packet - > tryDecode ( _r ) ) {
Mutex : : Lock _l ( _rxQueue_m ) ;
2013-07-12 02:06:25 +00:00
_rxQueue . push_back ( packet ) ;
2013-07-11 21:52:04 +00:00
}
2013-07-11 20:19:06 +00:00
}
} // else this is a duplicate fragment, ignore
}
2013-07-11 02:58:43 +00:00
}
2013-07-11 20:19:06 +00:00
}
2014-03-19 20:56:48 +00:00
void Switch : : _handleRemotePacketHead ( const SharedPtr < Socket > & fromSock , const InetAddress & fromAddr , const Buffer < 4096 > & data )
2013-07-11 20:19:06 +00:00
{
2014-03-19 20:56:48 +00:00
SharedPtr < PacketDecoder > packet ( new PacketDecoder ( data , fromSock , fromAddr ) ) ;
2013-07-13 02:07:48 +00:00
Address source ( packet - > source ( ) ) ;
2013-07-11 21:52:04 +00:00
Address destination ( packet - > destination ( ) ) ;
2013-07-11 20:19:06 +00:00
2013-08-07 15:55:55 +00:00
//TRACE("<< %.16llx %s -> %s (size: %u)",(unsigned long long)packet->packetId(),source.toString().c_str(),destination.toString().c_str(),packet->size());
2013-07-11 20:19:06 +00:00
if ( destination ! = _r - > identity . address ( ) ) {
// Packet is not for us, so try to relay it
2013-07-11 21:52:04 +00:00
if ( packet - > hops ( ) < ZT_RELAY_MAX_HOPS ) {
packet - > incrementHops ( ) ;
2013-07-11 20:19:06 +00:00
SharedPtr < Peer > relayTo = _r - > topology - > getPeer ( destination ) ;
2014-04-10 18:17:54 +00:00
Path : : Type relayedVia ;
if ( ( relayTo ) & & ( ( relayedVia = relayTo - > send ( _r , packet - > data ( ) , packet - > size ( ) , Utils : : now ( ) ) ) ! = Path : : PATH_TYPE_NULL ) ) {
/* If both paths are UDP, attempt to invoke UDP NAT-t between peers
* by sending VERB_RENDEZVOUS . Do not do this for TCP due to GitHub
* issue # 63. */
if ( ( fromSock - > udp ( ) ) & & ( relayedVia = = Path : : PATH_TYPE_UDP ) )
unite ( source , destination , false ) ;
2013-07-11 20:19:06 +00:00
} else {
2013-07-13 18:28:26 +00:00
// If we've received a packet not for us and we don't have
// a direct path to its recipient, pass it to (another)
// supernode. This can happen due to Internet weather -- the
// most direct supernode may not be reachable, yet another
// further away may be.
2013-07-13 02:07:48 +00:00
relayTo = _r - > topology - > getBestSupernode ( & source , 1 , true ) ;
2013-07-11 20:19:06 +00:00
if ( relayTo )
2013-07-13 18:28:26 +00:00
relayTo - > send ( _r , packet - > data ( ) , packet - > size ( ) , Utils : : now ( ) ) ;
2013-07-11 20:19:06 +00:00
}
} else {
2013-07-11 21:52:04 +00:00
TRACE ( " dropped relay %s(%s) -> %s, max hops exceeded " , packet - > source ( ) . toString ( ) . c_str ( ) , fromAddr . toString ( ) . c_str ( ) , destination . toString ( ) . c_str ( ) ) ;
2013-07-11 20:19:06 +00:00
}
2013-07-11 21:52:04 +00:00
} else if ( packet - > fragmented ( ) ) {
2013-07-11 20:19:06 +00:00
// Packet is the head of a fragmented packet series
2013-07-11 21:52:04 +00:00
uint64_t pid = packet - > packetId ( ) ;
2013-07-11 20:19:06 +00:00
Mutex : : Lock _l ( _defragQueue_m ) ;
std : : map < uint64_t , DefragQueueEntry > : : iterator dqe ( _defragQueue . find ( pid ) ) ;
if ( dqe = = _defragQueue . end ( ) ) {
// If we have no other fragments yet, create an entry and save the head
DefragQueueEntry & dq = _defragQueue [ pid ] ;
dq . creationTime = Utils : : now ( ) ;
dq . frag0 = packet ;
dq . totalFragments = 0 ; // 0 == unknown, waiting for Packet::Fragment
dq . haveFragments = 1 ; // head is first bit (left to right)
//TRACE("fragment (0/?) of %.16llx from %s",pid,fromAddr.toString().c_str());
} else if ( ! ( dqe - > second . haveFragments & 1 ) ) {
// If we have other fragments but no head, see if we are complete with the head
if ( ( dqe - > second . totalFragments ) & & ( Utils : : countBits ( dqe - > second . haveFragments | = 1 ) = = dqe - > second . totalFragments ) ) {
// We have all fragments -- assemble and process full Packet
//TRACE("packet %.16llx is complete, assembling and processing...",pid);
// packet already contains head, so append fragments
for ( unsigned int f = 1 ; f < dqe - > second . totalFragments ; + + f )
2013-07-11 21:52:04 +00:00
packet - > append ( dqe - > second . frags [ f - 1 ] . payload ( ) , dqe - > second . frags [ f - 1 ] . payloadLength ( ) ) ;
2013-07-11 20:19:06 +00:00
_defragQueue . erase ( dqe ) ;
2013-07-11 21:52:04 +00:00
if ( ! packet - > tryDecode ( _r ) ) {
Mutex : : Lock _l ( _rxQueue_m ) ;
2013-07-12 02:06:25 +00:00
_rxQueue . push_back ( packet ) ;
2013-07-11 21:52:04 +00:00
}
2013-07-11 20:19:06 +00:00
} else {
// Still waiting on more fragments, so queue the head
dqe - > second . frag0 = packet ;
}
} // else this is a duplicate head, ignore
} else {
// Packet is unfragmented, so just process it
2013-07-11 21:52:04 +00:00
if ( ! packet - > tryDecode ( _r ) ) {
Mutex : : Lock _l ( _rxQueue_m ) ;
2013-07-12 02:06:25 +00:00
_rxQueue . push_back ( packet ) ;
2013-07-04 20:56:19 +00:00
}
}
}
2014-04-10 23:30:15 +00:00
void Switch : : _handleBeacon ( const SharedPtr < Socket > & fromSock , const InetAddress & fromAddr , const Buffer < 4096 > & data )
{
Address beaconAddr ( data . field ( ZT_PROTO_BEACON_IDX_ADDRESS , ZT_ADDRESS_LENGTH ) , ZT_ADDRESS_LENGTH ) ;
if ( beaconAddr = = _r - > identity . address ( ) )
return ;
SharedPtr < Peer > peer ( _r - > topology - > getPeer ( beaconAddr ) ) ;
if ( peer ) {
uint64_t now = Utils : : now ( ) ;
if ( peer - > haveUdpPath ( fromAddr ) ) {
if ( ( now - peer - > lastDirectReceive ( ) ) > = ZT_PEER_DIRECT_PING_DELAY )
peer - > sendPing ( _r , now ) ;
} else {
if ( ( now - _lastBeacon ) < ZT_MIN_BEACON_RESPONSE_INTERVAL )
return ;
_lastBeacon = now ;
sendHELLO ( peer , fromAddr ) ;
}
}
}
2013-07-04 20:56:19 +00:00
Address Switch : : _sendWhoisRequest ( const Address & addr , const Address * peersAlreadyConsulted , unsigned int numPeersAlreadyConsulted )
{
2013-07-12 20:40:59 +00:00
SharedPtr < Peer > supernode ( _r - > topology - > getBestSupernode ( peersAlreadyConsulted , numPeersAlreadyConsulted , false ) ) ;
2013-07-04 20:56:19 +00:00
if ( supernode ) {
Packet outp ( supernode - > address ( ) , _r - > identity . address ( ) , Packet : : VERB_WHOIS ) ;
2013-07-25 17:24:39 +00:00
addr . appendTo ( outp ) ;
2013-09-27 20:03:13 +00:00
outp . armor ( supernode - > key ( ) , true ) ;
2013-07-13 18:28:26 +00:00
uint64_t now = Utils : : now ( ) ;
2014-04-10 18:17:54 +00:00
if ( supernode - > send ( _r , outp . data ( ) , outp . size ( ) , now ) ! = Path : : PATH_TYPE_NULL )
2013-07-13 18:28:26 +00:00
return supernode - > address ( ) ;
2013-07-04 20:56:19 +00:00
}
return Address ( ) ;
}
2013-07-11 20:19:06 +00:00
bool Switch : : _trySend ( const Packet & packet , bool encrypt )
2013-07-04 20:56:19 +00:00
{
SharedPtr < Peer > peer ( _r - > topology - > getPeer ( packet . destination ( ) ) ) ;
2013-07-11 21:52:04 +00:00
2013-07-04 20:56:19 +00:00
if ( peer ) {
uint64_t now = Utils : : now ( ) ;
SharedPtr < Peer > via ;
2014-01-30 22:23:52 +00:00
if ( peer - > hasActiveDirectPath ( now ) ) {
2013-07-04 20:56:19 +00:00
via = peer ;
} else {
via = _r - > topology - > getBestSupernode ( ) ;
if ( ! via )
2013-07-11 20:19:06 +00:00
return false ;
2013-07-04 20:56:19 +00:00
}
Packet tmp ( packet ) ;
unsigned int chunkSize = std : : min ( tmp . size ( ) , ( unsigned int ) ZT_UDP_DEFAULT_PAYLOAD_MTU ) ;
tmp . setFragmented ( chunkSize < tmp . size ( ) ) ;
2013-09-27 20:03:13 +00:00
tmp . armor ( peer - > key ( ) , encrypt ) ;
2013-07-04 20:56:19 +00:00
2014-04-10 18:17:54 +00:00
if ( via - > send ( _r , tmp . data ( ) , chunkSize , now ) ! = Path : : PATH_TYPE_NULL ) {
2013-07-04 20:56:19 +00:00
if ( chunkSize < tmp . size ( ) ) {
// Too big for one bite, fragment the rest
unsigned int fragStart = chunkSize ;
unsigned int remaining = tmp . size ( ) - chunkSize ;
unsigned int fragsRemaining = ( remaining / ( ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH ) ) ;
if ( ( fragsRemaining * ( ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH ) ) < remaining )
+ + fragsRemaining ;
unsigned int totalFragments = fragsRemaining + 1 ;
for ( unsigned int f = 0 ; f < fragsRemaining ; + + f ) {
chunkSize = std : : min ( remaining , ( unsigned int ) ( ZT_UDP_DEFAULT_PAYLOAD_MTU - ZT_PROTO_MIN_FRAGMENT_LENGTH ) ) ;
Packet : : Fragment frag ( tmp , fragStart , chunkSize , f + 1 , totalFragments ) ;
2014-04-10 18:17:54 +00:00
via - > send ( _r , frag . data ( ) , frag . size ( ) , now ) ;
2013-07-04 20:56:19 +00:00
fragStart + = chunkSize ;
remaining - = chunkSize ;
}
}
2013-12-24 18:39:29 +00:00
2014-03-26 22:35:15 +00:00
/* #ifdef ZT_TRACE
2014-01-30 22:23:52 +00:00
if ( via ! = peer ) {
TRACE ( " >> %s to %s via %s (%d) " , Packet : : verbString ( packet . verb ( ) ) , peer - > address ( ) . toString ( ) . c_str ( ) , via - > address ( ) . toString ( ) . c_str ( ) , ( int ) packet . size ( ) ) ;
} else {
TRACE ( " >> %s to %s (%d) " , Packet : : verbString ( packet . verb ( ) ) , peer - > address ( ) . toString ( ) . c_str ( ) , ( int ) packet . size ( ) ) ;
}
2014-03-26 22:35:15 +00:00
# endif * /
2014-01-30 22:23:52 +00:00
2013-07-11 20:19:06 +00:00
return true ;
2013-07-04 20:56:19 +00:00
}
2013-07-11 20:19:06 +00:00
return false ;
2013-07-04 20:56:19 +00:00
}
2013-07-11 21:52:04 +00:00
requestWhois ( packet . destination ( ) ) ;
2013-07-11 20:19:06 +00:00
return false ;
2013-07-04 20:56:19 +00:00
}
} // namespace ZeroTier