@ -2,6 +2,7 @@
# define LLARP_EV_HPP
# include <net/ip_address.hpp>
# include <net/ip_packet.hpp>
# include <ev/ev.h>
# include <util/buffer.hpp>
# include <util/codel.hpp>
@ -51,759 +52,81 @@ struct llarp_ev_pkt_pipe;
# define EV_WRITE_BUF_SZ (4 * 1024UL)
# endif
/// do io and reset errno after
static ssize_t
IO ( std : : function < ssize_t ( void ) > iofunc )
{
ssize_t ret = iofunc ( ) ;
# ifndef _WIN32
errno = 0 ;
# else
WSASetLastError ( 0 ) ;
# endif
return ret ;
}
namespace llarp
{
# ifdef _WIN32
struct win32_ev_io
namespace vpn
{
struct WriteBuffer
{
llarp_time_t timestamp = 0 s ;
size_t bufsz ;
byte_t buf [ EV_WRITE_BUF_SZ ] = { 0 } ;
WriteBuffer ( ) = default ;
WriteBuffer ( const byte_t * ptr , size_t sz )
{
if ( sz < = sizeof ( buf ) )
{
bufsz = sz ;
memcpy ( buf , ptr , bufsz ) ;
}
else
bufsz = 0 ;
}
struct GetTime
{
llarp_time_t
operator ( ) ( const WriteBuffer & buf ) const
{
return buf . timestamp ;
}
} ;
struct GetNow
{
llarp_ev_loop_ptr loop ;
GetNow ( llarp_ev_loop_ptr l ) : loop ( l )
{ }
llarp_time_t
operator ( ) ( ) const
{
return llarp_ev_loop_time_now_ms ( loop ) ;
}
} ;
struct PutTime
{
llarp_ev_loop_ptr loop ;
PutTime ( llarp_ev_loop_ptr l ) : loop ( l )
{ }
void
operator ( ) ( WriteBuffer & buf )
{
buf . timestamp = llarp_ev_loop_time_now_ms ( loop ) ;
}
} ;
struct Compare
{
bool
operator ( ) ( const WriteBuffer & left , const WriteBuffer & right ) const
{
return left . timestamp < right . timestamp ;
}
} ;
} ;
using LosslessWriteQueue_t = std : : deque < WriteBuffer > ;
intptr_t fd ; // Sockets only, fuck UNIX-style reactive IO with a rusty knife
int flags = 0 ;
win32_ev_io ( intptr_t f ) : fd ( f ) { } ;
/// for tcp
win32_ev_io ( intptr_t f , LosslessWriteQueue_t * q ) : fd ( f ) , m_BlockingWriteQueue ( q )
{ }
class NetworkInterface ;
}
virtual void
error ( )
{
char ebuf [ 1024 ] ;
int err = WSAGetLastError ( ) ;
FormatMessage ( FORMAT_MESSAGE_FROM_SYSTEM , nullptr , err , LANG_NEUTRAL , ebuf , 1024 , nullptr ) ;
llarp : : LogError ( ebuf ) ;
}
// this (nearly!) abstract base class
// is overriden for each platform
struct EventLoop
{
byte_t readbuf [ EV_READ_BUF_SZ ] = { 0 } ;
virtual int
read( byte_t * buf , size_t sz ) = 0 ;
virtual bool
init ( ) = 0 ;
virtual int
sendto ( const SockAddr & dst , const void * data , size_t sz )
{
UNREFERENCED_PARAMETER ( dst ) ;
UNREFERENCED_PARAMETER ( data ) ;
UNREFERENCED_PARAMETER ( sz ) ;
return - 1 ;
} ;
run ( ) = 0 ;
/// return false if we want to deregister and remove ourselves
virtual bool
tick ( )
{
return true ;
} ;
/// used for tun interface and tcp conn
virtual ssize_t
do_write ( void * data , size_t sz )
{
return send ( fd , ( char * ) data , sz , 0 ) ;
}
bool
queue_write ( const byte_t * buf , size_t sz )
{
if ( m_BlockingWriteQueue )
{
m_BlockingWriteQueue - > emplace_back ( buf , sz ) ;
return true ;
}
else
return false ;
}
running ( ) const = 0 ;
virtual void
flush_write ( )
{
flush_write_buffers ( 0 ) ;
}
update_time ( )
{ }
/// called in event loop when fd is ready for writing
/// requeues anything not written
/// this assumes fd is set to non blocking
virtual void
flush_write_buffers ( size_t amount )
virtual llarp_time_t
time_now ( ) const
{
if ( m_BlockingWriteQueue )
{
if ( amount )
{
while ( amount & & m_BlockingWriteQueue - > size ( ) )
{
auto & itr = m_BlockingWriteQueue - > front ( ) ;
ssize_t result = do_write ( itr . buf , std : : min ( amount , itr . bufsz ) ) ;
if ( result = = - 1 )
return ;
ssize_t dlt = itr . bufsz - result ;
if ( dlt > 0 )
{
// queue remaining to front of queue
WriteBuffer buff ( itr . buf + dlt , itr . bufsz - dlt ) ;
m_BlockingWriteQueue - > pop_front ( ) ;
m_BlockingWriteQueue - > push_front ( buff ) ;
// TODO: errno?
return ;
}
m_BlockingWriteQueue - > pop_front ( ) ;
amount - = result ;
}
}
else
{
// write buffers
while ( m_BlockingWriteQueue - > size ( ) )
{
auto & itr = m_BlockingWriteQueue - > front ( ) ;
ssize_t result = do_write ( itr . buf , itr . bufsz ) ;
if ( result = = - 1 )
return ;
ssize_t dlt = itr . bufsz - result ;
if ( dlt > 0 )
{
// queue remaining to front of queue
WriteBuffer buff ( itr . buf + dlt , itr . bufsz - dlt ) ;
m_BlockingWriteQueue - > pop_front ( ) ;
m_BlockingWriteQueue - > push_front ( buff ) ;
// TODO: errno?
return ;
}
m_BlockingWriteQueue - > pop_front ( ) ;
int wsaerr = WSAGetLastError ( ) ;
if ( wsaerr = = WSA_IO_PENDING | | wsaerr = = WSAEWOULDBLOCK )
{
WSASetLastError ( 0 ) ;
return ;
}
}
}
}
/// reset errno
WSASetLastError ( 0 ) ;
return llarp : : time_now_ms ( ) ;
}
std : : unique_ptr < LosslessWriteQueue_t > m_BlockingWriteQueue ;
virtual ~ win32_ev_io ( )
{
closesocket ( fd ) ;
} ;
} ;
# else
struct posix_ev_io
{
struct WriteBuffer
{
llarp_time_t timestamp = 0 s ;
size_t bufsz ;
byte_t buf [ EV_WRITE_BUF_SZ ] ;
WriteBuffer ( ) = default ;
WriteBuffer ( const byte_t * ptr , size_t sz )
{
if ( sz < = sizeof ( buf ) )
{
bufsz = sz ;
memcpy ( buf , ptr , bufsz ) ;
}
else
bufsz = 0 ;
}
struct GetTime
{
llarp_time_t
operator ( ) ( const WriteBuffer & writebuf ) const
{
return writebuf . timestamp ;
}
} ;
struct GetNow
{
llarp_ev_loop_ptr loop ;
GetNow ( llarp_ev_loop_ptr l ) : loop ( std : : move ( l ) )
{ }
llarp_time_t
operator ( ) ( ) const
{
return llarp_ev_loop_time_now_ms ( loop ) ;
}
} ;
struct PutTime
{
llarp_ev_loop_ptr loop ;
PutTime ( llarp_ev_loop_ptr l ) : loop ( std : : move ( l ) )
{ }
void
operator ( ) ( WriteBuffer & writebuf )
{
writebuf . timestamp = llarp_ev_loop_time_now_ms ( loop ) ;
}
} ;
struct Compare
{
bool
operator ( ) ( const WriteBuffer & left , const WriteBuffer & right ) const
{
return left . timestamp < right . timestamp ;
}
} ;
} ;
using LossyWriteQueue_t = llarp : : util : : CoDelQueue <
WriteBuffer ,
WriteBuffer : : GetTime ,
WriteBuffer : : PutTime ,
WriteBuffer : : Compare ,
WriteBuffer : : GetNow ,
llarp : : util : : NullMutex ,
llarp : : util : : NullLock > ;
using LosslessWriteQueue_t = std : : deque < WriteBuffer > ;
virtual void
stopped ( ) { } ;
int fd ;
int flags = 0 ;
# if defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || (__APPLE__ && __MACH__)
struct kevent change ;
# endif
virtual uint32_t
call_after_delay ( llarp_time_t delay_ms , std : : function < void ( void ) > callback ) = 0 ;
posix_ev_io ( int f ) : fd ( f )
{ }
virtual void
cancel_delayed_call ( uint32_t call_id ) = 0 ;
/// for tun
posix_ev_io ( int f , LossyWriteQueue_t * q ) : fd ( f ) , m_LossyWriteQueue ( q )
{ }
virtual bool
add_network_interface (
std : : shared_ptr < vpn : : NetworkInterface > netif ,
std : : function < void ( net : : IPPacket ) > packetHandler ) = 0 ;
/// for tcp
posix_ev_io ( int f , LosslessWriteQueue_t * q ) : fd ( f ) , m_BlockingWriteQueue ( q )
{ }
virtual bool
add_ticker ( std : : function < void ( void ) > ticker ) = 0 ;
virtual void
error ( )
{
llarp : : LogError ( strerror ( errno ) ) ;
}
virtual int
read ( byte_t * buf , size_t sz ) = 0 ;
stop ( ) = 0 ;
virtual int
sendto (
__attribute__ ( ( unused ) ) const SockAddr & dst ,
__attribute__ ( ( unused ) ) const void * data ,
__attribute__ ( ( unused ) ) size_t sz )
{
return - 1 ;
}
/// return false if we want to deregister and remove ourselves
virtual bool
tick ( )
{
return true ;
}
udp_listen ( llarp_udp_io * l , const llarp : : SockAddr & src ) = 0 ;
/// used for tun interface and tcp conn
virtual ssize_t
do_write ( void * data , size_t sz )
{
return write ( fd , data , sz ) ;
}
virtual bool
udp_close ( llarp_udp_io * l ) = 0 ;
bool
queue_write ( const byte_t * buf , size_t sz )
{
if ( m_LossyWriteQueue )
{
m_LossyWriteQueue - > Emplace ( buf , sz ) ;
return true ;
}
if ( m_BlockingWriteQueue )
{
m_BlockingWriteQueue - > emplace_back ( buf , sz ) ;
return true ;
}
/// give this event loop a logic thread for calling
virtual void set_logic ( std : : shared_ptr < llarp : : Logic > ) = 0 ;
return false ;
}
virtual ~ EventLoop ( ) = default ;
virtual void
flush_write ( )
{
flush_write_buffers ( 0 ) ;
}
call_soon ( std : : function < void ( void ) > f ) = 0 ;
/// set the function that is called once per cycle the flush all the queues
virtual void
before_flush_write ( )
{ }
set_pump_function ( std : : function < void ( void ) > pumpll ) = 0 ;
/// called in event loop when fd is ready for writing
/// requeues anything not written
/// this assumes fd is set to non blocking
virtual void
flush_write_buffers ( size_t amount )
{
before_flush_write ( ) ;
if ( m_LossyWriteQueue )
{
m_LossyWriteQueue - > Process ( [ & ] ( WriteBuffer & buffer ) {
do_write ( buffer . buf , buffer . bufsz ) ;
// if we would block we save the entries for later
// discard entry
} ) ;
}
else if ( m_BlockingWriteQueue )
{
if ( amount )
{
while ( amount & & m_BlockingWriteQueue - > size ( ) )
{
auto & itr = m_BlockingWriteQueue - > front ( ) ;
ssize_t result = do_write ( itr . buf , std : : min ( amount , itr . bufsz ) ) ;
if ( result < = 0 )
return ;
ssize_t dlt = itr . bufsz - result ;
if ( dlt > 0 )
{
// queue remaining to front of queue
WriteBuffer buff ( itr . buf + dlt , itr . bufsz - dlt ) ;
m_BlockingWriteQueue - > pop_front ( ) ;
m_BlockingWriteQueue - > push_front ( buff ) ;
// TODO: errno?
return ;
}
m_BlockingWriteQueue - > pop_front ( ) ;
amount - = result ;
}
}
else
{
// write buffers
while ( m_BlockingWriteQueue - > size ( ) )
{
auto & itr = m_BlockingWriteQueue - > front ( ) ;
ssize_t result = do_write ( itr . buf , itr . bufsz ) ;
if ( result < = 0 )
{
errno = 0 ;
return ;
}
ssize_t dlt = itr . bufsz - result ;
if ( dlt > 0 )
{
// queue remaining to front of queue
WriteBuffer buff ( itr . buf + dlt , itr . bufsz - dlt ) ;
m_BlockingWriteQueue - > pop_front ( ) ;
m_BlockingWriteQueue - > push_front ( buff ) ;
// TODO: errno?
return ;
}
m_BlockingWriteQueue - > pop_front ( ) ;
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK )
{