2014-04-07 14:47:39 -07:00
/*
* 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 <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2014-07-21 09:18:33 -07:00
# include <errno.h>
2014-04-07 14:47:39 -07:00
# include <unistd.h>
# include <signal.h>
2014-07-21 09:18:33 -07:00
2014-04-07 14:47:39 -07:00
# include <fcntl.h>
# include <sys/types.h>
# include <sys/stat.h>
# include <sys/ioctl.h>
# include <sys/wait.h>
# include <sys/select.h>
# include <sys/cdefs.h>
# include <sys/uio.h>
# include <sys/param.h>
# include <sys/sysctl.h>
# include <sys/ioctl.h>
# include <sys/socket.h>
2014-07-21 09:18:33 -07:00
# include <netinet/in.h>
# include <arpa/inet.h>
2014-04-07 14:47:39 -07:00
# include <net/route.h>
# include <net/if.h>
2014-07-21 09:18:33 -07:00
# include <net/if_arp.h>
2014-04-07 14:47:39 -07:00
# include <net/if_dl.h>
# include <net/if_media.h>
# include <netinet6/in6_var.h>
# include <netinet/in_var.h>
# include <netinet/icmp6.h>
# include <netinet6/nd6.h>
# include <ifaddrs.h>
2014-07-21 09:18:33 -07:00
// OSX compile fix... in6_var defines this in a struct which namespaces it for C++
struct prf_ra {
u_char onlink : 1 ;
u_char autonomous : 1 ;
u_char reserved : 6 ;
} prf_ra ;
2014-04-07 14:47:39 -07:00
// These are KERNEL_PRIVATE... why?
# ifndef SIOCAUTOCONF_START
# define SIOCAUTOCONF_START _IOWR('i', 132, struct in6_ifreq) /* accept rtadvd on this interface */
# endif
# ifndef SIOCAUTOCONF_STOP
# define SIOCAUTOCONF_STOP _IOWR('i', 133, struct in6_ifreq) /* stop accepting rtadv for this interface */
# endif
2014-07-21 09:18:33 -07:00
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
// This source is from:
// http://www.opensource.apple.com/source/Libinfo/Libinfo-406.17/gen.subproj/getifmaddrs.c?txt
// It's here because OSX 10.6 does not have this convenience function.
# define SALIGN (sizeof(uint32_t) - 1)
# define SA_RLEN(sa) ((sa)->sa_len ? (((sa)->sa_len + SALIGN) & ~SALIGN) : \
( SALIGN + 1 ) )
# define MAX_SYSCTL_TRY 5
# define RTA_MASKS (RTA_GATEWAY | RTA_IFP | RTA_IFA)
/* FreeBSD uses NET_RT_IFMALIST and RTM_NEWMADDR from <sys/socket.h> */
/* We can use NET_RT_IFLIST2 and RTM_NEWMADDR2 on Darwin */
//#define DARWIN_COMPAT
//#ifdef DARWIN_COMPAT
# define GIM_SYSCTL_MIB NET_RT_IFLIST2
# define GIM_RTM_ADDR RTM_NEWMADDR2
//#else
//#define GIM_SYSCTL_MIB NET_RT_IFMALIST
//#define GIM_RTM_ADDR RTM_NEWMADDR
//#endif
// Not in 10.6 includes so use our own
struct _intl_ifmaddrs {
struct _intl_ifmaddrs * ifma_next ;
struct sockaddr * ifma_name ;
struct sockaddr * ifma_addr ;
struct sockaddr * ifma_lladdr ;
} ;
static inline int _intl_getifmaddrs ( struct _intl_ifmaddrs * * pif )
{
int icnt = 1 ;
int dcnt = 0 ;
int ntry = 0 ;
size_t len ;
size_t needed ;
int mib [ 6 ] ;
int i ;
char * buf ;
char * data ;
char * next ;
char * p ;
struct ifma_msghdr2 * ifmam ;
struct _intl_ifmaddrs * ifa , * ift ;
struct rt_msghdr * rtm ;
struct sockaddr * sa ;
mib [ 0 ] = CTL_NET ;
mib [ 1 ] = PF_ROUTE ;
mib [ 2 ] = 0 ; /* protocol */
mib [ 3 ] = 0 ; /* wildcard address family */
mib [ 4 ] = GIM_SYSCTL_MIB ;
mib [ 5 ] = 0 ; /* no flags */
do {
if ( sysctl ( mib , 6 , NULL , & needed , NULL , 0 ) < 0 )
return ( - 1 ) ;
if ( ( buf = ( char * ) malloc ( needed ) ) = = NULL )
return ( - 1 ) ;
if ( sysctl ( mib , 6 , buf , & needed , NULL , 0 ) < 0 ) {
if ( errno ! = ENOMEM | | + + ntry > = MAX_SYSCTL_TRY ) {
free ( buf ) ;
return ( - 1 ) ;
}
free ( buf ) ;
buf = NULL ;
}
} while ( buf = = NULL ) ;
for ( next = buf ; next < buf + needed ; next + = rtm - > rtm_msglen ) {
rtm = ( struct rt_msghdr * ) ( void * ) next ;
if ( rtm - > rtm_version ! = RTM_VERSION )
continue ;
switch ( rtm - > rtm_type ) {
case GIM_RTM_ADDR :
ifmam = ( struct ifma_msghdr2 * ) ( void * ) rtm ;
if ( ( ifmam - > ifmam_addrs & RTA_IFA ) = = 0 )
break ;
icnt + + ;
p = ( char * ) ( ifmam + 1 ) ;
for ( i = 0 ; i < RTAX_MAX ; i + + ) {
if ( ( RTA_MASKS & ifmam - > ifmam_addrs &
( 1 < < i ) ) = = 0 )
continue ;
sa = ( struct sockaddr * ) ( void * ) p ;
len = SA_RLEN ( sa ) ;
dcnt + = len ;
p + = len ;
}
break ;
}
}
data = ( char * ) malloc ( sizeof ( struct _intl_ifmaddrs ) * icnt + dcnt ) ;
if ( data = = NULL ) {
free ( buf ) ;
return ( - 1 ) ;
}
ifa = ( struct _intl_ifmaddrs * ) ( void * ) data ;
data + = sizeof ( struct _intl_ifmaddrs ) * icnt ;
memset ( ifa , 0 , sizeof ( struct _intl_ifmaddrs ) * icnt ) ;
ift = ifa ;
for ( next = buf ; next < buf + needed ; next + = rtm - > rtm_msglen ) {
rtm = ( struct rt_msghdr * ) ( void * ) next ;
if ( rtm - > rtm_version ! = RTM_VERSION )
continue ;
switch ( rtm - > rtm_type ) {
case GIM_RTM_ADDR :
ifmam = ( struct ifma_msghdr2 * ) ( void * ) rtm ;
if ( ( ifmam - > ifmam_addrs & RTA_IFA ) = = 0 )
break ;
p = ( char * ) ( ifmam + 1 ) ;
for ( i = 0 ; i < RTAX_MAX ; i + + ) {
if ( ( RTA_MASKS & ifmam - > ifmam_addrs &
( 1 < < i ) ) = = 0 )
continue ;
sa = ( struct sockaddr * ) ( void * ) p ;
len = SA_RLEN ( sa ) ;
switch ( i ) {
case RTAX_GATEWAY :
ift - > ifma_lladdr =
( struct sockaddr * ) ( void * ) data ;
memcpy ( data , p , len ) ;
data + = len ;
break ;
case RTAX_IFP :
ift - > ifma_name =
( struct sockaddr * ) ( void * ) data ;
memcpy ( data , p , len ) ;
data + = len ;
break ;
case RTAX_IFA :
ift - > ifma_addr =
( struct sockaddr * ) ( void * ) data ;
memcpy ( data , p , len ) ;
data + = len ;
break ;
default :
data + = len ;
break ;
}
p + = len ;
}
ift - > ifma_next = ift + 1 ;
ift = ift - > ifma_next ;
break ;
}
}
free ( buf ) ;
if ( ift > ifa ) {
ift - - ;
ift - > ifma_next = NULL ;
* pif = ifa ;
} else {
* pif = NULL ;
free ( ifa ) ;
}
return ( 0 ) ;
}
static inline void _intl_freeifmaddrs ( struct _intl_ifmaddrs * ifmp )
{
free ( ifmp ) ;
}
// --------------------------------------------------------------------------
// --------------------------------------------------------------------------
# include <string>
# include <map>
# include <set>
# include <algorithm>
# include "../Constants.hpp"
# include "../Utils.hpp"
# include "../Mutex.hpp"
# include "OSXEthernetTap.hpp"
// ff:ff:ff:ff:ff:ff with no ADI
static const ZeroTier : : MulticastGroup _blindWildcardMulticastGroup ( ZeroTier : : MAC ( 0xff ) , 0 ) ;
2014-04-07 14:47:39 -07:00
static inline bool _setIpv6Stuff ( const char * ifname , bool performNUD , bool acceptRouterAdverts )
{
struct in6_ndireq nd ;
struct in6_ifreq ifr ;
int s = socket ( AF_INET6 , SOCK_DGRAM , 0 ) ;
if ( s < = 0 )
return false ;
memset ( & nd , 0 , sizeof ( nd ) ) ;
strncpy ( nd . ifname , ifname , sizeof ( nd . ifname ) ) ;
if ( ioctl ( s , SIOCGIFINFO_IN6 , & nd ) ) {
close ( s ) ;
return false ;
}
unsigned long oldFlags = ( unsigned long ) nd . ndi . flags ;
if ( performNUD )
nd . ndi . flags | = ND6_IFF_PERFORMNUD ;
else nd . ndi . flags & = ~ ND6_IFF_PERFORMNUD ;
if ( oldFlags ! = ( unsigned long ) nd . ndi . flags ) {
if ( ioctl ( s , SIOCSIFINFO_FLAGS , & nd ) ) {
close ( s ) ;
return false ;
}
}
memset ( & ifr , 0 , sizeof ( ifr ) ) ;
strncpy ( ifr . ifr_name , ifname , sizeof ( ifr . ifr_name ) ) ;
if ( ioctl ( s , acceptRouterAdverts ? SIOCAUTOCONF_START : SIOCAUTOCONF_STOP , & ifr ) ) {
close ( s ) ;
return false ;
}
close ( s ) ;
return true ;
}
namespace ZeroTier {
// Only permit one tap to be opened concurrently across the entire process
2014-07-21 09:18:33 -07:00
static Mutex __tapCreateLock ;
2014-04-07 14:47:39 -07:00
2014-07-21 09:18:33 -07:00
OSXEthernetTap : : OSXEthernetTap (
2014-04-07 14:47:39 -07:00
const RuntimeEnvironment * renv ,
const char * tryToGetDevice ,
const MAC & mac ,
unsigned int mtu ,
void ( * handler ) ( void * , const MAC & , const MAC & , unsigned int , const Buffer < 4096 > & ) ,
void * arg )
throw ( std : : runtime_error ) :
2014-07-21 09:18:33 -07:00
EthernetTap ( " OSXEthernetTap " , mac , mtu , metric ) ,
2014-04-07 14:47:39 -07:00
_r ( renv ) ,
_handler ( handler ) ,
_arg ( arg ) ,
_fd ( 0 ) ,
_enabled ( true )
{
char devpath [ 64 ] , ethaddr [ 64 ] , mtustr [ 16 ] , tmp [ 4096 ] ;
struct stat stattmp ;
Mutex : : Lock _l ( __tapCreateLock ) ; // create only one tap at a time, globally
2014-07-21 09:18:33 -07:00
if ( mtu > 2800 )
throw std : : runtime_error ( " max tap MTU is 2800 " ) ;
2014-04-07 14:47:39 -07:00
// Check for existence of ZT tap devices, try to load module if not there
const char * kextload = UNIX_COMMANDS [ ZT_MAC_KEXTLOAD_COMMAND ] ;
if ( ( stat ( " /dev/zt0 " , & stattmp ) ) & & ( kextload ) ) {
strcpy ( tmp , _r - > homePath . c_str ( ) ) ;
long kextpid = ( long ) vfork ( ) ;
if ( kextpid = = 0 ) {
chdir ( tmp ) ;
execl ( kextload , kextload , " -q " , " -repository " , tmp , " tap.kext " , ( const char * ) 0 ) ;
_exit ( - 1 ) ;
} else if ( kextpid > 0 ) {
int exitcode = - 1 ;
waitpid ( kextpid , & exitcode , 0 ) ;
usleep ( 500 ) ;
} else throw std : : runtime_error ( " unable to create subprocess with fork() " ) ;
}
if ( stat ( " /dev/zt0 " , & stattmp ) )
throw std : : runtime_error ( " /dev/zt# tap devices do not exist and unable to load kernel extension " ) ;
// Try to reopen the last device we had, if we had one and it's still unused.
bool recalledDevice = false ;
if ( ( tryToGetDevice ) & & ( tryToGetDevice [ 0 ] ) ) {
Utils : : snprintf ( devpath , sizeof ( devpath ) , " /dev/%s " , tryToGetDevice ) ;
if ( stat ( devpath , & stattmp ) = = 0 ) {
_fd = : : open ( devpath , O_RDWR ) ;
if ( _fd > 0 ) {
_dev = tryToGetDevice ;
recalledDevice = true ;
}
}
}
// Open the first unused tap device if we didn't recall a previous one.
if ( ! recalledDevice ) {
for ( int i = 0 ; i < 256 ; + + i ) {
Utils : : snprintf ( devpath , sizeof ( devpath ) , " /dev/zt%d " , i ) ;
if ( stat ( devpath , & stattmp ) )
throw std : : runtime_error ( " no more TAP devices available " ) ;
_fd = : : open ( devpath , O_RDWR ) ;
if ( _fd > 0 ) {
char foo [ 16 ] ;
Utils : : snprintf ( foo , sizeof ( foo ) , " zt%d " , i ) ;
_dev = foo ;
break ;
}
}
}
if ( _fd < = 0 )
throw std : : runtime_error ( " unable to open TAP device or no more devices available " ) ;
if ( fcntl ( _fd , F_SETFL , fcntl ( _fd , F_GETFL ) & ~ O_NONBLOCK ) = = - 1 ) {
: : close ( _fd ) ;
throw std : : runtime_error ( " unable to set flags on file descriptor for TAP device " ) ;
}
const char * ifconfig = UNIX_COMMANDS [ ZT_UNIX_IFCONFIG_COMMAND ] ;
if ( ! ifconfig ) {
: : close ( _fd ) ;
throw std : : runtime_error ( " unable to find 'ifconfig' command on system " ) ;
}
// Configure MAC address and MTU, bring interface up
Utils : : snprintf ( ethaddr , sizeof ( ethaddr ) , " %.2x:%.2x:%.2x:%.2x:%.2x:%.2x " , ( int ) mac [ 0 ] , ( int ) mac [ 1 ] , ( int ) mac [ 2 ] , ( int ) mac [ 3 ] , ( int ) mac [ 4 ] , ( int ) mac [ 5 ] ) ;
Utils : : snprintf ( mtustr , sizeof ( mtustr ) , " %u " , mtu ) ;
long cpid ;
if ( ( cpid = ( long ) vfork ( ) ) = = 0 ) {
execl ( ifconfig , ifconfig , _dev . c_str ( ) , " lladdr " , ethaddr , " mtu " , mtustr , " up " , ( const char * ) 0 ) ;
_exit ( - 1 ) ;
} else {
int exitcode = - 1 ;
waitpid ( cpid , & exitcode , 0 ) ;
if ( exitcode ) {
: : close ( _fd ) ;
throw std : : runtime_error ( " ifconfig failure setting link-layer address and activating tap interface " ) ;
}
}
_setIpv6Stuff ( _dev . c_str ( ) , true , false ) ;
2014-04-09 15:22:08 -07:00
// Set close-on-exec so that devices cannot persist if we fork/exec for update
fcntl ( _fd , F_SETFD , fcntl ( _fd , F_GETFD ) | FD_CLOEXEC ) ;
2014-04-07 14:47:39 -07:00
: : pipe ( _shutdownSignalPipe ) ;
_thread = Thread : : start ( this ) ;
EthernetTap_instances_m . lock ( ) ;
+ + EthernetTap_instances ;
EthernetTap_instances_m . unlock ( ) ;
}
2014-07-21 09:18:33 -07:00
OSXEthernetTap : : ~ OSXEthernetTap ( )
2014-04-07 14:47:39 -07:00
{
: : write ( _shutdownSignalPipe [ 1 ] , " \0 " , 1 ) ; // causes thread to exit
Thread : : join ( _thread ) ;
: : close ( _fd ) ;
2014-04-09 15:32:37 -07:00
: : close ( _shutdownSignalPipe [ 0 ] ) ;
: : close ( _shutdownSignalPipe [ 1 ] ) ;
2014-04-07 14:47:39 -07:00
EthernetTap_instances_m . lock ( ) ;
int instances = - - EthernetTap_instances ;
EthernetTap_instances_m . unlock ( ) ;
if ( instances < = 0 ) {
// Unload OSX kernel extension on the deletion of the last EthernetTap
// instance.
const char * kextunload = UNIX_COMMANDS [ ZT_MAC_KEXTUNLOAD_COMMAND ] ;
if ( kextunload ) {
char tmp [ 4096 ] ;
sprintf ( tmp , " %s/tap.kext " , _r - > homePath . c_str ( ) ) ;
long kextpid = ( long ) vfork ( ) ;
if ( kextpid = = 0 ) {
execl ( kextunload , kextunload , tmp , ( const char * ) 0 ) ;
_exit ( - 1 ) ;
} else if ( kextpid > 0 ) {
int exitcode = - 1 ;
waitpid ( kextpid , & exitcode , 0 ) ;
}
}
}
}
2014-07-21 09:18:33 -07:00
void OSXEthernetTap : : setEnabled ( bool en )
2014-04-07 14:47:39 -07:00
{
_enabled = en ;
// TODO: interface status change
}
2014-07-21 09:18:33 -07:00
bool OSXEthernetTap : : enabled ( ) const
2014-04-07 14:47:39 -07:00
{
return _enabled ;
}
static bool ___removeIp ( const std : : string & _dev , const InetAddress & ip )
{
const char * ifconfig = UNIX_COMMANDS [ ZT_UNIX_IFCONFIG_COMMAND ] ;
if ( ! ifconfig )
return false ;
long cpid ;
if ( ( cpid = ( long ) vfork ( ) ) = = 0 ) {
execl ( ifconfig , ifconfig , _dev . c_str ( ) , " inet " , ip . toIpString ( ) . c_str ( ) , " -alias " , ( const char * ) 0 ) ;
_exit ( - 1 ) ;
} else {
int exitcode = - 1 ;
waitpid ( cpid , & exitcode , 0 ) ;
return ( exitcode = = 0 ) ;
}
return false ; // never reached, make compiler shut up about return value
}
2014-07-21 09:18:33 -07:00
bool OSXEthernetTap : : addIP ( const InetAddress & ip )
2014-04-07 14:47:39 -07:00
{
const char * ifconfig = UNIX_COMMANDS [ ZT_UNIX_IFCONFIG_COMMAND ] ;
if ( ! ifconfig ) {
LOG ( " ERROR: could not configure IP address for %s: unable to find 'ifconfig' command on system (checked /sbin, /bin, /usr/sbin, /usr/bin) " , _dev . c_str ( ) ) ;
return false ;
}
if ( ! ip )
return false ;
std : : set < InetAddress > allIps ( ips ( ) ) ;
if ( allIps . count ( ip ) > 0 )
return true ; // IP/netmask already assigned
// Remove and reconfigure if address is the same but netmask is different
for ( std : : set < InetAddress > : : iterator i ( allIps . begin ( ) ) ; i ! = allIps . end ( ) ; + + i ) {
if ( ( i - > ipsEqual ( ip ) ) & & ( i - > netmaskBits ( ) ! = ip . netmaskBits ( ) ) ) {
if ( ___removeIp ( _dev , * i ) ) {
break ;
} else {
LOG ( " WARNING: failed to remove old IP/netmask %s to replace with %s " , i - > toString ( ) . c_str ( ) , ip . toString ( ) . c_str ( ) ) ;
}
}
}
long cpid ;
if ( ( cpid = ( long ) vfork ( ) ) = = 0 ) {
execl ( ifconfig , ifconfig , _dev . c_str ( ) , ip . isV4 ( ) ? " inet " : " inet6 " , ip . toString ( ) . c_str ( ) , " alias " , ( const char * ) 0 ) ;
_exit ( - 1 ) ;
} else {
int exitcode = - 1 ;
waitpid ( cpid , & exitcode , 0 ) ;
return ( exitcode = = 0 ) ;
}
return false ;
}
2014-07-21 09:18:33 -07:00
bool OSXEthernetTap : : removeIP ( const InetAddress & ip )
2014-04-07 14:47:39 -07:00
{
if ( ips ( ) . count ( ip ) > 0 ) {
if ( ___removeIp ( _dev , ip ) )
return true ;
}
return false ;
}
2014-07-21 09:18:33 -07:00
std : : set < InetAddress > OSXEthernetTap : : ips ( ) const
2014-04-07 14:47:39 -07:00
{
struct ifaddrs * ifa = ( struct ifaddrs * ) 0 ;
if ( getifaddrs ( & ifa ) )
return std : : set < InetAddress > ( ) ;
std : : set < InetAddress > r ;
struct ifaddrs * p = ifa ;
while ( p ) {
if ( ( ! strcmp ( p - > ifa_name , _dev . c_str ( ) ) ) & & ( p - > ifa_addr ) & & ( p - > ifa_netmask ) & & ( p - > ifa_addr - > sa_family = = p - > ifa_netmask - > sa_family ) ) {
switch ( p - > ifa_addr - > sa_family ) {
case AF_INET : {
struct sockaddr_in * sin = ( struct sockaddr_in * ) p - > ifa_addr ;
struct sockaddr_in * nm = ( struct sockaddr_in * ) p - > ifa_netmask ;
r . insert ( InetAddress ( & ( sin - > sin_addr . s_addr ) , 4 , Utils : : countBits ( ( uint32_t ) nm - > sin_addr . s_addr ) ) ) ;
} break ;
case AF_INET6 : {
struct sockaddr_in6 * sin = ( struct sockaddr_in6 * ) p - > ifa_addr ;
struct sockaddr_in6 * nm = ( struct sockaddr_in6 * ) p - > ifa_netmask ;
uint32_t b [ 4 ] ;
memcpy ( b , nm - > sin6_addr . s6_addr , sizeof ( b ) ) ;
r . insert ( InetAddress ( sin - > sin6_addr . s6_addr , 16 , Utils : : countBits ( b [ 0 ] ) + Utils : : countBits ( b [ 1 ] ) + Utils : : countBits ( b [ 2 ] ) + Utils : : countBits ( b [ 3 ] ) ) ) ;
} break ;
}
}
p = p - > ifa_next ;
}
if ( ifa )
freeifaddrs ( ifa ) ;
return r ;
}
2014-07-21 09:18:33 -07:00
void OSXEthernetTap : : put ( const MAC & from , const MAC & to , unsigned int etherType , const void * data , unsigned int len )
2014-04-07 14:47:39 -07:00
{
char putBuf [ 4096 + 14 ] ;
if ( ( _fd > 0 ) & & ( len < = _mtu ) ) {
2014-05-23 14:32:31 -07:00
to . copyTo ( putBuf , 6 ) ;
from . copyTo ( putBuf + 6 , 6 ) ;
2014-04-07 14:47:39 -07:00
* ( ( uint16_t * ) ( putBuf + 12 ) ) = htons ( ( uint16_t ) etherType ) ;
memcpy ( putBuf + 14 , data , len ) ;
len + = 14 ;
int n = : : write ( _fd , putBuf , len ) ;
if ( n < = 0 ) {
LOG ( " error writing packet to Ethernet tap device: %s " , strerror ( errno ) ) ;
} else if ( n ! = ( int ) len ) {
// Saw this gremlin once, so log it if we see it again... OSX tap
// or something seems to have goofy issues with certain MTUs.
LOG ( " ERROR: write underrun: %s tap write() wrote %d of %u bytes of frame " , _dev . c_str ( ) , n , len ) ;
}
}
}
2014-07-21 09:18:33 -07:00
std : : string OSXEthernetTap : : deviceName ( ) const
2014-04-07 14:47:39 -07:00
{
return _dev ;
}
2014-07-21 09:18:33 -07:00
std : : string OSXEthernetTap : : persistentId ( ) const
2014-04-07 14:47:39 -07:00
{
return std : : string ( ) ;
}
2014-07-21 09:18:33 -07:00
bool OSXEthernetTap : : updateMulticastGroups ( std : : set < MulticastGroup > & groups )
2014-04-07 14:47:39 -07:00
{
std : : set < MulticastGroup > newGroups ;
struct _intl_ifmaddrs * ifmap = ( struct _intl_ifmaddrs * ) 0 ;
if ( ! _intl_getifmaddrs ( & ifmap ) ) {
struct _intl_ifmaddrs * p = ifmap ;
while ( p ) {
if ( p - > ifma_addr - > sa_family = = AF_LINK ) {
struct sockaddr_dl * in = ( struct sockaddr_dl * ) p - > ifma_name ;
struct sockaddr_dl * la = ( struct sockaddr_dl * ) p - > ifma_addr ;
if ( ( la - > sdl_alen = = 6 ) & & ( in - > sdl_nlen < = _dev . length ( ) ) & & ( ! memcmp ( _dev . data ( ) , in - > sdl_data , in - > sdl_nlen ) ) )
2014-05-23 14:32:31 -07:00
newGroups . insert ( MulticastGroup ( MAC ( la - > sdl_data + la - > sdl_nlen , 6 ) , 0 ) ) ;
2014-04-07 14:47:39 -07:00
}
p = p - > ifma_next ;
}
_intl_freeifmaddrs ( ifmap ) ;
}
{
std : : set < InetAddress > allIps ( ips ( ) ) ;
for ( std : : set < InetAddress > : : const_iterator i ( allIps . begin ( ) ) ; i ! = allIps . end ( ) ; + + i )
newGroups . insert ( MulticastGroup : : deriveMulticastGroupForAddressResolution ( * i ) ) ;
}
bool changed = false ;
for ( std : : set < MulticastGroup > : : iterator mg ( newGroups . begin ( ) ) ; mg ! = newGroups . end ( ) ; + + mg ) {
if ( ! groups . count ( * mg ) ) {
groups . insert ( * mg ) ;
changed = true ;
}
}
for ( std : : set < MulticastGroup > : : iterator mg ( groups . begin ( ) ) ; mg ! = groups . end ( ) ; ) {
2014-05-23 15:13:34 -07:00
if ( ( ! newGroups . count ( * mg ) ) & & ( * mg ! = _blindWildcardMulticastGroup ) ) {
2014-04-07 14:47:39 -07:00
groups . erase ( mg + + ) ;
changed = true ;
} else + + mg ;
}
return changed ;
}
2014-07-21 09:18:33 -07:00
void OSXEthernetTap : : threadMain ( )
2014-04-07 14:47:39 -07:00
throw ( )
{
fd_set readfds , nullfds ;
MAC to , from ;
int n , nfds , r ;
char getBuf [ 8194 ] ;
Buffer < 4096 > data ;
// Wait for a moment after startup -- wait for Network to finish
// constructing itself.
Thread : : sleep ( 500 ) ;
FD_ZERO ( & readfds ) ;
FD_ZERO ( & nullfds ) ;
nfds = ( int ) std : : max ( _shutdownSignalPipe [ 0 ] , _fd ) + 1 ;
r = 0 ;
for ( ; ; ) {
FD_SET ( _shutdownSignalPipe [ 0 ] , & readfds ) ;
FD_SET ( _fd , & readfds ) ;
select ( nfds , & readfds , & nullfds , & nullfds , ( struct timeval * ) 0 ) ;
if ( FD_ISSET ( _shutdownSignalPipe [ 0 ] , & readfds ) ) // writes to shutdown pipe terminate thread
break ;
if ( FD_ISSET ( _fd , & readfds ) ) {
n = ( int ) : : read ( _fd , getBuf + r , sizeof ( getBuf ) - r ) ;
if ( n < 0 ) {
if ( ( errno ! = EINTR ) & & ( errno ! = ETIMEDOUT ) ) {
TRACE ( " unexpected error reading from tap: %s " , strerror ( errno ) ) ;
break ;
}
} else {
// Some tap drivers like to send the ethernet frame and the
// payload in two chunks, so handle that by accumulating
// data until we have at least a frame.
r + = n ;
if ( r > 14 ) {
if ( r > ( ( int ) _mtu + 14 ) ) // sanity check for weird TAP behavior on some platforms
r = _mtu + 14 ;
2014-05-23 14:32:31 -07:00
to . setTo ( getBuf , 6 ) ;
from . setTo ( getBuf + 6 , 6 ) ;
2014-04-07 14:47:39 -07:00
unsigned int etherType = ntohs ( ( ( const uint16_t * ) getBuf ) [ 6 ] ) ;
if ( etherType ! = 0x8100 ) { // VLAN tagged frames are not supported!
data . copyFrom ( getBuf + 14 , ( unsigned int ) r - 14 ) ;
_handler ( _arg , from , to , etherType , data ) ;
}
r = 0 ;
}
}
}
}
}
} // namespace ZeroTier