@ -20,10 +20,7 @@
# include "n2n.h"
# include "n2n.h"
# ifdef __linux__ /* currently, Linux only */
# if defined (__linux__) || defined(WIN32) /* currently, Linux and Windows only */
# include <net/route.h>
# define WITH_ADDRESS 1
# define WITH_ADDRESS 1
@ -45,6 +42,11 @@
# define NO_DETECT 0
# define NO_DETECT 0
# define AUTO_DETECT 1
# define AUTO_DETECT 1
// REVISIT: may become obsolete
# ifdef WIN32
# define STDIN_FILENO _fileno(stdin)
# endif
typedef struct n2n_route {
typedef struct n2n_route {
struct in_addr net_addr ; /* network address to be routed, also key for hash table*/
struct in_addr net_addr ; /* network address to be routed, also key for hash table*/
@ -74,12 +76,32 @@ static int keep_running = 1; /* for main loop, handled by signals *
// PLATFORM-DEPENDANT CODE
// PLATFORM-DEPENDANT CODE
// taken from https://stackoverflow.com/questions/4159910/check-if-user-is-root-in-c
int is_privileged ( void ) {
int is_privileged ( void ) {
# if defined(__linux__)
// taken from https://stackoverflow.com/questions/4159910/check-if-user-is-root-in-c
uid_t euid = geteuid ( ) ;
uid_t euid = geteuid ( ) ;
return euid = = 0 ;
return euid = = 0 ;
# elif defined(WIN32)
// taken from https://stackoverflow.com/a/10553065
int result ;
DWORD rc ;
wchar_t user_name [ 256 ] ;
USER_INFO_1 * info ;
DWORD size = sizeof ( user_name ) ;
GetUserNameW ( user_name , & size ) ;
rc = NetUserGetInfo ( NULL , user_name , 1 , ( unsigned char * * ) & info ) ;
if ( rc ) {
return 0 ;
}
result = ( info - > usri1_priv = = USER_PRIV_ADMIN ) ;
NetApiBufferFree ( info ) ;
return result ;
# endif
}
}
@ -89,21 +111,20 @@ int is_privileged (void) {
void set_term_handler ( const void * handler ) {
void set_term_handler ( const void * handler ) {
# ifdef __linux__
# if defined(__linux__)
signal ( SIGPIPE , SIG_IGN ) ;
signal ( SIGPIPE , SIG_IGN ) ;
signal ( SIGTERM , handler ) ;
signal ( SIGTERM , handler ) ;
signal ( SIGINT , handler ) ;
signal ( SIGINT , handler ) ;
# endif
# elif defined(WIN32)
# ifdef WIN32 /* the beginning of Windows support ...? */
SetConsoleCtrlHandler ( handler , TRUE ) ;
SetConsoleCtrlHandler ( handler , TRUE ) ;
# endif
# endif
}
}
# ifdef WIN32 /* the beginning of Windows support ...? */
# if !defined(WIN32)
BOOL WINAPI term_handler ( DWORD sig ) {
# else
static void term_handler ( int sig ) {
static void term_handler ( int sig ) {
# else
BOOL WINAPI term_handler ( DWORD sig ) {
# endif
# endif
static int called = 0 ;
static int called = 0 ;
@ -117,7 +138,7 @@ static void term_handler (int sig) {
}
}
keep_running = 0 ;
keep_running = 0 ;
# ifdef WIN32 /* the beginning of Windows support ...? */
# if defined(WIN32)
return TRUE ;
return TRUE ;
# endif
# endif
}
}
@ -127,14 +148,16 @@ static void term_handler (int sig) {
// PLATFORM-DEPENDANT CODE
// PLATFORM-DEPENDANT CODE
// taken from https://gist.github.com/javiermon/6272065
// with modifications
// originally licensed under GPLV2, Apache, and/or MIT
# define RTLINK_BUFFER_SIZE 8192
# define RTLINK_BUFFER_SIZE 8192
int find_default_gateway ( struct in_addr * gateway_addr , struct in_addr * exclude ) {
int find_default_gateway ( struct in_addr * gateway_addr , struct in_addr * exclude ) {
# if defined(__linux__)
// taken from https://gist.github.com/javiermon/6272065
// with modifications
// originally licensed under GPLV2, Apache, and/or MIT
int ret = 0 ;
int ret = 0 ;
int received_bytes = 0 , msg_len = 0 , route_attribute_len = 0 ;
int received_bytes = 0 , msg_len = 0 , route_attribute_len = 0 ;
SOCKET sock = - 1 ;
SOCKET sock = - 1 ;
@ -260,7 +283,125 @@ find_default_gateway_end:
closesocket ( sock ) ;
closesocket ( sock ) ;
return ret ;
return ret ;
# elif defined(WIN32)
// taken from (and modified)
// https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-createipforwardentry
PMIB_IPFORWARDTABLE pIpForwardTable = NULL ;
DWORD dwSize = 0 ;
BOOL bOrder = FALSE ;
DWORD dwStatus = 0 ;
unsigned int i ;
ipstr_t gateway_address ;
// find out how big our buffer needs to be
dwStatus = GetIpForwardTable ( pIpForwardTable , & dwSize , bOrder ) ;
if ( dwStatus = = ERROR_INSUFFICIENT_BUFFER ) {
// allocate the memory for the table
if ( ! ( pIpForwardTable = ( PMIB_IPFORWARDTABLE ) malloc ( dwSize ) ) ) {
traceEvent ( TRACE_DEBUG , " malloc failed, out of memory \n " ) ;
return EXIT_FAILURE ;
}
// now get the table
dwStatus = GetIpForwardTable ( pIpForwardTable , & dwSize , bOrder ) ;
}
if ( dwStatus ! = ERROR_SUCCESS ) {
traceEvent ( TRACE_DEBUG , " getIpForwardTable failed \n " ) ;
if ( pIpForwardTable )
free ( pIpForwardTable ) ;
return EXIT_FAILURE ;
}
dwStatus = EXIT_FAILURE ;
// search for the row in the table we want. The default gateway has a destination of 0.0.0.0
for ( i = 0 ; i < pIpForwardTable - > dwNumEntries ; i + + ) {
if ( pIpForwardTable - > table [ i ] . dwForwardDest = = 0 ) {
// we have found a default route
// do not use if the gateway is the one to be excluded
if ( pIpForwardTable - > table [ i ] . dwForwardNextHop = = exclude - > S_un . S_addr )
continue ;
dwStatus = 0 ;
gateway_addr - > S_un . S_addr = pIpForwardTable - > table [ i ] . dwForwardNextHop ;
traceEvent ( TRACE_DEBUG , " assuming default gateway %s " ,
inaddrtoa ( gateway_address , * gateway_addr ) ) ;
break ;
}
}
if ( pIpForwardTable ) {
free ( pIpForwardTable ) ;
}
return dwStatus ;
# endif
}
// -------------------------------------------------------------------------------------------------------
// PLATFORM-DEPENDANT CODE
# if defined(WIN32)
DWORD get_interface_index ( struct in_addr addr ) {
// taken from (and modified)
// https://docs.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-createipforwardentry
PMIB_IPFORWARDTABLE pIpForwardTable = NULL ;
DWORD dwSize = 0 ;
BOOL bOrder = FALSE ;
DWORD dwStatus = 0 ;
DWORD mask_addr = 0 ;
DWORD max_idx = 0 ;
uint8_t bitlen , max_bitlen = 0 ;
unsigned int i ;
ipstr_t gateway_address ;
// find out how big our buffer needs to be
dwStatus = GetIpForwardTable ( pIpForwardTable , & dwSize , bOrder ) ;
if ( dwStatus = = ERROR_INSUFFICIENT_BUFFER ) {
// allocate the memory for the table
if ( ! ( pIpForwardTable = ( PMIB_IPFORWARDTABLE ) malloc ( dwSize ) ) ) {
traceEvent ( TRACE_DEBUG , " malloc failed, out of memory \n " ) ;
return EXIT_FAILURE ;
}
// now get the table
dwStatus = GetIpForwardTable ( pIpForwardTable , & dwSize , bOrder ) ;
}
if ( dwStatus ! = ERROR_SUCCESS ) {
traceEvent ( TRACE_DEBUG , " getIpForwardTable failed \n " ) ;
if ( pIpForwardTable )
free ( pIpForwardTable ) ;
return 0 ;
}
// search for the row in the table we want. The default gateway has a destination of 0.0.0.0
for ( i = 0 ; i < pIpForwardTable - > dwNumEntries ; i + + ) {
mask_addr = pIpForwardTable - > table [ i ] . dwForwardMask ;
// if same subnet ...
if ( ( mask_addr & addr . S_un . S_addr ) = = ( mask_addr & pIpForwardTable - > table [ i ] . dwForwardDest ) ) {
mask_addr = ntohl ( mask_addr ) ;
for ( bitlen = 0 ; ( int ) mask_addr < 0 ; mask_addr < < = 1 )
bitlen + + ;
if ( bitlen > max_bitlen ) {
max_bitlen = bitlen ;
max_idx = pIpForwardTable - > table [ i ] . dwForwardIfIndex ;
}
}
}
traceEvent ( TRACE_DEBUG , " found interface index %u for gateway %s " ,
max_idx , inaddrtoa ( gateway_address , addr ) ) ;
if ( pIpForwardTable ) {
free ( pIpForwardTable ) ;
}
return max_idx ;
}
}
# endif
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
@ -270,6 +411,7 @@ find_default_gateway_end:
/* adds (verb == ROUTE_ADD) or deletes (verb == ROUTE_DEL) a route */
/* adds (verb == ROUTE_ADD) or deletes (verb == ROUTE_DEL) a route */
void handle_route ( n2n_route_t * in_route , int verb ) {
void handle_route ( n2n_route_t * in_route , int verb ) {
# if defined(__linux__)
struct sockaddr_in * addr_tmp ;
struct sockaddr_in * addr_tmp ;
struct rtentry route ;
struct rtentry route ;
SOCKET sock ;
SOCKET sock ;
@ -320,6 +462,34 @@ void handle_route (n2n_route_t* in_route, int verb) {
}
}
closesocket ( sock ) ;
closesocket ( sock ) ;
# elif defined(WIN32)
// REVISIT: use 'CreateIpForwardEntry()' and 'DeleteIpForwardEntry()' [iphlpapi.h]
struct in_addr net_addr , gateway ;
char c_net_addr [ 32 ] ;
char c_gateway [ 32 ] ;
char c_interface [ 32 ] ;
char c_verb [ 32 ] ;
uint32_t mask ;
uint8_t bitlen ;
DWORD if_idx ;
char cmd [ 256 ] ;
// assemble route command components
_snprintf ( c_net_addr , sizeof ( c_net_addr ) , inet_ntoa ( in_route - > net_addr ) ) ;
_snprintf ( c_gateway , sizeof ( c_gateway ) , inet_ntoa ( in_route - > gateway ) ) ;
mask = ntohl ( in_route - > net_mask . S_un . S_addr ) ;
for ( bitlen = 0 ; ( int ) mask < 0 ; mask < < = 1 )
bitlen + + ;
if_idx = get_interface_index ( in_route - > gateway ) ;
_snprintf ( c_interface , sizeof ( c_interface ) , " if %u " , if_idx ) ;
_snprintf ( c_verb , sizeof ( c_verb ) , ( verb = = ROUTE_ADD ) ? " add " : " delete " ) ;
_snprintf ( cmd , sizeof ( cmd ) , " route %s %s/%d %s %s > nul " , c_verb , c_net_addr , bitlen , c_gateway , c_interface ) ;
traceEvent ( TRACE_INFO , " ROUTE CMD = '%s' \n " , cmd ) ;
// issue the route command
system ( cmd ) ;
# endif
}
}
@ -363,6 +533,7 @@ int same_subnet (struct in_addr addr0, struct in_addr addr1, struct in_addr subn
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
// PLATFORM-DEPENDANT CODE
SOCKET connect_to_management_port ( n2n_route_conf_t * rrr ) {
SOCKET connect_to_management_port ( n2n_route_conf_t * rrr ) {
@ -370,6 +541,23 @@ SOCKET connect_to_management_port (n2n_route_conf_t *rrr) {
SOCKET ret ;
SOCKET ret ;
struct sockaddr_in sock_addr ;
struct sockaddr_in sock_addr ;
# if defined(WIN32)
// Windows requires a call to WSAStartup() before it can work with sockets
WORD wVersionRequested ;
WSADATA wsaData ;
int err ;
// Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h
wVersionRequested = MAKEWORD ( 2 , 2 ) ;
err = WSAStartup ( wVersionRequested , & wsaData ) ;
if ( err ! = 0 ) {
// tell the user that we could not find a usable Winsock DLL
traceEvent ( TRACE_ERROR , " WSAStartup failed with error: %d \n " , err ) ;
return - 1 ;
}
# endif
ret = socket ( PF_INET , SOCK_DGRAM , 0 ) ;
ret = socket ( PF_INET , SOCK_DGRAM , 0 ) ;
if ( ( int ) ret < 0 )
if ( ( int ) ret < 0 )
return - 1 ;
return - 1 ;
@ -420,10 +608,12 @@ int get_addr_from_json (struct in_addr *addr, json_object_t *json, char *key, in
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
// PLATFORM-DEPENDANT CODE
# if !defined(WIN32)
// taken from https://web.archive.org/web/20170407122137/http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/
// taken from https://web.archive.org/web/20170407122137/http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/
int kbhit ( ) {
int _ kbhit ( ) {
struct timeval tv ;
struct timeval tv ;
fd_set fds ;
fd_set fds ;
@ -436,6 +626,7 @@ int kbhit () {
return FD_ISSET ( STDIN_FILENO , & fds ) ;
return FD_ISSET ( STDIN_FILENO , & fds ) ;
}
}
# endif
// -------------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------------
@ -446,7 +637,8 @@ static void help (int level) {
if ( level = = 0 ) return ; /* no help required */
if ( level = = 0 ) return ; /* no help required */
printf ( " n2n-route [-t <manangement_port>] [-p <management_port_password>] [-v] [-V] "
printf ( " n2n-route [-t <manangement_port>] [-p <management_port_password>] [-v] [-V] "
" \n [-g <default gateway>] [-n <network address>/bitlen] <vpn gateway> "
" \n [-g <default gateway>] [-n <network address>/bitlen[:gateway]] "
" \n <vpn gateway> "
" \n "
" \n "
" \n This tool sets new routes for all the traffic to be routed via the "
" \n This tool sets new routes for all the traffic to be routed via the "
" \n <vpn gateway> and polls the management port of a local n2n edge for "
" \n <vpn gateway> and polls the management port of a local n2n edge for "
@ -454,7 +646,7 @@ static void help (int level) {
" \n gateway. Adapt port (default: %d) and password (default: '%s') "
" \n gateway. Adapt port (default: %d) and password (default: '%s') "
" \n to match your edge's configuration. "
" \n to match your edge's configuration. "
" \n \n If no <default gateway> provided, the tool will try to auto-detect. "
" \n \n If no <default gateway> provided, the tool will try to auto-detect. "
" \n \n To not route all traffic through vpn, inidicate the networks to be "
" \n \n To only route some traffic through vpn, inidicate the networks to be "
" \n routed with '-n' option and use as many as required. "
" \n routed with '-n' option and use as many as required. "
" \n \n Verbosity can be increased or decreased with -v or -V , repeat as "
" \n \n Verbosity can be increased or decreased with -v or -V , repeat as "
" \n as needed. "
" \n as needed. "
@ -495,14 +687,18 @@ static int set_option (n2n_route_conf_t *rrr, int optkey, char *optargument) {
}
}
case ' n ' : /* user-provided network to be routed */ {
case ' n ' : /* user-provided network to be routed */ {
char cidr_net [ 64 ] , bitlen ;
char cidr_net [ 64 ] , bitlen , gateway [ 64 ] ;
n2n_route_t * route ;
n2n_route_t * route ;
struct in_addr mask ;
struct in_addr mask ;
int ret ;
if ( sscanf ( optargument , " %63[^/]/%hhd " , cidr_net , & bitlen ) ! = 2 ) {
gateway [ 0 ] = ' \0 ' ; // optional parameter
traceEvent ( TRACE_WARNING , " bad cidr network format '%d' " , optargument ) ;
ret = sscanf ( optargument , " %63[^/]/%hhd:%63s " , cidr_net , & bitlen , gateway ) ;
if ( ( ret < 2 ) | | ( ret > 3 ) ) {
traceEvent ( TRACE_WARNING , " bad cidr network format '%s' " , optargument ) ;
return 1 ;
return 1 ;
}
}
if ( ( bitlen < 0 ) | | ( bitlen > 32 ) ) {
if ( ( bitlen < 0 ) | | ( bitlen > 32 ) ) {
traceEvent ( TRACE_WARNING , " bad prefix '%d' in '%s' " , bitlen , optargument ) ;
traceEvent ( TRACE_WARNING , " bad prefix '%d' in '%s' " , bitlen , optargument ) ;
return 1 ;
return 1 ;
@ -511,14 +707,19 @@ static int set_option (n2n_route_conf_t *rrr, int optkey, char *optargument) {
traceEvent ( TRACE_WARNING , " bad network '%s' in '%s' " , cidr_net , optargument ) ;
traceEvent ( TRACE_WARNING , " bad network '%s' in '%s' " , cidr_net , optargument ) ;
return 1 ;
return 1 ;
}
}
if ( gateway [ 0 ] ) {
traceEvent ( TRACE_NORMAL , " routing %s/%d " , cidr_net , bitlen ) ;
if ( ! inet_address_valid ( inet_address ( gateway ) ) ) {
traceEvent ( TRACE_WARNING , " bad gateway '%s' in '%s' " , gateway , optargument ) ;
return 1 ;
}
}
traceEvent ( TRACE_NORMAL , " routing %s/%d via %s " , cidr_net , bitlen , gateway [ 0 ] ? gateway : " vpn gateway " ) ;
route = calloc ( 1 , sizeof ( * route ) ) ;
route = calloc ( 1 , sizeof ( * route ) ) ;
if ( route ) {
if ( route ) {
mask . s_addr = htonl ( bitlen2mask ( bitlen ) ) ;
mask . s_addr = htonl ( bitlen2mask ( bitlen ) ) ;
// gateway is unknown at this point, will be rectified later
// gateway might be unknown at this point, will be rectified later
fill_route ( route , inet_address ( cidr_net ) , mask , inet_address ( " " ) ) ;
fill_route ( route , inet_address ( cidr_net ) , mask , inet_address ( gateway ) ) ;
HASH_ADD ( hh , rrr - > routes , net_addr , sizeof ( route - > net_addr ) , route ) ;
HASH_ADD ( hh , rrr - > routes , net_addr , sizeof ( route - > net_addr ) , route ) ;
// will be added to system table later
// will be added to system table later
}
}
@ -617,11 +818,15 @@ int main (int argc, char* argv[]) {
fill_route ( route , inet_address ( UPPER_HALF ) , inet_address ( MASK_HALF ) , rrr . gateway_vpn ) ;
fill_route ( route , inet_address ( UPPER_HALF ) , inet_address ( MASK_HALF ) , rrr . gateway_vpn ) ;
HASH_ADD ( hh , rrr . routes , net_addr , sizeof ( route - > net_addr ) , route ) ;
HASH_ADD ( hh , rrr . routes , net_addr , sizeof ( route - > net_addr ) , route ) ;
}
}
} else {
traceEvent ( TRACE_WARNING , " only user-supplied networks will be routed, not the complete traffic " ) ;
}
}
// set gateway for all so far present routes as '-n'-provided do not have it yet,
// set gateway for all so far present routes if '-n'-provided do not have it yet,
// make them UNPURGEABLE and add them to system table
// make them UNPURGEABLE and add them to system table
HASH_ITER ( hh , rrr . routes , route , tmp_route ) {
HASH_ITER ( hh , rrr . routes , route , tmp_route ) {
route - > gateway = rrr . gateway_vpn ;
if ( ! inet_address_valid ( route - > gateway ) ) {
route - > gateway = rrr . gateway_vpn ;
}
route - > purgeable = UNPURGEABLE ;
route - > purgeable = UNPURGEABLE ;
handle_route ( route , ROUTE_ADD ) ;
handle_route ( route , ROUTE_ADD ) ;
}
}
@ -658,7 +863,7 @@ reset_main_loop:
// read answer packet by packet which are only accepted if a corresponding request was sent before
// read answer packet by packet which are only accepted if a corresponding request was sent before
// of which we know about by having set the related tag, tag_info or tag_route_ip resp.
// of which we know about by having set the related tag, tag_info or tag_route_ip resp.
// a valid edge ip address indicates that we have seen a valid answer to the info request
// a valid edge ip address indicates that we have seen a valid answer to the info request
while ( keep_running & & ! kbhit ( ) ) {
while ( keep_running & & ! _ kbhit( ) ) {
// current time
// current time
now = time ( NULL ) ;
now = time ( NULL ) ;
@ -846,16 +1051,16 @@ end_route_tool:
}
}
# else /* ifdef __linux__ -- currently, Linux only */
# else /* if defined(__linux__) || defined(WIN32) -- currently, Linux and Windows only */
int main ( int argc , char * argv [ ] ) {
int main ( int argc , char * argv [ ] ) {
traceEvent ( TRACE_WARNING , " currently, only Linux is supported " ) ;
traceEvent ( TRACE_WARNING , " currently, only Linux and W indow s are supported " ) ;
traceEvent ( TRACE_WARNING , " if you want to port to other OS, please find the source code having clearly marked the platform-dependant portions " ) ;
traceEvent ( TRACE_WARNING , " if you want to port to other OS, please find the source code having clearly marked the platform-dependant portions " ) ;
return 0 ;
return 0 ;
}
}
# endif /* ifdef __linux__ -- currently, Linux only */
# endif /* if defined ( __linux__) || defined(WIN32) -- currently, Linux and Windows only */