edge_utils.c (n2n-2.8) | : | edge_utils.c (n2n-3.0) | ||
---|---|---|---|---|
/** | /** | |||
* (C) 2007-20 - ntop.org and contributors | * (C) 2007-21 - ntop.org and contributors | |||
* | * | |||
* This program is free software; you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 3 of the License, or | * the Free Software Foundation; either version 3 of the License, or | |||
* (at your option) any later version. | * (at your option) any later version. | |||
* | * | |||
* This program is distributed in the hope that it will be useful, | * This program is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | * GNU General Public License for more details. | |||
* | * | |||
* You should have received a copy of the GNU General Public License | * You should have received a copy of the GNU General Public License | |||
* along with this program; if not see see <http://www.gnu.org/licenses/> | * along with this program; if not see see <http://www.gnu.org/licenses/> | |||
* | * | |||
*/ | */ | |||
#include "n2n.h" | #include "n2n.h" | |||
#include "network_traffic_filter.h" | ||||
#include "edge_utils_win32.h" | #include "edge_utils_win32.h" | |||
/* heap allocation for compression as per lzo example doc */ | /* heap allocation for compression as per lzo example doc */ | |||
#define HEAP_ALLOC(var,size) lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lz o_align_t) - 1)) / sizeof(lzo_align_t) ] | #define HEAP_ALLOC(var,size) lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lz o_align_t) - 1)) / sizeof(lzo_align_t) ] | |||
static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS); | static HEAP_ALLOC(wrkmem, LZO1X_1_MEM_COMPRESS); | |||
/* ************************************** */ | /* ************************************** */ | |||
static const char * supernode_ip(const n2n_edge_t * eee); | int resolve_create_thread (n2n_resolve_parameter_t **param, struct peer_info *sn | |||
static void send_register(n2n_edge_t *eee, const n2n_sock_t *remote_peer, const | _list); | |||
n2n_mac_t peer_mac); | int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, t | |||
static void check_peer_registration_needed(n2n_edge_t * eee, | ime_t now); | |||
uint8_t from_supernode, | int resolve_cancel_thread (n2n_resolve_parameter_t *param); | |||
const n2n_mac_t mac, | ||||
const n2n_sock_t * peer); | static const char * supernode_ip (const n2n_edge_t * eee); | |||
static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port, | static void send_register (n2n_edge_t *eee, const n2n_sock_t *remote_peer, const | |||
uint8_t tos); | n2n_mac_t peer_mac, n2n_cookie_t cookie); | |||
static int edge_init_routes(n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_r | ||||
outes); | static void check_peer_registration_needed (n2n_edge_t *eee, | |||
static void edge_cleanup_routes(n2n_edge_t *eee); | uint8_t from_supernode, | |||
static int supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn); | uint8_t via_multicast, | |||
static void check_known_peer_sock_change(n2n_edge_t * eee, | const n2n_mac_t mac, | |||
uint8_t from_supernode, | const n2n_cookie_t cookie, | |||
const n2n_mac_t mac, | const n2n_ip_subnet_t *dev_addr, | |||
const n2n_sock_t * peer, | const n2n_desc_t *dev_desc, | |||
time_t when); | const n2n_sock_t *peer); | |||
static int edge_init_sockets (n2n_edge_t *eee); | ||||
int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) | ||||
; | ||||
static void edge_cleanup_routes (n2n_edge_t *eee); | ||||
static void check_known_peer_sock_change (n2n_edge_t *eee, | ||||
uint8_t from_supernode, | ||||
uint8_t via_multicast, | ||||
const n2n_mac_t mac, | ||||
const n2n_ip_subnet_t *dev_addr, | ||||
const n2n_desc_t *dev_desc, | ||||
const n2n_sock_t *peer, | ||||
time_t when); | ||||
/* ************************************** */ | /* ************************************** */ | |||
int edge_verify_conf(const n2n_edge_conf_t *conf) { | int edge_verify_conf (const n2n_edge_conf_t *conf) { | |||
if(conf->community_name[0] == 0) | ||||
return(-1); | if(conf->community_name[0] == 0) | |||
return -1; | ||||
// REVISIT: are the following two conditions equal? if so, remove one. but n | ||||
ote that sn_num is used elsewhere | ||||
if(conf->sn_num == 0) | ||||
return -2; | ||||
if(HASH_COUNT(conf->supernodes) == 0) | ||||
return -5; | ||||
if(conf->register_interval < 1) | ||||
return -3; | ||||
if(((conf->encrypt_key == NULL) && (conf->transop_id != N2N_TRANSFORM_ID_NUL | ||||
L)) || | ||||
((conf->encrypt_key != NULL) && (conf->transop_id == N2N_TRANSFORM_ID_NUL | ||||
L))) | ||||
return -4; | ||||
if(conf->sn_num == 0) | return 0; | |||
return(-2); | ||||
if(conf->register_interval < 1) | ||||
return(-3); | ||||
if(((conf->encrypt_key == NULL) && (conf->transop_id != N2N_TRANSFORM_ID_NULL) | ||||
) || | ||||
((conf->encrypt_key != NULL) && (conf->transop_id == N2N_TRANSFORM_ID_NULL) | ||||
)) | ||||
return(-4); | ||||
return(0); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void edge_set_callbacks(n2n_edge_t *eee, const n2n_edge_callbacks_t *callbacks) | void edge_set_callbacks (n2n_edge_t *eee, const n2n_edge_callbacks_t *callbacks) | |||
{ | { | |||
memcpy(&eee->cb, callbacks, sizeof(n2n_edge_callbacks_t)); | ||||
memcpy(&eee->cb, callbacks, sizeof(n2n_edge_callbacks_t)); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void edge_set_userdata(n2n_edge_t *eee, void *user_data) { | void edge_set_userdata (n2n_edge_t *eee, void *user_data) { | |||
eee->user_data = user_data; | ||||
eee->user_data = user_data; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void* edge_get_userdata(n2n_edge_t *eee) { | void* edge_get_userdata (n2n_edge_t *eee) { | |||
return(eee->user_data); | ||||
return(eee->user_data); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
int edge_get_n2n_socket(n2n_edge_t *eee) { | int edge_get_n2n_socket (n2n_edge_t *eee) { | |||
return(eee->udp_sock); | ||||
return(eee->sock); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
int edge_get_management_socket(n2n_edge_t *eee) { | int edge_get_management_socket (n2n_edge_t *eee) { | |||
return(eee->udp_mgmt_sock); | ||||
return(eee->udp_mgmt_sock); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
const char* transop_str(enum n2n_transform tr) { | const char* transop_str (enum n2n_transform tr) { | |||
switch(tr) { | ||||
case N2N_TRANSFORM_ID_NULL: return("null"); | switch(tr) { | |||
case N2N_TRANSFORM_ID_TWOFISH: return("twofish"); | case N2N_TRANSFORM_ID_NULL: return("null"); | |||
case N2N_TRANSFORM_ID_AESCBC: return("AES-CBC"); | case N2N_TRANSFORM_ID_TWOFISH: return("Twofish"); | |||
case N2N_TRANSFORM_ID_CHACHA20:return("ChaCha20"); | case N2N_TRANSFORM_ID_AES: return("AES"); | |||
case N2N_TRANSFORM_ID_SPECK :return("Speck"); | case N2N_TRANSFORM_ID_CHACHA20:return("ChaCha20"); | |||
default: return("invalid"); | case N2N_TRANSFORM_ID_SPECK: return("Speck"); | |||
}; | default: return("invalid"); | |||
}; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
const char* compression_str(uint8_t cmpr) { | const char* compression_str (uint8_t cmpr) { | |||
switch(cmpr) { | ||||
case N2N_COMPRESSION_ID_NONE: return("none"); | ||||
case N2N_COMPRESSION_ID_LZO: return("lzo1x"); | ||||
#ifdef HAVE_LIBZSTD | switch(cmpr) { | |||
case N2N_COMPRESSION_ID_ZSTD: return("zstd"); | case N2N_COMPRESSION_ID_NONE: return("none"); | |||
#endif | case N2N_COMPRESSION_ID_LZO: return("lzo1x"); | |||
default: return("invalid"); | case N2N_COMPRESSION_ID_ZSTD: return("zstd"); | |||
}; | default: return("invalid"); | |||
}; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Destination 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF is multicast ethernet. | /** Destination 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF is multicast ethernet. | |||
*/ | */ | |||
static int is_ethMulticast(const void * buf, size_t bufsize) { | static int is_ethMulticast (const void * buf, size_t bufsize) { | |||
int retval = 0; | ||||
/* Match 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF */ | int retval = 0; | |||
if(bufsize >= sizeof(ether_hdr_t)) { | ||||
/* copy to aligned memory */ | /* Match 01:00:5E:00:00:00 - 01:00:5E:7F:FF:FF */ | |||
ether_hdr_t eh; | if(bufsize >= sizeof(ether_hdr_t)) { | |||
memcpy(&eh, buf, sizeof(ether_hdr_t)); | /* copy to aligned memory */ | |||
ether_hdr_t eh; | ||||
memcpy(&eh, buf, sizeof(ether_hdr_t)); | ||||
if((0x01 == eh.dhost[0]) && | if((0x01 == eh.dhost[0]) && | |||
(0x00 == eh.dhost[1]) && | (0x00 == eh.dhost[1]) && | |||
(0x5E == eh.dhost[2]) && | (0x5E == eh.dhost[2]) && | |||
(0 == (0x80 & eh.dhost[3]))) | (0 == (0x80 & eh.dhost[3]))) | |||
retval = 1; /* This is an ethernet multicast packet [RFC1112]. */ | retval = 1; /* This is an ethernet multicast packet [RFC1112]. */ | |||
} | } | |||
return retval; | return retval; | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Destination MAC 33:33:0:00:00:00 - 33:33:FF:FF:FF:FF is reserved for IPv6 | /** Destination MAC 33:33:0:00:00:00 - 33:33:FF:FF:FF:FF is reserved for IPv6 | |||
* neighbour discovery. | * neighbour discovery. | |||
*/ | */ | |||
static int is_ip6_discovery(const void * buf, size_t bufsize) { | static int is_ip6_discovery (const void * buf, size_t bufsize) { | |||
int retval = 0; | ||||
if(bufsize >= sizeof(ether_hdr_t)) { | int retval = 0; | |||
/* copy to aligned memory */ | ||||
ether_hdr_t eh; | if(bufsize >= sizeof(ether_hdr_t)) { | |||
/* copy to aligned memory */ | ||||
ether_hdr_t eh; | ||||
memcpy(&eh, buf, sizeof(ether_hdr_t)); | memcpy(&eh, buf, sizeof(ether_hdr_t)); | |||
if((0x33 == eh.dhost[0]) && (0x33 == eh.dhost[1])) | if((0x33 == eh.dhost[0]) && (0x33 == eh.dhost[1])) | |||
retval = 1; /* This is an IPv6 multicast packet [RFC2464]. */ | retval = 1; /* This is an IPv6 multicast packet [RFC2464]. */ | |||
} | } | |||
return retval; | ||||
return retval; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Initialise an edge to defaults. | // reset number of supernode connection attempts: try only once for already more | |||
* | realiable tcp connections | |||
* This also initialises the NULL transform operation opstruct. | void reset_sup_attempts (n2n_edge_t *eee) { | |||
*/ | ||||
n2n_edge_t* edge_init(const tuntap_dev *dev, const n2n_edge_conf_t *conf, int *r | ||||
v) { | ||||
n2n_transform_t transop_id = conf->transop_id; | ||||
n2n_edge_t *eee = calloc(1, sizeof(n2n_edge_t)); | ||||
int rc = -1, i; | ||||
if((rc = edge_verify_conf(conf)) != 0) { | ||||
traceEvent(TRACE_ERROR, "Invalid configuration"); | ||||
goto edge_init_error; | ||||
} | ||||
if(!eee) { | ||||
traceEvent(TRACE_ERROR, "Cannot allocate memory"); | ||||
goto edge_init_error; | ||||
} | ||||
#ifdef WIN32 | eee->sup_attempts = (eee->conf.connect_tcp) ? 1 : N2N_EDGE_SUP_ATTEMPTS; | |||
initWin32(); | } | |||
#endif | ||||
memcpy(&eee->conf, conf, sizeof(*conf)); | // detect local IP address by probing a connection to the supernode | |||
memcpy(&eee->device, dev, sizeof(*dev)); | static int detect_local_ip_address (n2n_sock_t* out_sock, const n2n_edge_t* eee) | |||
eee->start_time = time(NULL); | { | |||
eee->known_peers = NULL; | struct sockaddr_in local_sock; | |||
eee->pending_peers = NULL; | struct sockaddr_in sn_sock; | |||
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; | socklen_t sock_len = sizeof(local_sock); | |||
eee->sn_last_valid_time_stamp = initial_time_stamp (); | SOCKET probe_sock; | |||
int ret = 0; | ||||
pearson_hash_init(); | out_sock->family = AF_INVALID; | |||
if(eee->conf.compression == N2N_COMPRESSION_ID_LZO) | // always detetct local port even/especially if chosen by OS... | |||
if(lzo_init() != LZO_E_OK) { | if((getsockname(eee->sock, (struct sockaddr *)&local_sock, &sock_len) == 0) | |||
traceEvent(TRACE_ERROR, "LZO compression error"); | && (local_sock.sin_family == AF_INET) | |||
goto edge_init_error; | && (sock_len == sizeof(local_sock))) | |||
} | // remember the port number | |||
out_sock->port = ntohs(local_sock.sin_port); | ||||
else | ||||
ret = -1; | ||||
#ifdef N2N_HAVE_ZSTD | // probe for local IP address | |||
// zstd does not require initialization. if it were required, this would be a | probe_sock = socket(PF_INET, SOCK_DGRAM, 0); | |||
good place | // connecting the UDP socket makes getsockname read the local address it use | |||
#endif | s to connect (to the sn in this case); | |||
// we cannot do it with the real (eee->sock) socket because socket does not | ||||
accept any conenction from elsewhere then, | ||||
// e.g. from another edge instead of the supernode; as re-connecting to AF_U | ||||
NSPEC might not work to release the socket | ||||
// on non-UNIXoids, we use a temporary socket | ||||
if((int)probe_sock >= 0) { | ||||
fill_sockaddr((struct sockaddr*)&sn_sock, sizeof(sn_sock), &eee->curr_sn | ||||
->sock); | ||||
if(connect(probe_sock, (struct sockaddr *)&sn_sock, sizeof(sn_sock)) == | ||||
0) { | ||||
if((getsockname(probe_sock, (struct sockaddr *)&local_sock, &sock_le | ||||
n) == 0) | ||||
&& (local_sock.sin_family == AF_INET) | ||||
&& (sock_len == sizeof(local_sock))) { | ||||
memcpy(&(out_sock->addr.v4), &(local_sock.sin_addr.s_addr), IPV4 | ||||
_SIZE); | ||||
} else | ||||
ret = -4; | ||||
} else | ||||
ret = -3; | ||||
closesocket(probe_sock); | ||||
} else | ||||
ret = -2; | ||||
out_sock->family = AF_INET; | ||||
return ret; | ||||
} | ||||
// open socket, close it before if TCP | ||||
// in case of TCP, 'connect()' is required | ||||
int supernode_connect (n2n_edge_t *eee) { | ||||
int sockopt; | ||||
struct sockaddr_in sn_sock; | ||||
n2n_sock_t local_sock; | ||||
n2n_sock_str_t sockbuf; | ||||
if((eee->conf.connect_tcp) && (eee->sock >= 0)) { | ||||
closesocket(eee->sock); | ||||
eee->sock = -1; | ||||
} | ||||
if(eee->sock < 0) { | ||||
if(eee->conf.local_port > 0) | ||||
traceEvent(TRACE_NORMAL, "binding to local port %d", | ||||
(eee->conf.connect_tcp) ? 0 : eee->conf.loc | ||||
al_port); | ||||
eee->sock = open_socket((eee->conf.connect_tcp) ? 0 : eee->conf.local_p | ||||
ort, | ||||
eee->conf.bind_address, | ||||
eee->conf.connect_tcp); | ||||
if(eee->sock < 0) { | ||||
traceEvent(TRACE_ERROR, "failed to bind main UDP port %u", | ||||
(eee->conf.connect_tcp) ? 0 : eee->conf.loc | ||||
al_port); | ||||
return -1; | ||||
} | ||||
for(i=0; i<conf->sn_num; ++i) | fill_sockaddr((struct sockaddr*)&sn_sock, sizeof(sn_sock), &eee->curr_sn | |||
traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (conf->sn_ip_array[i])); | ->sock); | |||
/* Set the active supernode */ | // set tcp socket to O_NONBLOCK so connect does not hang | |||
supernode2addr(&(eee->supernode), conf->sn_ip_array[eee->sn_idx]); | // requires checking the socket for readiness before sending and recevin | |||
g | ||||
if(eee->conf.connect_tcp) { | ||||
#ifdef WIN32 | ||||
u_long value = 1; | ||||
ioctlsocket(eee->sock, FIONBIO, &value); | ||||
#else | ||||
fcntl(eee->sock, F_SETFL, O_NONBLOCK); | ||||
#endif | ||||
if((connect(eee->sock, (struct sockaddr*)&(sn_sock), sizeof(struct s | ||||
ockaddr)) < 0) | ||||
&& (errno != EINPROGRESS)) { | ||||
eee->sock = -1; | ||||
return -1; | ||||
} | ||||
} | ||||
/* Set active transop */ | if(eee->conf.tos) { | |||
switch(transop_id) { | /* https://www.tucny.com/Home/dscp-tos */ | |||
case N2N_TRANSFORM_ID_TWOFISH: | sockopt = eee->conf.tos; | |||
rc = n2n_transop_twofish_init(&eee->conf, &eee->transop); | ||||
break; | if(setsockopt(eee->sock, IPPROTO_IP, IP_TOS, (char *)&sockopt, sizeo | |||
#ifdef N2N_HAVE_AES | f(sockopt)) == 0) | |||
case N2N_TRANSFORM_ID_AESCBC: | traceEvent(TRACE_INFO, "TOS set to 0x%x", eee->conf.tos); | |||
rc = n2n_transop_aes_cbc_init(&eee->conf, &eee->transop); | else | |||
break; | traceEvent(TRACE_WARNING, "could not set TOS 0x%x[%d]: %s", eee- | |||
#endif | >conf.tos, errno, strerror(errno)); | |||
#ifdef HAVE_OPENSSL_1_1 | } | |||
case N2N_TRANSFORM_ID_CHACHA20: | #ifdef IP_PMTUDISC_DO | |||
rc = n2n_transop_cc20_init(&eee->conf, &eee->transop); | sockopt = (eee->conf.disable_pmtu_discovery) ? IP_PMTUDISC_DONT : IP_PMT | |||
break; | UDISC_DO; | |||
#endif | ||||
case N2N_TRANSFORM_ID_SPECK: | ||||
rc = n2n_transop_speck_init(&eee->conf, &eee->transop); | ||||
break; | ||||
default: | ||||
rc = n2n_transop_null_init(&eee->conf, &eee->transop); | ||||
} | ||||
if((rc < 0) || (eee->transop.fwd == NULL) || (eee->transop.transform_id != tra | ||||
nsop_id)) { | ||||
traceEvent(TRACE_ERROR, "Transop init failed"); | ||||
goto edge_init_error; | ||||
} | ||||
/* Set the key schedule (context) for header encryption if enabled */ | ||||
if(conf->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
traceEvent(TRACE_NORMAL, "Header encryption is enabled."); | ||||
packet_header_setup_key ((char *)(conf->community_name), &(eee->conf.header_ | ||||
encryption_ctx),&(eee->conf.header_iv_ctx)); | ||||
} | ||||
if(eee->transop.no_encryption) | ||||
traceEvent(TRACE_WARNING, "Encryption is disabled in edge"); | ||||
if(edge_init_sockets(eee, conf->local_port, conf->mgmt_port, conf->tos) < 0) { | ||||
traceEvent(TRACE_ERROR, "socket setup failed"); | ||||
goto edge_init_error; | ||||
} | ||||
if(edge_init_routes(eee, conf->routes, conf->num_routes) < 0) { | ||||
traceEvent(TRACE_ERROR, "routes setup failed"); | ||||
goto edge_init_error; | ||||
} | ||||
//edge_init_success: | ||||
*rv = 0; | ||||
return(eee); | ||||
edge_init_error: | if(setsockopt(eee->sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(s | |||
if(eee) | ockopt)) < 0) | |||
free(eee); | traceEvent(TRACE_WARNING, "could not %s PMTU discovery[%d]: %s", | |||
*rv = rc; | (eee->conf.disable_pmtu_discovery) ? "disable" : "enable" | |||
return(NULL); | , errno, strerror(errno)); | |||
} | else | |||
traceEvent(TRACE_INFO, "PMTU discovery %s", (eee->conf.disable_pmtu_ | ||||
discovery) ? "disabled" : "enabled"); | ||||
#endif | ||||
memset(&local_sock, 0, sizeof(n2n_sock_t)); | ||||
if(detect_local_ip_address(&local_sock, eee) == 0) { | ||||
// always overwrite local port even/especially if chosen by OS... | ||||
eee->conf.preferred_sock.port = local_sock.port; | ||||
// only if auto-detection mode, ... | ||||
if(eee->conf.preferred_sock_auto) { | ||||
// ... overwrite IP address, too (whole socket struct here) | ||||
memcpy(&eee->conf.preferred_sock, &local_sock, sizeof(n2n_sock_t | ||||
)); | ||||
traceEvent(TRACE_INFO, "determined local socket [%s]", | ||||
sock_to_cstr(sockbuf, &local_sock)); | ||||
} | ||||
} | ||||
/* ************************************** */ | if(eee->cb.sock_opened) | |||
eee->cb.sock_opened(eee); | ||||
} | ||||
static int find_and_remove_peer(struct peer_info **head, const n2n_mac_t mac) { | return 0; | |||
struct peer_info *peer; | } | |||
HASH_FIND_PEER(*head, mac, peer); | // always closes the socket | |||
if(peer) { | void supernode_disconnect (n2n_edge_t *eee) { | |||
HASH_DEL(*head, peer); | ||||
free(peer); | ||||
return(1); | ||||
} | ||||
return(0); | if(eee->sock >= 0) { | |||
closesocket(eee->sock); | ||||
eee->sock = -1; | ||||
} | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static uint32_t localhost_v4 = 0x7f000001; | /** Initialise an edge to defaults. | |||
static uint8_t localhost_v6[IPV6_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; | * | |||
* This also initialises the NULL transform operation opstruct. | ||||
/* Exclude localhost as it may be received when an edge node runs | ||||
* in the same supernode host. | ||||
*/ | */ | |||
static int is_valid_peer_sock(const n2n_sock_t *sock) { | n2n_edge_t* edge_init (const n2n_edge_conf_t *conf, int *rv) { | |||
switch(sock->family) { | ||||
case AF_INET: | n2n_transform_t transop_id = conf->transop_id; | |||
{ | n2n_edge_t *eee = calloc(1, sizeof(n2n_edge_t)); | |||
uint32_t *a = (uint32_t*)sock->addr.v4; | int rc = -1, i = 0; | |||
struct peer_info *scan, *tmp; | ||||
uint8_t tmp_key[N2N_AUTH_CHALLENGE_SIZE]; | ||||
if(*a != htonl(localhost_v4)) | if((rc = edge_verify_conf(conf)) != 0) { | |||
return(1); | traceEvent(TRACE_ERROR, "invalid configuration"); | |||
goto edge_init_error; | ||||
} | } | |||
break; | ||||
case AF_INET6: | if(!eee) { | |||
if(memcmp(sock->addr.v6, localhost_v6, IPV6_SIZE)) | traceEvent(TRACE_ERROR, "cannot allocate memory"); | |||
return(1); | goto edge_init_error; | |||
break; | } | |||
} | ||||
return(0); | memcpy(&eee->conf, conf, sizeof(*conf)); | |||
} | eee->curr_sn = eee->conf.supernodes; | |||
eee->start_time = time(NULL); | ||||
/* ***************************************************** */ | eee->known_peers = NULL; | |||
eee->pending_peers = NULL; | ||||
reset_sup_attempts(eee); | ||||
/** Resolve the supernode IP address. | sn_selection_criterion_common_data_default(eee); | |||
* | ||||
* REVISIT: This is a really bad idea. The edge will block completely while the | ||||
* hostname resolution is performed. This could take 15 seconds. | ||||
*/ | ||||
static int supernode2addr(n2n_sock_t * sn, const n2n_sn_name_t addrIn) { | ||||
n2n_sn_name_t addr; | ||||
const char *supernode_host; | ||||
int rv = 0; | ||||
memcpy(addr, addrIn, N2N_EDGE_SN_HOST_SIZE); | ||||
supernode_host = strtok(addr, ":"); | ||||
if(supernode_host) { | ||||
in_addr_t sn_addr; | ||||
char *supernode_port = strtok(NULL, ":"); | ||||
const struct addrinfo aihints = {0, PF_INET, 0, 0, 0, NULL, NULL, NULL}; | ||||
struct addrinfo * ainfo = NULL; | ||||
int nameerr; | ||||
if(supernode_port) | pearson_hash_init(); | |||
sn->port = atoi(supernode_port); | ||||
else | if(eee->conf.compression == N2N_COMPRESSION_ID_LZO) | |||
traceEvent(TRACE_WARNING, "Bad supernode parameter (-l <host:port>) %s %s: | if(lzo_init() != LZO_E_OK) { | |||
%s", | traceEvent(TRACE_ERROR, "LZO compression error"); | |||
addr, supernode_host, supernode_port); | goto edge_init_error; | |||
} | ||||
#ifdef N2N_HAVE_ZSTD | ||||
// zstd does not require initialization. if it were required, this would be | ||||
a good place | ||||
#endif | ||||
nameerr = getaddrinfo(supernode_host, NULL, &aihints, &ainfo); | traceEvent(TRACE_NORMAL, "number of supernodes in the list: %d\n", HASH_COUN | |||
T(eee->conf.supernodes)); | ||||
HASH_ITER(hh, eee->conf.supernodes, scan, tmp) { | ||||
traceEvent(TRACE_NORMAL, "supernode %u => %s\n", i, (scan->ip_addr)); | ||||
i++; | ||||
} | ||||
/* Set active transop */ | ||||
switch(transop_id) { | ||||
case N2N_TRANSFORM_ID_TWOFISH: | ||||
rc = n2n_transop_tf_init(&eee->conf, &eee->transop); | ||||
break; | ||||
case N2N_TRANSFORM_ID_AES: | ||||
rc = n2n_transop_aes_init(&eee->conf, &eee->transop); | ||||
break; | ||||
case N2N_TRANSFORM_ID_CHACHA20: | ||||
rc = n2n_transop_cc20_init(&eee->conf, &eee->transop); | ||||
break; | ||||
case N2N_TRANSFORM_ID_SPECK: | ||||
rc = n2n_transop_speck_init(&eee->conf, &eee->transop); | ||||
break; | ||||
default: | ||||
rc = n2n_transop_null_init(&eee->conf, &eee->transop); | ||||
} | ||||
if((rc < 0) || (eee->transop.fwd == NULL) || (eee->transop.transform_id != t | ||||
ransop_id)) { | ||||
traceEvent(TRACE_ERROR, "transop init failed"); | ||||
goto edge_init_error; | ||||
} | ||||
// set the key schedule (context) for header encryption if enabled | ||||
if(conf->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
traceEvent(TRACE_NORMAL, "Header encryption is enabled."); | ||||
packet_header_setup_key((char *)(eee->conf.community_name), | ||||
&(eee->conf.header_encryption_ctx_static), | ||||
&(eee->conf.header_encryption_ctx_dynamic), | ||||
&(eee->conf.header_iv_ctx_static), | ||||
&(eee->conf.header_iv_ctx_dynamic)); | ||||
// in case of user/password auth, initialize a random dynamic key to pre | ||||
vent | ||||
// unintentional communication with only-header-encrypted community; wil | ||||
l be | ||||
// overwritten by legit key later | ||||
if(conf->shared_secret) { | ||||
memrnd(tmp_key, N2N_AUTH_CHALLENGE_SIZE); | ||||
packet_header_change_dynamic_key(tmp_key, | ||||
&(eee->conf.header_encryption_ctx_d | ||||
ynamic), | ||||
&(eee->conf.header_iv_ctx_dynamic)) | ||||
; | ||||
} | ||||
} | ||||
if(0 == nameerr) | // setup authentication scheme | |||
{ | if(!conf->shared_secret) { | |||
struct sockaddr_in * saddr; | // id-based scheme | |||
eee->conf.auth.scheme = n2n_auth_simple_id; | ||||
// random authentication token | ||||
memrnd(eee->conf.auth.token, N2N_AUTH_ID_TOKEN_SIZE); | ||||
eee->conf.auth.token_size = N2N_AUTH_ID_TOKEN_SIZE; | ||||
} else { | ||||
// user-password scheme | ||||
eee->conf.auth.scheme = n2n_auth_user_password; | ||||
// 'token' stores public key and the last random challenge being set upo | ||||
n sending REGISTER_SUPER | ||||
memcpy(eee->conf.auth.token, eee->conf.public_key, N2N_PRIVATE_PUBLIC_KE | ||||
Y_SIZE); | ||||
// random part of token (challenge) will be generated and filled in at e | ||||
ach REGISTER_SUPER | ||||
eee->conf.auth.token_size = N2N_AUTH_PW_TOKEN_SIZE; | ||||
// make sure that only stream ciphers are being used | ||||
if((transop_id != N2N_TRANSFORM_ID_CHACHA20) | ||||
&& (transop_id != N2N_TRANSFORM_ID_SPECK)) { | ||||
traceEvent(TRACE_ERROR, "user-password authentication requires ChaCh | ||||
a20 (-A4) or SPECK (-A5) to be used."); | ||||
goto edge_init_error; | ||||
} | ||||
} | ||||
/* ainfo s the head of a linked list if non-NULL. */ | if(eee->transop.no_encryption) | |||
if(ainfo && (PF_INET == ainfo->ai_family)) | traceEvent(TRACE_WARNING, "encryption is disabled in edge"); | |||
{ | ||||
/* It is definitely and IPv4 address -> sockaddr_in */ | ||||
saddr = (struct sockaddr_in *)ainfo->ai_addr; | ||||
memcpy(sn->addr.v4, &(saddr->sin_addr.s_addr), IPV4_SIZE); | // first time calling edge_init_sockets needs -1 in the sockets for it does | |||
sn->family=AF_INET; | throw an error | |||
} | // on trying to close them (open_sockets does so for also being able to RE-o | |||
else | pen the sockets | |||
{ | // if called in-between, see "Supernode not responding" in update_supernode_ | |||
/* Should only return IPv4 addresses due to aihints. */ | reg(...) | |||
traceEvent(TRACE_WARNING, "Failed to resolve supernode IPv4 address f | eee->sock = -1; | |||
or %s", supernode_host); | eee->udp_mgmt_sock = -1; | |||
rv = -1; | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
} | eee->udp_multicast_sock = -1; | |||
#endif | ||||
if(edge_init_sockets(eee) < 0) { | ||||
traceEvent(TRACE_ERROR, "socket setup failed"); | ||||
goto edge_init_error; | ||||
} | ||||
freeaddrinfo(ainfo); /* free everything allocated by getaddrinfo(). */ | if(resolve_create_thread(&(eee->resolve_parameter), eee->conf.supernodes) == | |||
ainfo = NULL; | 0) { | |||
} else { | traceEvent(TRACE_NORMAL, "successfully created resolver thread"); | |||
traceEvent(TRACE_WARNING, "Failed to resolve supernode host %s", supernode | ||||
_host); | ||||
rv = -2; | ||||
} | } | |||
} else { | eee->network_traffic_filter = create_network_traffic_filter(); | |||
traceEvent(TRACE_WARNING, "Wrong supernode parameter (-l <host:port>)"); | network_traffic_filter_add_rule(eee->network_traffic_filter, eee->conf.netwo | |||
rv = -3; | rk_traffic_filter_rules); | |||
} | ||||
//edge_init_success: | ||||
*rv = 0; | ||||
return(eee); | ||||
edge_init_error: | ||||
if(eee) | ||||
free(eee); | ||||
*rv = rc; | ||||
return(NULL); | ||||
} | ||||
/* ************************************** */ | ||||
static int find_and_remove_peer (struct peer_info **head, const n2n_mac_t mac) { | ||||
struct peer_info *peer; | ||||
HASH_FIND_PEER(*head, mac, peer); | ||||
if(peer) { | ||||
HASH_DEL(*head, peer); | ||||
free(peer); | ||||
return(1); | ||||
} | ||||
return(rv); | return(0); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static const int definitely_from_supernode = 1; | static uint32_t localhost_v4 = 0x7f000001; | |||
static uint8_t localhost_v6[IPV6_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1}; | ||||
/* Exclude localhost as it may be received when an edge node runs | ||||
* in the same supernode host. | ||||
*/ | ||||
static int is_valid_peer_sock (const n2n_sock_t *sock) { | ||||
switch(sock->family) { | ||||
case AF_INET: { | ||||
uint32_t *a = (uint32_t*)sock->addr.v4; | ||||
if(*a != htonl(localhost_v4)) | ||||
return(1); | ||||
} | ||||
break; | ||||
case AF_INET6: | ||||
if(memcmp(sock->addr.v6, localhost_v6, IPV6_SIZE)) | ||||
return(1); | ||||
break; | ||||
} | ||||
return(0); | ||||
} | ||||
/* ***************************************************** */ | ||||
/*** | /*** | |||
* | * | |||
* For a given packet, find the apporopriate internal last valid time stamp for lookup | * For a given packet, find the apporopriate internal last valid time stamp for lookup | |||
* and verify it (and also update, if applicable). | * and verify it (and also update, if applicable). | |||
*/ | */ | |||
static int find_peer_time_stamp_and_verify (n2n_edge_t * eee, | static int find_peer_time_stamp_and_verify (n2n_edge_t * eee, | |||
int from_supernode, n2n_mac_t mac, | peer_info_t *sn, const n2n_mac_t mac | |||
uint64_t stamp) { | , | |||
uint64_t stamp, int allow_jitter) { | ||||
uint64_t * previous_stamp = NULL; | uint64_t *previous_stamp = NULL; | |||
if(from_supernode) { | if(sn) { | |||
// from supernode | // from supernode | |||
previous_stamp = &(eee->sn_last_valid_time_stamp); | previous_stamp = &(sn->last_valid_time_stamp); | |||
} else { | } else { | |||
// from (peer) edge | // from (peer) edge | |||
struct peer_info *peer; | struct peer_info *peer; | |||
HASH_FIND_PEER(eee->pending_peers, mac, peer); | HASH_FIND_PEER(eee->pending_peers, mac, peer); | |||
if(!peer) { | if(!peer) { | |||
HASH_FIND_PEER(eee->known_peers, mac, peer); | HASH_FIND_PEER(eee->known_peers, mac, peer); | |||
} | } | |||
if(peer) { | ||||
// time_stamp_verify_and_update allows the pointer a previous stamp to be | if(peer) { | |||
NULL | // time_stamp_verify_and_update allows the pointer a previous stamp | |||
// if it is a (so far) unknown peer | to be NULL | |||
previous_stamp = &(peer->last_valid_time_stamp); | // if it is a (so far) unknown peer | |||
previous_stamp = &(peer->last_valid_time_stamp); | ||||
} | ||||
} | } | |||
} | ||||
// failure --> 0; success --> 1 | // failure --> 0; success --> 1 | |||
return ( time_stamp_verify_and_update (stamp, previous_stamp) ); | return time_stamp_verify_and_update(stamp, previous_stamp, allow_jitter); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/*** | /*** | |||
* | * | |||
* Register over multicast in case there is a peer on the same network listening | * Register over multicast in case there is a peer on the same network listening | |||
*/ | */ | |||
static void register_with_local_peers(n2n_edge_t * eee) { | static void register_with_local_peers (n2n_edge_t * eee) { | |||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
if(eee->multicast_joined && eee->conf.allow_p2p) { | if((eee->multicast_joined && eee->conf.allow_p2p) | |||
/* send registration to the local multicast group */ | && (eee->conf.preferred_sock.family == (uint8_t)AF_INVALID)) { | |||
traceEvent(TRACE_DEBUG, "Registering with multicast group %s:%u", | /* send registration to the local multicast group */ | |||
N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); | traceEvent(TRACE_DEBUG, "registering with multicast group %s:%u", | |||
send_register(eee, &(eee->multicast_peer), NULL); | N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); | |||
} | send_register(eee, &(eee->multicast_peer), NULL, N2N_MCAST_REG_COOKIE); | |||
} | ||||
#else | #else | |||
traceEvent(TRACE_DEBUG, "Multicast peers discovery is disabled, skipping"); | traceEvent(TRACE_DEBUG, "multicast peers discovery is disabled, skipping"); | |||
#endif | #endif | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static struct peer_info* find_peer_by_sock (const n2n_sock_t *sock, struct peer_ | ||||
info *peer_list) { | ||||
struct peer_info *scan, *tmp, *ret = NULL; | ||||
HASH_ITER(hh, peer_list, scan, tmp) { | ||||
if(memcmp(&(scan->sock), sock, sizeof(n2n_sock_t)) == 0) { | ||||
ret = scan; | ||||
break; | ||||
} | ||||
} | ||||
return ret; | ||||
} | ||||
/* ************************************** */ | ||||
/** Start the registration process. | /** Start the registration process. | |||
* | * | |||
* If the peer is already in pending_peers, ignore the request. | * If the peer is already in pending_peers, ignore the request. | |||
* If not in pending_peers, add it and send a REGISTER. | * If not in pending_peers, add it and send a REGISTER. | |||
* | * | |||
* If hdr is for a direct peer-to-peer packet, try to register back to sender | * If hdr is for a direct peer-to-peer packet, try to register back to sender | |||
* even if the MAC is in pending_peers. This is because an incident direct | * even if the MAC is in pending_peers. This is because an incident direct | |||
* packet indicates that peer-to-peer exchange should work so more aggressive | * packet indicates that peer-to-peer exchange should work so more aggressive | |||
* registration can be permitted (once per incoming packet) as this should only | * registration can be permitted (once per incoming packet) as this should on | |||
* last for a small number of packets.. | ly | |||
* last for a small number of packets.. | ||||
* | * | |||
* Called from the main loop when Rx a packet for our device mac. | * Called from the main loop when Rx a packet for our device mac. | |||
*/ | */ | |||
static void register_with_new_peer(n2n_edge_t * eee, | static void register_with_new_peer (n2n_edge_t *eee, | |||
uint8_t from_supernode, | uint8_t from_supernode, | |||
const n2n_mac_t mac, | uint8_t via_multicast, | |||
const n2n_sock_t * peer) { | const n2n_mac_t mac, | |||
/* REVISIT: purge of pending_peers not yet done. */ | const n2n_ip_subnet_t *dev_addr, | |||
struct peer_info * scan; | const n2n_desc_t *dev_desc, | |||
macstr_t mac_buf; | const n2n_sock_t *peer) { | |||
n2n_sock_str_t sockbuf; | ||||
/* REVISIT: purge of pending_peers not yet done. */ | ||||
HASH_FIND_PEER(eee->pending_peers, mac, scan); | struct peer_info *scan; | |||
macstr_t mac_buf; | ||||
/* NOTE: pending_peers are purged periodically with purge_expired_registration | n2n_sock_str_t sockbuf; | |||
s */ | ||||
if(scan == NULL) { | HASH_FIND_PEER(eee->pending_peers, mac, scan); | |||
scan = calloc(1, sizeof(struct peer_info)); | ||||
/* NOTE: pending_peers are purged periodically with purge_expired_nodes */ | ||||
memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); | if(scan == NULL) { | |||
scan->sock = *peer; | scan = calloc(1, sizeof(struct peer_info)); | |||
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to t | ||||
he peer supernode registration timeout */ | memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); | |||
scan->last_seen = time(NULL); /* Don't change this it marks the pending peer | scan->sock = *peer; | |||
for removal. */ | scan->timeout = eee->conf.register_interval; /* TODO: should correspond | |||
scan->last_valid_time_stamp = initial_time_stamp (); | to the peer supernode registration timeout */ | |||
scan->last_valid_time_stamp = initial_time_stamp(); | ||||
HASH_ADD_PEER(eee->pending_peers, scan); | if(via_multicast) | |||
scan->local = 1; | ||||
traceEvent(TRACE_DEBUG, "=== new pending %s -> %s", | ||||
macaddr_str(mac_buf, scan->mac_addr), | HASH_ADD_PEER(eee->pending_peers, scan); | |||
sock_to_cstr(sockbuf, &(scan->sock))); | ||||
traceEvent(TRACE_DEBUG, "new pending peer %s [%s]", | ||||
traceEvent(TRACE_DEBUG, "Pending peers list size=%u", | macaddr_str(mac_buf, scan->mac_addr), | |||
HASH_COUNT(eee->pending_peers)); | sock_to_cstr(sockbuf, &(scan->sock))); | |||
/* trace Sending REGISTER */ | traceEvent(TRACE_DEBUG, "pending peers list size=%u", | |||
if(from_supernode) { | HASH_COUNT(eee->pending_peers)); | |||
/* UDP NAT hole punching through supernode. Send to peer first(punch local | /* trace Sending REGISTER */ | |||
UDP hole) | if(from_supernode) { | |||
* and then ask supernode to forward. Supernode then ask peer to ack. Some | /* UDP NAT hole punching through supernode. Send to peer first(punch | |||
nat device | local UDP hole) | |||
* drop and block ports with incoming UDP packet if out-come traffic does | * and then ask supernode to forward. Supernode then ask peer to ack | |||
not exist. | . Some nat device | |||
* So we can alternatively set TTL so that the packet sent to peer never r | * drop and block ports with incoming UDP packet if out-come traffic | |||
eally reaches | does not exist. | |||
* The register_ttl is basically nat level + 1. Set it to 1 means host lik | * So we can alternatively set TTL so that the packet sent to peer n | |||
e DMZ. | ever really reaches | |||
*/ | * The register_ttl is basically nat level + 1. Set it to 1 means ho | |||
if(eee->conf.register_ttl == 1) { | st like DMZ. | |||
/* We are DMZ host or port is directly accessible. Just let peer to send | */ | |||
back the ack */ | if(eee->conf.register_ttl == 1) { | |||
/* We are DMZ host or port is directly accessible. Just let peer | ||||
to send back the ack */ | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
} else if(eee->conf.register_ttl > 1) { | } else if(eee->conf.register_ttl > 1) { | |||
/* Setting register_ttl usually implies that the edge knows the internal | /* Setting register_ttl usually implies that the edge knows the | |||
net topology | internal net topology | |||
* clearly, we can apply aggressive port prediction to support incoming | * clearly, we can apply aggressive port prediction to support i | |||
Symmetric NAT | ncoming Symmetric NAT | |||
*/ | */ | |||
int curTTL = 0; | int curTTL = 0; | |||
socklen_t lenTTL = sizeof(int); | socklen_t lenTTL = sizeof(int); | |||
n2n_sock_t sock = scan->sock; | n2n_sock_t sock = scan->sock; | |||
int alter = 16; /* TODO: set by command line or more reliable prediction | int alter = 16; /* TODO: set by command line or more reliable pr | |||
method */ | ediction method */ | |||
getsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *)(char *)&curTTL, & | getsockopt(eee->sock, IPPROTO_IP, IP_TTL, (void *) (char *) &cur | |||
lenTTL); | TTL, &lenTTL); | |||
setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, | setsockopt(eee->sock, IPPROTO_IP, IP_TTL, | |||
(void *)(char *)&eee->conf.register_ttl, | (void *) (char *) &eee->conf.register_ttl, | |||
sizeof(eee->conf.register_ttl)); | sizeof(eee->conf.register_ttl)); | |||
for (; alter > 0; alter--, sock.port++) | for(; alter > 0; alter--, sock.port++) { | |||
{ | send_register(eee, &sock, mac, N2N_PORT_REG_COOKIE); | |||
send_register(eee, &sock, mac); | } | |||
} | setsockopt(eee->sock, IPPROTO_IP, IP_TTL, (void *) (char *) &cur | |||
setsockopt(eee->udp_sock, IPPROTO_IP, IP_TTL, (void *)(char *)&curTTL, s | TTL, sizeof(curTTL)); | |||
izeof(curTTL)); | #endif | |||
#endif | } else { /* eee->conf.register_ttl <= 0 */ | |||
} else { /* eee->conf.register_ttl <= 0 */ | /* Normal STUN */ | |||
/* Normal STUN */ | send_register(eee, &(scan->sock), mac, N2N_REGULAR_REG_COOKIE); | |||
send_register(eee, &(scan->sock), mac); | } | |||
} | send_register(eee, &(eee->curr_sn->sock), mac, N2N_FORWARDED_REG_COO | |||
send_register(eee, &(eee->supernode), mac); | KIE); | |||
} else { | } else { | |||
/* P2P register, send directly */ | /* P2P register, send directly */ | |||
send_register(eee, &(scan->sock), mac); | send_register(eee, &(scan->sock), mac, N2N_REGULAR_REG_COOKIE); | |||
} | ||||
register_with_local_peers(eee); | ||||
} else{ | ||||
scan->sock = *peer; | ||||
} | ||||
scan->last_seen = time(NULL); | ||||
if(dev_addr != NULL) { | ||||
memcpy(&(scan->dev_addr), dev_addr, sizeof(n2n_ip_subnet_t)); | ||||
} | } | |||
if(dev_desc) memcpy(scan->dev_desc, dev_desc, N2N_DESC_SIZE); | ||||
register_with_local_peers(eee); | ||||
} else | ||||
scan->sock = *peer; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Update the last_seen time for this peer, or get registered. */ | /** Update the last_seen time for this peer, or get registered. */ | |||
static void check_peer_registration_needed(n2n_edge_t * eee, | static void check_peer_registration_needed (n2n_edge_t *eee, | |||
uint8_t from_supernode, | uint8_t from_supernode, | |||
const n2n_mac_t mac, | uint8_t via_multicast, | |||
const n2n_sock_t * peer) { | const n2n_mac_t mac, | |||
struct peer_info *scan; | const n2n_cookie_t cookie, | |||
const n2n_ip_subnet_t *dev_addr, | ||||
HASH_FIND_PEER(eee->known_peers, mac, scan); | const n2n_desc_t *dev_desc, | |||
const n2n_sock_t *peer) { | ||||
if(scan == NULL) { | ||||
/* Not in known_peers - start the REGISTER process. */ | struct peer_info *scan; | |||
register_with_new_peer(eee, from_supernode, mac, peer); | ||||
} else { | HASH_FIND_PEER(eee->known_peers, mac, scan); | |||
/* Already in known_peers. */ | ||||
time_t now = time(NULL); | /* If we were not able to find it by MAC, we try to find it by socket. */ | |||
if(scan == NULL ) { | ||||
scan = find_peer_by_sock(peer, eee->known_peers); | ||||
// MAC change | ||||
if(scan) { | ||||
HASH_DEL(eee->known_peers, scan); | ||||
memcpy(scan->mac_addr, mac, sizeof(n2n_mac_t)); | ||||
HASH_ADD_PEER(eee->known_peers, scan); | ||||
// reset last_local_reg to allow re-registration | ||||
scan->last_cookie = N2N_NO_REG_COOKIE; | ||||
} | ||||
} | ||||
if(!from_supernode) | if(scan == NULL) { | |||
scan->last_p2p = now; | /* Not in known_peers - start the REGISTER process. */ | |||
register_with_new_peer(eee, from_supernode, via_multicast, mac, dev_addr | ||||
, dev_desc, peer); | ||||
} else { | ||||
/* Already in known_peers. */ | ||||
time_t now = time(NULL); | ||||
if(!from_supernode) | ||||
scan->last_p2p = now; | ||||
if(via_multicast) | ||||
scan->local = 1; | ||||
if((now - scan->last_seen) > 0 /* >= 1 sec */) { | if(((now - scan->last_seen) > 0 /* >= 1 sec */) | |||
/* Don't register too often */ | ||(cookie > scan->last_cookie)) { | |||
check_known_peer_sock_change(eee, from_supernode, mac, peer, now); | /* Don't register too often */ | |||
check_known_peer_sock_change(eee, from_supernode, via_multicast, mac | ||||
, dev_addr, dev_desc, peer, now); | ||||
} | ||||
} | } | |||
} | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/* Confirm that a pending peer is reachable directly via P2P. | /* Confirm that a pending peer is reachable directly via P2P. | |||
* | * | |||
* peer must be a pointer to an element of the pending_peers list. | * peer must be a pointer to an element of the pending_peers list. | |||
*/ | */ | |||
static void peer_set_p2p_confirmed(n2n_edge_t * eee, | static void peer_set_p2p_confirmed (n2n_edge_t * eee, | |||
const n2n_mac_t mac, | const n2n_mac_t mac, | |||
const n2n_sock_t * peer, | const n2n_cookie_t cookie, | |||
time_t now) { | const n2n_sock_t * peer, | |||
struct peer_info *scan; | time_t now) { | |||
macstr_t mac_buf; | ||||
n2n_sock_str_t sockbuf; | struct peer_info *scan, *scan_tmp; | |||
macstr_t mac_buf; | ||||
n2n_sock_str_t sockbuf; | ||||
HASH_FIND_PEER(eee->pending_peers, mac, scan); | ||||
if(scan == NULL) { | ||||
scan = find_peer_by_sock(peer, eee->pending_peers); | ||||
// in case of MAC change, reset last_local_reg to allow re-registration | ||||
if(scan) | ||||
scan->last_cookie = N2N_NO_REG_COOKIE; | ||||
} | ||||
if(scan) { | ||||
HASH_DEL(eee->pending_peers, scan); | ||||
scan_tmp = find_peer_by_sock(peer, eee->known_peers); | ||||
if(scan_tmp != NULL) { | ||||
HASH_DEL(eee->known_peers, scan_tmp); | ||||
free(scan); | ||||
scan = scan_tmp; | ||||
memcpy(scan->mac_addr, mac, sizeof(n2n_mac_t)); | ||||
// in case of MAC change, reset cookie to allow immediate re-registr | ||||
ation | ||||
scan->last_cookie = N2N_NO_REG_COOKIE; | ||||
} else { | ||||
// update sock but ... | ||||
// ... ignore ACKs's (and their socks) from lower ranked inbound way | ||||
s for a while | ||||
if(((now - scan->last_seen) > REGISTRATION_TIMEOUT / 4) | ||||
||(cookie > scan->last_cookie)) { | ||||
scan->sock = *peer; | ||||
scan->last_cookie = cookie; | ||||
} | ||||
} | ||||
HASH_FIND_PEER(eee->pending_peers, mac, scan); | HASH_ADD_PEER(eee->known_peers, scan); | |||
scan->last_p2p = now; | ||||
if(scan) { | traceEvent(TRACE_DEBUG, "p2p connection established: %s [%s]", | |||
HASH_DEL(eee->pending_peers, scan); | macaddr_str(mac_buf, mac), | |||
sock_to_cstr(sockbuf, peer)); | ||||
traceEvent(TRACE_DEBUG, "new peer %s [%s]", | ||||
macaddr_str(mac_buf, scan->mac_addr), | ||||
sock_to_cstr(sockbuf, &(scan->sock))); | ||||
traceEvent(TRACE_DEBUG, "pending peers list size=%u", | ||||
HASH_COUNT(eee->pending_peers)); | ||||
traceEvent(TRACE_DEBUG, "known peers list size=%u", | ||||
HASH_COUNT(eee->known_peers)); | ||||
scan->last_seen = now; | ||||
} else | ||||
traceEvent(TRACE_DEBUG, "failed to find sender in pending_peers"); | ||||
} | ||||
/* Add scan to known_peers. */ | /* ************************************** */ | |||
HASH_ADD_PEER(eee->known_peers, scan); | ||||
scan->sock = *peer; | // provides the current / a new local auth token | |||
scan->last_p2p = now; | static int get_local_auth (n2n_edge_t *eee, n2n_auth_t *auth) { | |||
switch(eee->conf.auth.scheme) { | ||||
case n2n_auth_simple_id: | ||||
memcpy(auth, &(eee->conf.auth), sizeof(n2n_auth_t)); | ||||
break; | ||||
case n2n_auth_user_password: | ||||
// start from the locally stored complete auth token (including type | ||||
and size fields) | ||||
memcpy(auth, &(eee->conf.auth), sizeof(n2n_auth_t)); | ||||
// the token data consists of | ||||
// 32 bytes public key | ||||
// 16 bytes random challenge | ||||
// generate a new random auth challenge every time | ||||
memrnd(auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE | ||||
_SIZE); | ||||
// store it in local auth token (for comparison later) | ||||
memcpy(eee->conf.auth.token + N2N_PRIVATE_PUBLIC_KEY_SIZE, auth->tok | ||||
en + N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE); | ||||
// encrypt the challenge for transmission | ||||
speck_128_encrypt(auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_ | ||||
context_t*)eee->conf.shared_secret_ctx); | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
traceEvent(TRACE_DEBUG, "P2P connection established: %s [%s]", | return 0; | |||
macaddr_str(mac_buf, mac), | } | |||
sock_to_cstr(sockbuf, peer)); | ||||
traceEvent(TRACE_DEBUG, "=== new peer %s -> %s", | // handles a returning (remote) auth token, takes action as required by auth sch | |||
macaddr_str(mac_buf, scan->mac_addr), | eme | |||
sock_to_cstr(sockbuf, &(scan->sock))); | static int handle_remote_auth (n2n_edge_t *eee, struct peer_info *peer, const n2 | |||
n_auth_t *remote_auth) { | ||||
traceEvent(TRACE_DEBUG, "Pending peers list size=%u", | uint8_t tmp_token[N2N_AUTH_MAX_TOKEN_SIZE]; | |||
HASH_COUNT(eee->pending_peers)); | ||||
traceEvent(TRACE_DEBUG, "Known peers list size=%u", | switch(eee->conf.auth.scheme) { | |||
HASH_COUNT(eee->known_peers)); | case n2n_auth_simple_id: | |||
// no action required | ||||
break; | ||||
case n2n_auth_user_password: | ||||
memcpy(tmp_token, remote_auth->token, N2N_AUTH_PW_TOKEN_SIZE); | ||||
// the returning token data consists of | ||||
// 16 bytes double-encrypted challenge | ||||
// 16 bytes public key (second half) | ||||
// 16 bytes encrypted (original random challenge XOR shared secre | ||||
t XOR dynamic key) | ||||
// decrypt double-encrypted received challenge (first half of public | ||||
key field) | ||||
speck_128_decrypt(tmp_token, (speck_context_t*)eee->conf.shared_secr | ||||
et_ctx); | ||||
speck_128_decrypt(tmp_token, (speck_context_t*)eee->conf.shared_secr | ||||
et_ctx); | ||||
// compare to original challenge | ||||
if(0 != memcmp(tmp_token, eee->conf.auth.token + N2N_PRIVATE_PUBLIC_ | ||||
KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE)) | ||||
return -1; | ||||
// decrypt the received challenge in which the dynamic key is wrappe | ||||
d | ||||
speck_128_decrypt(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, (speck_co | ||||
ntext_t*)eee->conf.shared_secret_ctx); | ||||
// un-XOR the original challenge | ||||
memxor(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, eee->conf.auth.token | ||||
+ N2N_PRIVATE_PUBLIC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE); | ||||
// un-XOR the shared secret | ||||
memxor(tmp_token + N2N_PRIVATE_PUBLIC_KEY_SIZE, *(eee->conf.shared_s | ||||
ecret), N2N_AUTH_CHALLENGE_SIZE); | ||||
// setup for use as dynamic key | ||||
packet_header_change_dynamic_key(tmp_token + N2N_PRIVATE_PUBLIC_KEY_ | ||||
SIZE, | ||||
&(eee->conf.header_encryption_ctx_d | ||||
ynamic), | ||||
&(eee->conf.header_iv_ctx_dynamic)) | ||||
; | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
scan->last_seen = now; | return 0; | |||
} else | ||||
traceEvent(TRACE_DEBUG, "Failed to find sender in pending_peers."); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
int is_empty_ip_address(const n2n_sock_t * sock) { | int is_empty_ip_address (const n2n_sock_t * sock) { | |||
const uint8_t * ptr=NULL; | ||||
size_t len=0; | ||||
size_t i; | ||||
if(AF_INET6 == sock->family) | const uint8_t * ptr = NULL; | |||
{ | size_t len = 0; | |||
ptr = sock->addr.v6; | size_t i; | |||
len = 16; | ||||
} | if(AF_INET6 == sock->family) { | |||
else | ptr = sock->addr.v6; | |||
{ | len = 16; | |||
ptr = sock->addr.v4; | } else { | |||
len = 4; | ptr = sock->addr.v4; | |||
len = 4; | ||||
} | } | |||
for (i=0; i<len; ++i) | for(i = 0; i < len; ++i) { | |||
{ | if(0 != ptr[i]) { | |||
if(0 != ptr[i]) | /* found a non-zero byte in address */ | |||
{ | return 0; | |||
/* found a non-zero byte in address */ | ||||
return 0; | ||||
} | } | |||
} | } | |||
return 1; | return 1; | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static n2n_mac_t broadcast_mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; | ||||
static n2n_mac_t null_mac = {0, 0, 0, 0, 0, 0}; | ||||
/** Check if a known peer socket has changed and possibly register again. | /** Check if a known peer socket has changed and possibly register again. | |||
*/ | */ | |||
static void check_known_peer_sock_change(n2n_edge_t * eee, | static void check_known_peer_sock_change (n2n_edge_t *eee, | |||
uint8_t from_supernode, | uint8_t from_supernode, | |||
const n2n_mac_t mac, | uint8_t via_multicast, | |||
const n2n_sock_t * peer, | const n2n_mac_t mac, | |||
time_t when) { | const n2n_ip_subnet_t *dev_addr, | |||
struct peer_info *scan; | const n2n_desc_t *dev_desc, | |||
n2n_sock_str_t sockbuf1; | const n2n_sock_t *peer, | |||
n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two addresses to | time_t when) { | |||
trace */ | ||||
macstr_t mac_buf; | struct peer_info *scan; | |||
n2n_sock_str_t sockbuf1; | ||||
if(is_empty_ip_address(peer)) | n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two addresses | |||
return; | to trace */ | |||
macstr_t mac_buf; | ||||
if(!memcmp(mac, broadcast_mac, N2N_MAC_SIZE)) | ||||
return; | if(is_empty_ip_address(peer)) | |||
return; | ||||
/* Search the peer in known_peers */ | ||||
HASH_FIND_PEER(eee->known_peers, mac, scan); | if(is_multi_broadcast(mac)) | |||
return; | ||||
if(!scan) | ||||
/* Not in known_peers */ | /* Search the peer in known_peers */ | |||
return; | HASH_FIND_PEER(eee->known_peers, mac, scan); | |||
if(!sock_equal(&(scan->sock), peer)) { | if(!scan) | |||
if(!from_supernode) { | /* Not in known_peers */ | |||
/* This is a P2P packet */ | return; | |||
traceEvent(TRACE_NORMAL, "Peer changed %s: %s -> %s", | ||||
macaddr_str(mac_buf, scan->mac_addr), | if(!sock_equal(&(scan->sock), peer)) { | |||
sock_to_cstr(sockbuf1, &(scan->sock)), | if(!from_supernode) { | |||
sock_to_cstr(sockbuf2, peer)); | /* This is a P2P packet */ | |||
/* The peer has changed public socket. It can no longer be assumed to be r | traceEvent(TRACE_NORMAL, "peer %s changed [%s] -> [%s]", | |||
eachable. */ | macaddr_str(mac_buf, scan->mac_addr), | |||
HASH_DEL(eee->known_peers, scan); | sock_to_cstr(sockbuf1, &(scan->sock)), | |||
free(scan); | sock_to_cstr(sockbuf2, peer)); | |||
/* The peer has changed public socket. It can no longer be assumed t | ||||
o be reachable. */ | ||||
HASH_DEL(eee->known_peers, scan); | ||||
free(scan); | ||||
register_with_new_peer(eee, from_supernode, via_multicast, mac, dev_ | ||||
addr, dev_desc, peer); | ||||
} else { | ||||
/* Don't worry about what the supernode reports, it could be seeing | ||||
a different socket. */ | ||||
} | ||||
} else | ||||
scan->last_seen = when; | ||||
} | ||||
/* ************************************** */ | ||||
/** Send a datagram to a socket file descriptor */ | ||||
static ssize_t sendto_fd (n2n_edge_t *eee, const void *buf, | ||||
size_t len, struct sockaddr_in *dest) { | ||||
ssize_t sent = 0; | ||||
int rc = 1; | ||||
register_with_new_peer(eee, from_supernode, mac, peer); | // if required (tcp), wait until writeable as soket is set to O_NONBLOCK, co | |||
uld require | ||||
// some wait time directly after re-opening | ||||
if(eee->conf.connect_tcp) { | ||||
fd_set socket_mask; | ||||
struct timeval wait_time; | ||||
FD_ZERO(&socket_mask); | ||||
FD_SET(eee->sock, &socket_mask); | ||||
wait_time.tv_sec = 0; | ||||
wait_time.tv_usec = 500000; | ||||
rc = select(eee->sock + 1, NULL, &socket_mask, NULL, &wait_time); | ||||
} | ||||
if(rc > 0) { | ||||
sent = sendto(eee->sock, buf, len, 0 /*flags*/, | ||||
(struct sockaddr *)dest, sizeof(struct sockaddr_in)); | ||||
if((sent <= 0) && (errno)) { | ||||
char * c = strerror(errno); | ||||
// downgrade to TRACE_DEBUG in case of custom AF_INVALID, i.e. super | ||||
node not resolved yet | ||||
if(errno == EAFNOSUPPORT /* 93 */) { | ||||
traceEvent(TRACE_DEBUG, "sendto failed (%d) %s", errno, c); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_DEBUG, "WSAGetLastError(): %u", WSAGetLastError | ||||
()); | ||||
#endif | ||||
} else { | ||||
traceEvent(TRACE_WARNING, "sendto failed (%d) %s", errno, c); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_WARNING, "WSAGetLastError(): %u", WSAGetLastErr | ||||
or()); | ||||
#endif | ||||
} | ||||
if(eee->conf.connect_tcp) { | ||||
supernode_disconnect(eee); | ||||
eee->sn_wait = 1; | ||||
traceEvent(TRACE_DEBUG, "disconnected supernode due to sendto() | ||||
error"); | ||||
return -1; | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "sent=%d to ", (signed int)sent); | ||||
} | ||||
} else { | } else { | |||
/* Don't worry about what the supernode reports, it could be seeing a diff | supernode_disconnect(eee); | |||
erent socket. */ | eee->sn_wait = 1; | |||
traceEvent(TRACE_DEBUG, "disconnected supernode due to select() timeout" | ||||
); | ||||
return -1; | ||||
} | } | |||
} else | return sent; | |||
scan->last_seen = when; | ||||
} | } | |||
/* ************************************** */ | ||||
/** Send a datagram to a socket defined by a n2n_sock_t */ | /** Send a datagram to a socket defined by a n2n_sock_t */ | |||
static ssize_t sendto_sock(int fd, const void * buf, | static ssize_t sendto_sock (n2n_edge_t *eee, const void * buf, | |||
size_t len, const n2n_sock_t * dest) { | size_t len, const n2n_sock_t * dest) { | |||
struct sockaddr_in peer_addr; | ||||
ssize_t sent; | ||||
if(!dest->family) | struct sockaddr_in peer_addr; | |||
// Invalid socket | ssize_t sent; | |||
return 0; | int value = 0; | |||
fill_sockaddr((struct sockaddr *) &peer_addr, | if(!dest->family) | |||
sizeof(peer_addr), | // invalid socket | |||
dest); | return 0; | |||
sent = sendto(fd, buf, len, 0/*flags*/, | if(eee->sock < 0) | |||
(struct sockaddr *)&peer_addr, sizeof(struct sockaddr_in)); | // invalid socket file descriptor, e.g. TCP unconnected has fd of '-1' | |||
if(sent < 0) | return 0; | |||
{ | ||||
char * c = strerror(errno); | // network order socket | |||
traceEvent(TRACE_ERROR, "sendto failed (%d) %s", errno, c); | fill_sockaddr((struct sockaddr *) &peer_addr, sizeof(peer_addr), dest); | |||
// if the connection is tcp, i.e. not the regular sock... | ||||
if(eee->conf.connect_tcp) { | ||||
setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); | ||||
value = 1; | ||||
#ifdef LINUX | ||||
setsockopt(eee->sock, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); | ||||
#endif | ||||
// prepend packet length... | ||||
uint16_t pktsize16 = htobe16(len); | ||||
sent = sendto_fd(eee, (uint8_t*)&pktsize16, sizeof(pktsize16), &peer_add | ||||
r); | ||||
if(sent <= 0) | ||||
return -1; | ||||
// ...before sending the actual data | ||||
} | } | |||
else | sent = sendto_fd(eee, buf, len, &peer_addr); | |||
{ | ||||
traceEvent(TRACE_DEBUG, "sendto sent=%d to ", (signed int)sent); | // if the connection is tcp, i.e. not the regular sock... | |||
if(eee->conf.connect_tcp) { | ||||
value = 1; /* value should still be set to 1 */ | ||||
setsockopt(eee->sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); | ||||
#ifdef LINUX | ||||
value = 0; | ||||
setsockopt(eee->sock, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); | ||||
#endif | ||||
} | } | |||
return sent; | return sent; | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/* Bind eee->udp_multicast_sock to multicast group */ | /* Bind eee->udp_multicast_sock to multicast group */ | |||
static void check_join_multicast_group(n2n_edge_t *eee) { | static void check_join_multicast_group (n2n_edge_t *eee) { | |||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
if(!eee->multicast_joined) { | if((eee->conf.allow_p2p) | |||
struct ip_mreq mreq; | && (eee->conf.preferred_sock.family == (uint8_t)AF_INVALID)) { | |||
mreq.imr_multiaddr.s_addr = inet_addr(N2N_MULTICAST_GROUP); | if(!eee->multicast_joined) { | |||
mreq.imr_interface.s_addr = htonl(INADDR_ANY); | struct ip_mreq mreq; | |||
mreq.imr_multiaddr.s_addr = inet_addr(N2N_MULTICAST_GROUP); | ||||
if(setsockopt(eee->udp_multicast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char | #ifdef WIN32 | |||
*)&mreq, sizeof(mreq)) < 0) { | dec_ip_str_t ip_addr; | |||
traceEvent(TRACE_WARNING, "Failed to bind to local multicast group %s:%u [ | get_best_interface_ip(eee, ip_addr); | |||
errno %u]", | mreq.imr_interface.s_addr = inet_addr(ip_addr); | |||
N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT, errno); | #else | |||
mreq.imr_interface.s_addr = htonl(INADDR_ANY); | ||||
#endif | ||||
if(setsockopt(eee->udp_multicast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP | ||||
, (char *)&mreq, sizeof(mreq)) < 0) { | ||||
traceEvent(TRACE_WARNING, "failed to bind to local multicast gro | ||||
up %s:%u [errno %u]", | ||||
N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT, errno); | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); | traceEvent(TRACE_WARNING, "WSAGetLastError(): %u", WSAGetLastErr or()); | |||
#endif | #endif | |||
} else { | } else { | |||
traceEvent(TRACE_NORMAL, "Successfully joined multicast group %s:%u", | traceEvent(TRACE_NORMAL, "successfully joined multicast group %s | |||
N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); | :%u", | |||
eee->multicast_joined = 1; | N2N_MULTICAST_GROUP, N2N_MULTICAST_PORT); | |||
eee->multicast_joined = 1; | ||||
} | ||||
} | ||||
} | } | |||
} | ||||
#endif | #endif | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Send a QUERY_PEER packet to the current supernode. */ | ||||
void send_query_peer (n2n_edge_t * eee, | ||||
const n2n_mac_t dst_mac) { | ||||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | ||||
size_t idx; | ||||
n2n_common_t cmn = {0}; | ||||
n2n_QUERY_PEER_t query = {0}; | ||||
struct peer_info *peer, *tmp; | ||||
int n_o_pings = 0; | ||||
int n_o_top_sn = 0; | ||||
int n_o_rest_sn = 0; | ||||
int n_o_skip_sn = 0; | ||||
cmn.ttl = N2N_DEFAULT_TTL; | ||||
cmn.pc = n2n_query_peer; | ||||
cmn.flags = 0; | ||||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | ||||
idx = 0; | ||||
encode_mac(query.srcMac, &idx, eee->device.mac_addr); | ||||
idx = 0; | ||||
encode_mac(query.targetMac, &idx, dst_mac); | ||||
idx = 0; | ||||
encode_QUERY_PEER(pktbuf, &idx, &cmn, &query); | ||||
if(!is_null_mac(dst_mac)) { | ||||
traceEvent(TRACE_DEBUG, "send QUERY_PEER to supernode"); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(pktbuf, idx, idx, | ||||
eee->conf.header_encryption_ctx_dynamic, eee-> | ||||
conf.header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "send PING to supernodes"); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(pktbuf, idx, idx, | ||||
eee->conf.header_encryption_ctx_dynamic, eee-> | ||||
conf.header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
n_o_pings = eee->conf.number_max_sn_pings; | ||||
eee->conf.number_max_sn_pings = NUMBER_SN_PINGS_REGULAR; | ||||
// ping the 'floor(n/2)' top supernodes and 'ceiling(n/2)' of the remain | ||||
ing | ||||
n_o_top_sn = n_o_pings >> 1; | ||||
n_o_rest_sn = (n_o_pings + 1) >> 1; | ||||
// skip a random number of supernodes between top and remaining | ||||
n_o_skip_sn = HASH_COUNT(eee->conf.supernodes) - n_o_pings; | ||||
n_o_skip_sn = (n_o_skip_sn < 0) ? 0 : n2n_rand_sqr(n_o_skip_sn); | ||||
HASH_ITER(hh, eee->conf.supernodes, peer, tmp) { | ||||
if(n_o_top_sn) { | ||||
n_o_top_sn--; | ||||
// fall through (send to top supernode) | ||||
} else if(n_o_skip_sn) { | ||||
n_o_skip_sn--; | ||||
// skip (do not send) | ||||
continue; | ||||
} else if(n_o_rest_sn) { | ||||
n_o_rest_sn--; | ||||
// fall through (send to remaining supernode) | ||||
} else { | ||||
// done with the remaining (do not send anymore) | ||||
break; | ||||
} | ||||
sendto_sock(eee, pktbuf, idx, &(peer->sock)); | ||||
} | ||||
} | ||||
} | ||||
/* ******************************************************** */ | ||||
/** Send a REGISTER_SUPER packet to the current supernode. */ | /** Send a REGISTER_SUPER packet to the current supernode. */ | |||
static void send_register_super(n2n_edge_t *eee, const n2n_sock_t *supernode, in | void send_register_super (n2n_edge_t *eee) { | |||
t sn_idx) { | ||||
uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; | uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; | |||
size_t idx; | uint8_t hash_buf[16] = {0}; | |||
/* ssize_t sent; */ | size_t idx; | |||
n2n_common_t cmn; | /* ssize_t sent; */ | |||
n2n_REGISTER_SUPER_t reg; | n2n_common_t cmn; | |||
n2n_sock_str_t sockbuf; | n2n_REGISTER_SUPER_t reg; | |||
n2n_sock_str_t sockbuf; | ||||
memset(&cmn, 0, sizeof(cmn)); | ||||
memset(®, 0, sizeof(reg)); | memset(&cmn, 0, sizeof(cmn)); | |||
cmn.ttl=N2N_DEFAULT_TTL; | memset(®, 0, sizeof(reg)); | |||
cmn.pc = n2n_register_super; | ||||
cmn.flags = 0; | cmn.ttl = N2N_DEFAULT_TTL; | |||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | cmn.pc = n2n_register_super; | |||
if(eee->conf.preferred_sock.family == (uint8_t)AF_INVALID) { | ||||
for (idx = 0; (sn_idx==0) && (idx < N2N_COOKIE_SIZE); ++idx) | cmn.flags = 0; | |||
eee->last_cookie[idx] = n2n_rand() % 0xff; | } else { | |||
cmn.flags = N2N_FLAGS_SOCKET; | ||||
memcpy(reg.cookie, eee->last_cookie, N2N_COOKIE_SIZE); | memcpy(&(reg.sock), &(eee->conf.preferred_sock), sizeof(n2n_sock_t)); | |||
reg.auth.scheme=0; /* No auth yet */ | } | |||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | ||||
idx=0; | ||||
encode_mac(reg.edgeMac, &idx, eee->device.mac_addr); | eee->curr_sn->last_cookie = n2n_rand(); | |||
idx=0; | reg.cookie = eee->curr_sn->last_cookie; | |||
encode_REGISTER_SUPER(pktbuf, &idx, &cmn, ®); | reg.dev_addr.net_addr = ntohl(eee->device.ip_addr); | |||
reg.dev_addr.net_bitlen = mask2bitlen(ntohl(eee->device.device_mask)); | ||||
traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s", | memcpy(reg.dev_desc, eee->conf.dev_desc, N2N_DESC_SIZE); | |||
sock_to_cstr(sockbuf, supernode)); | get_local_auth(eee, &(reg.auth)); | |||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, | ||||
eee->conf.header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (pktbuf, | ||||
idx)); | ||||
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, supernode); | idx = 0; | |||
encode_mac(reg.edgeMac, &idx, eee->device.mac_addr); | ||||
idx = 0; | ||||
encode_REGISTER_SUPER(pktbuf, &idx, &cmn, ®); | ||||
traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to [%s]", | ||||
sock_to_cstr(sockbuf, &(eee->curr_sn->sock))); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(pktbuf, idx, idx, | ||||
eee->conf.header_encryption_ctx_static, eee->conf. | ||||
header_iv_ctx_static, | ||||
time_stamp()); | ||||
if(eee->conf.shared_secret) { | ||||
pearson_hash_128(hash_buf, pktbuf, idx); | ||||
speck_128_encrypt(hash_buf, (speck_context_t*)eee->conf.shared_secre | ||||
t_ctx); | ||||
encode_buf(pktbuf, &idx, hash_buf, N2N_REG_SUP_HASH_CHECK_LEN); | ||||
} | ||||
} | ||||
/* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); | ||||
} | } | |||
/* ************************************** */ | static void send_unregister_super (n2n_edge_t *eee) { | |||
/** Send a QUERY_PEER packet to the current supernode. */ | uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; | |||
static void send_query_peer( n2n_edge_t * eee, | size_t idx; | |||
const n2n_mac_t dstMac) { | /* ssize_t sent; */ | |||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | n2n_common_t cmn; | |||
size_t idx; | n2n_UNREGISTER_SUPER_t unreg; | |||
n2n_common_t cmn = {0}; | n2n_sock_str_t sockbuf; | |||
n2n_QUERY_PEER_t query = {{0}}; | ||||
memset(&cmn, 0, sizeof(cmn)); | ||||
cmn.ttl=N2N_DEFAULT_TTL; | memset(&unreg, 0, sizeof(unreg)); | |||
cmn.pc = n2n_query_peer; | ||||
cmn.flags = 0; | cmn.ttl = N2N_DEFAULT_TTL; | |||
memcpy( cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE ); | cmn.pc = n2n_unregister_super; | |||
cmn.flags = 0; | ||||
idx=0; | memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | |||
encode_mac( query.srcMac, &idx, eee->device.mac_addr ); | get_local_auth(eee, &(unreg.auth)); | |||
idx=0; | ||||
encode_mac( query.targetMac, &idx, dstMac ); | idx = 0; | |||
encode_mac(unreg.srcMac, &idx, eee->device.mac_addr); | ||||
idx=0; | ||||
encode_QUERY_PEER( pktbuf, &idx, &cmn, &query ); | idx = 0; | |||
encode_UNREGISTER_SUPER(pktbuf, &idx, &cmn, &unreg); | ||||
traceEvent( TRACE_DEBUG, "send QUERY_PEER to supernode" ); | ||||
traceEvent(TRACE_DEBUG, "send UNREGISTER_SUPER to [%s]", | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED){ | sock_to_cstr(sockbuf, &(eee->curr_sn->sock))); | |||
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, | ||||
eee->conf.header_iv_ctx, | if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | |||
time_stamp (), pearson_hash_16 (pktbuf, | packet_header_encrypt(pktbuf, idx, idx, | |||
idx)); | eee->conf.header_encryption_ctx_dynamic, eee->conf | |||
} | .header_iv_ctx_dynamic, | |||
sendto_sock( eee->udp_sock, pktbuf, idx, &(eee->supernode) ); | time_stamp()); | |||
/* sent = */ sendto_sock(eee, pktbuf, idx, &(eee->curr_sn->sock)); | ||||
} | ||||
static int sort_supernodes (n2n_edge_t *eee, time_t now) { | ||||
struct peer_info *scan, *tmp; | ||||
if(now - eee->last_sweep > SWEEP_TIME) { | ||||
// this routine gets periodically called | ||||
if(!eee->sn_wait) { | ||||
// sort supernodes in ascending order of their selection_criterion f | ||||
ields | ||||
sn_selection_sort(&(eee->conf.supernodes)); | ||||
} | ||||
if(eee->curr_sn != eee->conf.supernodes) { | ||||
// we have not been connected to the best/top one | ||||
send_unregister_super(eee); | ||||
eee->curr_sn = eee->conf.supernodes; | ||||
reset_sup_attempts(eee); | ||||
supernode_connect(eee); | ||||
traceEvent(TRACE_INFO, "registering with supernode [%s][number of su | ||||
pernodes %d][attempts left %u]", | ||||
supernode_ip(eee), HASH_COUNT(eee->conf.supernodes), (uns | ||||
igned int)eee->sup_attempts); | ||||
send_register_super(eee); | ||||
eee->last_register_req = now; | ||||
eee->sn_wait = 1; | ||||
} | ||||
HASH_ITER(hh, eee->conf.supernodes, scan, tmp) { | ||||
if(scan == eee->curr_sn) | ||||
sn_selection_criterion_good(&(scan->selection_criterion)); | ||||
else | ||||
sn_selection_criterion_default(&(scan->selection_criterion)); | ||||
} | ||||
sn_selection_criterion_common_data_default(eee); | ||||
// send PING to all the supernodes | ||||
if(!eee->conf.connect_tcp) | ||||
send_query_peer(eee, null_mac); | ||||
eee->last_sweep = now; | ||||
// no answer yet (so far, unused in regular edge code; mainly used durin | ||||
g bootstrap loading) | ||||
eee->sn_pong = 0; | ||||
} | ||||
return 0; /* OK */ | ||||
} | } | |||
/** Send a REGISTER packet to another edge. */ | /** Send a REGISTER packet to another edge. */ | |||
static void send_register(n2n_edge_t * eee, | static void send_register (n2n_edge_t * eee, | |||
const n2n_sock_t * remote_peer, | const n2n_sock_t * remote_peer, | |||
const n2n_mac_t peer_mac) { | const n2n_mac_t peer_mac, | |||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | const n2n_cookie_t cookie) { | |||
size_t idx; | ||||
/* ssize_t sent; */ | uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | |||
n2n_common_t cmn; | size_t idx; | |||
n2n_REGISTER_t reg; | /* ssize_t sent; */ | |||
n2n_sock_str_t sockbuf; | n2n_common_t cmn; | |||
n2n_REGISTER_t reg; | ||||
if(!eee->conf.allow_p2p) { | n2n_sock_str_t sockbuf; | |||
traceEvent(TRACE_DEBUG, "Skipping register as P2P is disabled"); | ||||
return; | if(!eee->conf.allow_p2p) { | |||
} | traceEvent(TRACE_DEBUG, "skipping register as P2P is disabled"); | |||
return; | ||||
memset(&cmn, 0, sizeof(cmn)); | } | |||
memset(®, 0, sizeof(reg)); | ||||
cmn.ttl=N2N_DEFAULT_TTL; | memset(&cmn, 0, sizeof(cmn)); | |||
cmn.pc = n2n_register; | memset(®, 0, sizeof(reg)); | |||
cmn.flags = 0; | cmn.ttl = N2N_DEFAULT_TTL; | |||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | cmn.pc = n2n_register; | |||
cmn.flags = 0; | ||||
idx=0; | memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | |||
encode_uint32(reg.cookie, &idx, 123456789); | ||||
idx=0; | reg.cookie = cookie; | |||
encode_mac(reg.srcMac, &idx, eee->device.mac_addr); | idx = 0; | |||
encode_mac(reg.srcMac, &idx, eee->device.mac_addr); | ||||
if(peer_mac) { | ||||
/* Can be NULL for multicast registrations */ | if(peer_mac) { | |||
idx=0; | // can be NULL for multicast registrations | |||
encode_mac(reg.dstMac, &idx, peer_mac); | idx = 0; | |||
} | encode_mac(reg.dstMac, &idx, peer_mac); | |||
} | ||||
idx=0; | reg.dev_addr.net_addr = ntohl(eee->device.ip_addr); | |||
encode_REGISTER(pktbuf, &idx, &cmn, ®); | reg.dev_addr.net_bitlen = mask2bitlen(ntohl(eee->device.device_mask)); | |||
memcpy(reg.dev_desc, eee->conf.dev_desc, N2N_DESC_SIZE); | ||||
traceEvent(TRACE_INFO, "Send REGISTER to %s", | ||||
sock_to_cstr(sockbuf, remote_peer)); | idx = 0; | |||
encode_REGISTER(pktbuf, &idx, &cmn, ®); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, | traceEvent(TRACE_INFO, "send REGISTER to [%s]", | |||
eee->conf.header_iv_ctx, | sock_to_cstr(sockbuf, remote_peer)); | |||
time_stamp (), pearson_hash_16 (pktbuf, | ||||
idx)); | if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | |||
packet_header_encrypt(pktbuf, idx, idx, | ||||
eee->conf.header_encryption_ctx_dynamic, eee->conf | ||||
.header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); | /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Send a REGISTER_ACK packet to a peer edge. */ | /** Send a REGISTER_ACK packet to a peer edge. */ | |||
static void send_register_ack(n2n_edge_t * eee, | static void send_register_ack (n2n_edge_t * eee, | |||
const n2n_sock_t * remote_peer, | const n2n_sock_t * remote_peer, | |||
const n2n_REGISTER_t * reg) { | const n2n_REGISTER_t * reg) { | |||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | ||||
size_t idx; | ||||
/* ssize_t sent; */ | ||||
n2n_common_t cmn; | ||||
n2n_REGISTER_ACK_t ack; | ||||
n2n_sock_str_t sockbuf; | ||||
if(!eee->conf.allow_p2p) { | ||||
traceEvent(TRACE_DEBUG, "Skipping register ACK as P2P is disabled"); | ||||
return; | ||||
} | ||||
memset(&cmn, 0, sizeof(cmn)); | ||||
memset(&ack, 0, sizeof(reg)); | ||||
cmn.ttl=N2N_DEFAULT_TTL; | ||||
cmn.pc = n2n_register_ack; | ||||
cmn.flags = 0; | ||||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | ||||
memset(&ack, 0, sizeof(ack)); | ||||
memcpy(ack.cookie, reg->cookie, N2N_COOKIE_SIZE); | ||||
memcpy(ack.srcMac, eee->device.mac_addr, N2N_MAC_SIZE); | ||||
memcpy(ack.dstMac, reg->srcMac, N2N_MAC_SIZE); | ||||
idx=0; | ||||
encode_REGISTER_ACK(pktbuf, &idx, &cmn, &ack); | ||||
traceEvent(TRACE_INFO, "send REGISTER_ACK %s", | ||||
sock_to_cstr(sockbuf, remote_peer)); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (pktbuf, idx, eee->conf.header_encryption_ctx, | ||||
eee->conf.header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (pktbuf, | ||||
idx)); | ||||
/* sent = */ sendto_sock(eee->udp_sock, pktbuf, idx, remote_peer); | uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | |||
} | size_t idx; | |||
/* ssize_t sent; */ | ||||
n2n_common_t cmn; | ||||
n2n_REGISTER_ACK_t ack; | ||||
n2n_sock_str_t sockbuf; | ||||
/* ************************************** */ | if(!eee->conf.allow_p2p) { | |||
traceEvent(TRACE_DEBUG, "skipping register ACK as P2P is disabled"); | ||||
return; | ||||
} | ||||
/** @brief Check to see if we should re-register with the supernode. | memset(&cmn, 0, sizeof(cmn)); | |||
* | memset(&ack, 0, sizeof(reg)); | |||
* This is frequently called by the main loop. | cmn.ttl = N2N_DEFAULT_TTL; | |||
*/ | cmn.pc = n2n_register_ack; | |||
static void update_supernode_reg(n2n_edge_t * eee, time_t nowTime) { | cmn.flags = 0; | |||
u_int sn_idx; | memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | |||
if(eee->sn_wait && (nowTime > (eee->last_register_req + (eee->conf.register_in | memset(&ack, 0, sizeof(ack)); | |||
terval/10)))) { | ack.cookie = reg->cookie; | |||
/* fall through */ | memcpy(ack.srcMac, eee->device.mac_addr, N2N_MAC_SIZE); | |||
traceEvent(TRACE_DEBUG, "update_supernode_reg: doing fast retry."); | memcpy(ack.dstMac, reg->srcMac, N2N_MAC_SIZE); | |||
} else if(nowTime < (eee->last_register_req + eee->conf.register_interval)) | ||||
return; /* Too early */ | ||||
check_join_multicast_group(eee); | idx = 0; | |||
encode_REGISTER_ACK(pktbuf, &idx, &cmn, &ack); | ||||
if(0 == eee->sup_attempts) { | traceEvent(TRACE_INFO, "send REGISTER_ACK to [%s]", | |||
/* Give up on that supernode and try the next one. */ | sock_to_cstr(sockbuf, remote_peer)); | |||
++(eee->sn_idx); | ||||
if(eee->sn_idx >= eee->conf.sn_num) { | if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | |||
/* Got to end of list, go back to the start. Also works for list of one en | packet_header_encrypt(pktbuf, idx, idx, | |||
try. */ | eee->conf.header_encryption_ctx_dynamic, eee->conf | |||
eee->sn_idx=0; | .header_iv_ctx_dynamic, | |||
} | time_stamp()); | |||
traceEvent(TRACE_WARNING, "Supernode not responding, now trying %s", superno | /* sent = */ sendto_sock(eee, pktbuf, idx, remote_peer); | |||
de_ip(eee)); | } | |||
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; | /* ************************************** */ | |||
} | ||||
else | ||||
--(eee->sup_attempts); | ||||
for(sn_idx=0; sn_idx<eee->conf.sn_num; sn_idx++) { | static char gratuitous_arp[] = { | |||
if(supernode2addr(&(eee->supernode), eee->conf.sn_ip_array[sn_idx]) == 0) { | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* dest MAC */ | |||
traceEvent(TRACE_INFO, "Registering with supernode [id: %u/%u][%s][attempt | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* src MAC */ | |||
s left %u]", | 0x08, 0x06, /* ARP */ | |||
sn_idx+1, eee->conf.sn_num, | 0x00, 0x01, /* ethernet */ | |||
supernode_ip(eee), (unsigned int)eee->sup_attempts); | 0x08, 0x00, /* IP */ | |||
0x06, /* hw Size */ | ||||
0x04, /* protocol Size */ | ||||
0x00, 0x02, /* ARP reply */ | ||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* src MAC */ | ||||
0x00, 0x00, 0x00, 0x00, /* src IP */ | ||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, /* target MAC */ | ||||
0x00, 0x00, 0x00, 0x00 /* target IP */ | ||||
}; | ||||
send_register_super(eee, &(eee->supernode), sn_idx); | // build a gratuitous ARP packet */ | |||
} | static int build_gratuitous_arp (n2n_edge_t * eee, char *buffer, uint16_t buffer | |||
} | _len) { | |||
register_with_local_peers(eee); | if(buffer_len < sizeof(gratuitous_arp)) return(-1); | |||
eee->sn_wait=1; | memcpy(buffer, gratuitous_arp, sizeof(gratuitous_arp)); | |||
memcpy(&buffer[6], eee->device.mac_addr, 6); | ||||
memcpy(&buffer[22], eee->device.mac_addr, 6); | ||||
memcpy(&buffer[28], &(eee->device.ip_addr), 4); | ||||
memcpy(&buffer[38], &(eee->device.ip_addr), 4); | ||||
/* REVISIT: turn-on gratuitous ARP with config option. */ | return(sizeof(gratuitous_arp)); | |||
/* send_grat_arps(sock_fd, is_udp_sock); */ | } | |||
eee->last_register_req = nowTime; | /** Called from update_supernode_reg to periodically send gratuitous ARP | |||
* broadcasts. */ | ||||
static void send_grat_arps (n2n_edge_t * eee) { | ||||
uint8_t buffer[48]; | ||||
size_t len; | ||||
traceEvent(TRACE_DEBUG, "sending gratuitous ARP..."); | ||||
len = build_gratuitous_arp(eee, (char*)buffer, sizeof(buffer)); | ||||
edge_send_packet2net(eee, buffer, len); | ||||
edge_send_packet2net(eee, buffer, len); /* Two is better than one :-) */ | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** NOT IMPLEMENTED | /** @brief Check to see if we should re-register with the supernode. | |||
* | * | |||
* This would send a DEREGISTER packet to a peer edge or supernode to indicate | * This is frequently called by the main loop. | |||
* the edge is going away. | ||||
*/ | */ | |||
static void send_deregister(n2n_edge_t * eee, | void update_supernode_reg (n2n_edge_t * eee, time_t now) { | |||
n2n_sock_t * remote_peer) { | ||||
/* Marshall and send message */ | struct peer_info *peer, *tmp_peer; | |||
int cnt = 0; | ||||
int off = 0; | ||||
if((eee->sn_wait && (now > (eee->last_register_req + (eee->conf.register_int | ||||
erval / 10)))) | ||||
||(eee->sn_wait == 2)) /* immediately re-register in case of RE_REGISTER_SU | ||||
PER */ { | ||||
/* fall through */ | ||||
traceEvent(TRACE_DEBUG, "update_supernode_reg: doing fast retry."); | ||||
} else if(now < (eee->last_register_req + eee->conf.register_interval)) | ||||
return; /* Too early */ | ||||
// determine time offset to apply on last_register_req for | ||||
// all edges's next re-registration does not happen all at once | ||||
if(eee->sn_wait == 2) { | ||||
// remaining 1/4 is greater than 1/10 fast retry allowance; | ||||
// '%' might be expensive but does not happen all too often | ||||
off = n2n_rand() % ((eee->conf.register_interval * 3) / 4); | ||||
} | ||||
check_join_multicast_group(eee); | ||||
if(0 == eee->sup_attempts) { | ||||
/* Give up on that supernode and try the next one. */ | ||||
sn_selection_criterion_bad(&(eee->curr_sn->selection_criterion)); | ||||
sn_selection_sort(&(eee->conf.supernodes)); | ||||
eee->curr_sn = eee->conf.supernodes; | ||||
traceEvent(TRACE_WARNING, "supernode not responding, now trying [%s]", s | ||||
upernode_ip(eee)); | ||||
supernode_connect(eee); | ||||
reset_sup_attempts(eee); | ||||
// trigger out-of-schedule DNS resolution | ||||
eee->resolution_request = 1; | ||||
// in some multi-NATed scenarios communication gets stuck on losing conn | ||||
ection to supernode | ||||
// closing and re-opening the socket allows for re-establishing communic | ||||
ation | ||||
// this can only be done, if working on some unprivileged port and/or ha | ||||
ving sufficent | ||||
// privileges. as we are not able to check for sufficent privileges here | ||||
, we only do it | ||||
// if port is sufficently high or unset. uncovered: privileged port and | ||||
sufficent privileges | ||||
if((eee->conf.local_port == 0) || (eee->conf.local_port > 1024)) { | ||||
// do not explicitly disconnect every time as the condition describe | ||||
d is rare, so ... | ||||
// ... check that there are no external peers (indicating a working | ||||
socket) ... | ||||
HASH_ITER(hh, eee->known_peers, peer, tmp_peer) | ||||
if(!peer->local) { | ||||
cnt++; | ||||
break; | ||||
} | ||||
if(!cnt) { | ||||
// ... and then count the connection retries | ||||
(eee->close_socket_counter)++; | ||||
if(eee->close_socket_counter >= N2N_CLOSE_SOCKET_COUNTER_MAX) { | ||||
eee->close_socket_counter = 0; | ||||
supernode_disconnect(eee); | ||||
traceEvent(TRACE_DEBUG, "disconnected supernode"); | ||||
} | ||||
} | ||||
supernode_connect(eee); | ||||
traceEvent(TRACE_DEBUG, "reconnected to supernode"); | ||||
} | ||||
} else { | ||||
--(eee->sup_attempts); | ||||
} | ||||
#ifndef HAVE_PTHREAD | ||||
if(supernode2sock(&(eee->curr_sn->sock), eee->curr_sn->ip_addr) == 0) { | ||||
#endif | ||||
traceEvent(TRACE_INFO, "registering with supernode [%s][number of supern | ||||
odes %d][attempts left %u]", | ||||
supernode_ip(eee), HASH_COUNT(eee->conf.supernodes), (unsigne | ||||
d int)eee->sup_attempts); | ||||
send_register_super(eee); | ||||
#ifndef HAVE_PTHREAD | ||||
} | ||||
#endif | ||||
register_with_local_peers(eee); | ||||
// if supernode repeatedly not responding (already waiting), safeguard the | ||||
// current known connections to peers by re-registering | ||||
if(eee->sn_wait == 1) | ||||
HASH_ITER(hh, eee->known_peers, peer, tmp_peer) | ||||
if((now - peer->last_seen) > REGISTER_SUPER_INTERVAL_DFL) | ||||
send_register(eee, &(peer->sock), peer->mac_addr, peer->last_coo | ||||
kie); | ||||
eee->sn_wait = 1; | ||||
eee->last_register_req = now - off; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Return the IP address of the current supernode in the ring. */ | /** Return the IP address of the current supernode in the ring. */ | |||
static const char * supernode_ip(const n2n_edge_t * eee) { | static const char * supernode_ip (const n2n_edge_t * eee) { | |||
return (eee->conf.sn_ip_array)[eee->sn_idx]; | ||||
return (eee->curr_sn->ip_addr); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** A PACKET has arrived containing an encapsulated ethernet datagram - usually | /** A PACKET has arrived containing an encapsulated ethernet datagram - usually | |||
* encrypted. */ | * encrypted. */ | |||
static int handle_PACKET(n2n_edge_t * eee, | static int handle_PACKET (n2n_edge_t * eee, | |||
const n2n_common_t * cmn, | const uint8_t from_supernode, | |||
const n2n_PACKET_t * pkt, | const n2n_PACKET_t * pkt, | |||
const n2n_sock_t * orig_sender, | const n2n_sock_t * orig_sender, | |||
uint8_t * payload, | uint8_t * payload, | |||
size_t psize) { | size_t psize) { | |||
ssize_t data_sent_len; | ||||
uint8_t from_supernode; | ssize_t data_sent_len; | |||
uint8_t * eth_payload=NULL; | uint8_t * eth_payload = NULL; | |||
int retval = -1; | int retval = -1; | |||
time_t now; | time_t now; | |||
ether_hdr_t * eh; | ether_hdr_t * eh; | |||
ipstr_t ip_buf; | ipstr_t ip_buf; | |||
macstr_t mac_buf; | ||||
now = time(NULL); | n2n_sock_str_t sockbuf; | |||
traceEvent(TRACE_DEBUG, "handle_PACKET size %u transform %u", | now = time(NULL); | |||
(unsigned int)psize, (unsigned int)pkt->transform); | ||||
/* hexdump(payload, psize); */ | traceEvent(TRACE_DEBUG, "handle_PACKET size %u transform %u", | |||
(unsigned int)psize, (unsigned int)pkt->transform); | ||||
/* hexdump(payload, psize); */ | ||||
from_supernode= cmn->flags & N2N_FLAGS_FROM_SUPERNODE; | if(from_supernode) { | |||
if(is_multi_broadcast(pkt->dstMac)) | ||||
if(from_supernode) | ++(eee->stats.rx_sup_broadcast); | |||
{ | ||||
if(!memcmp(pkt->dstMac, broadcast_mac, N2N_MAC_SIZE)) | ||||
++(eee->stats.rx_sup_broadcast); | ||||
++(eee->stats.rx_sup); | ++(eee->stats.rx_sup); | |||
eee->last_sup=now; | eee->last_sup = now; | |||
} else { | ||||
++(eee->stats.rx_p2p); | ||||
eee->last_p2p=now; | ||||
} | } | |||
else | ||||
/* Handle transform. */ | ||||
{ | { | |||
++(eee->stats.rx_p2p); | uint8_t decodebuf[N2N_PKT_BUF_SIZE]; | |||
eee->last_p2p=now; | size_t eth_size; | |||
} | n2n_transform_t rx_transop_id; | |||
uint8_t rx_compression_id; | ||||
rx_transop_id = (n2n_transform_t)pkt->transform; | ||||
rx_compression_id = pkt->compression; | ||||
if(rx_transop_id == eee->conf.transop_id) { | ||||
uint8_t is_multicast; | ||||
eth_payload = decodebuf; | ||||
eh = (ether_hdr_t*)eth_payload; | ||||
eth_size = eee->transop.rev(&eee->transop, | ||||
eth_payload, N2N_PKT_BUF_SIZE, | ||||
payload, psize, pkt->srcMac); | ||||
++(eee->transop.rx_cnt); /* stats */ | ||||
/* decompress if necessary */ | ||||
uint8_t * deflation_buffer = 0; | ||||
lzo_uint deflated_len; | ||||
switch(rx_compression_id) { | ||||
case N2N_COMPRESSION_ID_NONE: | ||||
break; // continue afterwards | ||||
case N2N_COMPRESSION_ID_LZO: | ||||
deflation_buffer = malloc(N2N_PKT_BUF_SIZE); | ||||
lzo1x_decompress(eth_payload, eth_size, deflation_buffer, &d | ||||
eflated_len, NULL); | ||||
break; | ||||
#ifdef N2N_HAVE_ZSTD | ||||
case N2N_COMPRESSION_ID_ZSTD: | ||||
deflated_len = N2N_PKT_BUF_SIZE; | ||||
deflation_buffer = malloc(deflated_len); | ||||
deflated_len = ZSTD_decompress(deflation_buffer, deflated_le | ||||
n, eth_payload, eth_size); | ||||
if(ZSTD_isError(deflated_len)) { | ||||
traceEvent(TRACE_WARNING, "payload decompression failed | ||||
with zstd error '%s'.", | ||||
ZSTD_getErrorName(deflated_len)); | ||||
free(deflation_buffer); | ||||
return(-1); // cannot help it | ||||
} | ||||
break; | ||||
#endif | ||||
default: | ||||
traceEvent(TRACE_WARNING, "payload decompression failed: rec | ||||
eived packet indicating unsupported %s compression.", | ||||
compression_str(rx_compression_id)); | ||||
return(-1); // cannot handle it | ||||
} | ||||
/* Update the sender in peer table entry */ | if(rx_compression_id != N2N_COMPRESSION_ID_NONE) { | |||
check_peer_registration_needed(eee, from_supernode, pkt->srcMac, orig_sender); | traceEvent(TRACE_DEBUG, "payload decompression %s: deflated %u b | |||
ytes to %u bytes", | ||||
compression_str(rx_compression_id), eth_size, (int)de | ||||
flated_len); | ||||
memcpy(eth_payload,deflation_buffer, deflated_len ); | ||||
eth_size = deflated_len; | ||||
free(deflation_buffer); | ||||
} | ||||
/* Handle transform. */ | is_multicast = (is_ip6_discovery(eth_payload, eth_size) || is_ethMul | |||
{ | ticast(eth_payload, eth_size)); | |||
uint8_t decodebuf[N2N_PKT_BUF_SIZE]; | ||||
size_t eth_size; | if(eee->conf.drop_multicast && is_multicast) { | |||
n2n_transform_t rx_transop_id; | traceEvent(TRACE_INFO, "dropping RX multicast"); | |||
return(-1); | ||||
rx_transop_id = (n2n_transform_t)pkt->transform; | } else if((!eee->conf.allow_routing) && (!is_multicast)) { | |||
/* optional compression is encoded in uppermost bit of transform field. | /* Check if it is a routed packet */ | |||
* this is an intermediate solution to maintain compatibility until some | ||||
* upcoming major release (3.0?) brings up changes in packet structure anywa | if((ntohs(eh->type) == 0x0800) && (eth_size >= ETH_FRAMESIZE + I | |||
y | P4_MIN_SIZE)) { | |||
* in the course of which a dedicated compression field could be spent. | uint32_t *dst = (uint32_t*)ð_payload[ETH_FRAMESIZE + IP4_ | |||
* REVISIT then. */ | DSTOFFSET]; | |||
uint16_t rx_compression_id; | uint8_t *dst_mac = (uint8_t*)eth_payload; | |||
rx_compression_id = (uint16_t)rx_transop_id >> (8*sizeof((uint16_t)rx_transo | /* Note: all elements of the_ip are in network order */ | |||
p_id)-N2N_COMPRESSION_ID_BITLEN); | if(!memcmp(dst_mac, broadcast_mac, N2N_MAC_SIZE)) | |||
rx_transop_id &= (1 << (8*sizeof((uint16_t)rx_transop_id)-N2N_COMPRESSION_ID | traceEvent(TRACE_DEBUG, "RX broadcast packet destined to | |||
_BITLEN)) -1; | [%s]", | |||
intoa(ntohl(*dst), ip_buf, sizeof(ip_buf))); | ||||
if(rx_transop_id == eee->conf.transop_id) { | else if((*dst != eee->device.ip_addr)) { | |||
uint8_t is_multicast; | /* This is a packet that needs to be routed */ | |||
eth_payload = decodebuf; | traceEvent(TRACE_INFO, "discarding routed packet destine | |||
eh = (ether_hdr_t*)eth_payload; | d to [%s]", | |||
eth_size = eee->transop.rev(&eee->transop, | intoa(ntohl(*dst), ip_buf, sizeof(ip_buf))); | |||
eth_payload, N2N_PKT_BUF_SIZE, | return(-1); | |||
payload, psize, pkt->srcMac); | } else { | |||
++(eee->transop.rx_cnt); /* stats */ | /* This packet is directed to us */ | |||
/* traceEvent(TRACE_INFO, "Sending non-routed packet"); | ||||
/* decompress if necessary */ | */ | |||
uint8_t * deflation_buffer = 0; | } | |||
int32_t deflated_len; | } | |||
switch (rx_compression_id) { | } | |||
case N2N_COMPRESSION_ID_NONE: | ||||
break; // continue afterwards | if(eee->network_traffic_filter->filter_packet_from_peer(eee->network | |||
_traffic_filter, eee, orig_sender, | ||||
case N2N_COMPRESSION_ID_LZO: | eth_payload, | |||
deflation_buffer = malloc (N2N_PKT_BUF_SIZE); | eth_size) == N2N_DROP) { | |||
lzo1x_decompress (eth_payload, eth_size, deflation_buffer, (lzo_uint*)&de | traceEvent(TRACE_DEBUG, "filtered packet of size %u", (unsigned | |||
flated_len, NULL); | int)eth_size); | |||
break; | return(0); | |||
#ifdef N2N_HAVE_ZSTD | } | |||
case N2N_COMPRESSION_ID_ZSTD: | ||||
deflated_len = N2N_PKT_BUF_SIZE; | if(eee->cb.packet_from_peer) { | |||
deflation_buffer = malloc (deflated_len); | uint16_t tmp_eth_size = eth_size; | |||
deflated_len = (int32_t)ZSTD_decompress (deflation_buffer, deflated_len, | if(eee->cb.packet_from_peer(eee, orig_sender, eth_payload, &tmp_ | |||
eth_payload, eth_size); | eth_size) == N2N_DROP) { | |||
if(ZSTD_isError(deflated_len)) { | traceEvent(TRACE_DEBUG, "DROP packet of size %u", (unsigned | |||
traceEvent (TRACE_ERROR, "payload decompression failed with zstd error | int)eth_size); | |||
'%s'.", | return(0); | |||
ZSTD_getErrorName(deflated_len)); | } | |||
free (deflation_buffer); | eth_size = tmp_eth_size; | |||
return (-1); // cannot help it | } | |||
} | ||||
break; | /* Write ethernet packet to tap device. */ | |||
#endif | traceEvent(TRACE_DEBUG, "sending data of size %u to TAP", (unsigned | |||
default: | int)eth_size); | |||
traceEvent (TRACE_ERROR, "payload decompression failed: received packet i | data_sent_len = tuntap_write(&(eee->device), eth_payload, eth_size); | |||
ndicating unsupported %s compression.", | ||||
compression_str(rx_compression_id)); | if(data_sent_len == eth_size) { | |||
return (-1); // cannot handle it | retval = 0; | |||
} | } | |||
} else { | ||||
if(rx_compression_id) { | traceEvent(TRACE_WARNING, "invalid transop ID: expected %s (%u), | |||
traceEvent (TRACE_DEBUG, "payload decompression [%s]: deflated %u bytes t | got %s (%u) from %s [%s]", | |||
o %u bytes", | transop_str(eee->conf.transop_id), eee->conf.transop_ | |||
compression_str(rx_compression_id), eth_size, (int)deflated_l | id, | |||
en); | transop_str(rx_transop_id), rx_transop_id, | |||
memcpy(eth_payload ,deflation_buffer, deflated_len ); | macaddr_str(mac_buf, pkt->srcMac), | |||
eth_size = deflated_len; | sock_to_cstr(sockbuf, orig_sender)); | |||
free (deflation_buffer); | } | |||
} | ||||
is_multicast = (is_ip6_discovery(eth_payload, eth_size) || is_ethMulticast | ||||
(eth_payload, eth_size)); | ||||
if(eee->conf.drop_multicast && is_multicast) { | ||||
traceEvent(TRACE_INFO, "Dropping RX multicast"); | ||||
return(-1); | ||||
} else if((!eee->conf.allow_routing) && (!is_multicast)) { | ||||
/* Check if it is a routed packet */ | ||||
if((ntohs(eh->type) == 0x0800) && (eth_size >= ETH_FRAMESIZE + IP4_MIN_SI | ||||
ZE)) { | ||||
uint32_t *dst = (uint32_t*)ð_payload[ETH_FRAMESIZE + IP4_DSTOFFSET]; | ||||
uint8_t *dst_mac = (uint8_t*)eth_payload; | ||||
/* Note: all elements of the_ip are in network order */ | ||||
if(!memcmp(dst_mac, broadcast_mac, N2N_MAC_SIZE)) | ||||
traceEvent(TRACE_DEBUG, "Broadcast packet [%s]", | ||||
intoa(ntohl(*dst), ip_buf, sizeof(ip_buf))); | ||||
else if((*dst != eee->device.ip_addr)) { | ||||
/* This is a packet that needs to be routed */ | ||||
traceEvent(TRACE_INFO, "Discarding routed packet [%s]", | ||||
intoa(ntohl(*dst), ip_buf, sizeof(ip_buf))); | ||||
return(-1); | ||||
} else { | ||||
/* This packet is directed to us */ | ||||
/* traceEvent(TRACE_INFO, "Sending non-routed packet"); */ | ||||
} | ||||
} | ||||
} | ||||
if(eee->cb.packet_from_peer) { | ||||
uint16_t tmp_eth_size = eth_size; | ||||
if(eee->cb.packet_from_peer(eee, orig_sender, eth_payload, &tmp_eth_size) | ||||
== N2N_DROP) { | ||||
traceEvent(TRACE_DEBUG, "DROP packet %u", (unsigned int)eth_size); | ||||
return(0); | ||||
} | ||||
eth_size = tmp_eth_size; | ||||
} | ||||
/* Write ethernet packet to tap device. */ | ||||
traceEvent(TRACE_DEBUG, "sending to TAP %u", (unsigned int)eth_size); | ||||
data_sent_len = tuntap_write(&(eee->device), eth_payload, eth_size); | ||||
if(data_sent_len == eth_size) | ||||
{ | ||||
retval = 0; | ||||
} | ||||
} | } | |||
else | ||||
{ | ||||
traceEvent(TRACE_ERROR, "invalid transop ID: expected %s(%u), got %s(%u)" | ||||
, | ||||
transop_str(eee->conf.transop_id), eee->conf.transop_id, | ||||
transop_str(rx_transop_id), rx_transop_id); | ||||
} | ||||
} | ||||
return retval; | return retval; | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Read a datagram from the management UDP socket and take appropriate | #if 0 | |||
* action. */ | #ifndef WIN32 | |||
static void readFromMgmtSocket(n2n_edge_t * eee, int * keep_running) { | ||||
uint8_t udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */ | ||||
ssize_t recvlen; | ||||
/* ssize_t sendlen; */ | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
size_t msg_len; | ||||
time_t now; | ||||
now = time(NULL); | ||||
i = sizeof(sender_sock); | ||||
recvlen = recvfrom(eee->udp_mgmt_sock, udp_buf, N2N_PKT_BUF_SIZE, 0/*flags*/, | ||||
(struct sockaddr *)&sender_sock, (socklen_t*)&i); | ||||
if(recvlen < 0) | static char *get_ip_from_arp (dec_ip_str_t buf, const n2n_mac_t req_mac) { | |||
{ | ||||
traceEvent(TRACE_ERROR, "mgmt recvfrom failed with %s", strerror(errno)); | FILE *fd; | |||
dec_ip_str_t ip_str = {'\0'}; | ||||
char dev_str[N2N_IFNAMSIZ] = {'\0'}; | ||||
macstr_t mac_str = {'\0'}; | ||||
n2n_mac_t mac = {'\0'}; | ||||
strncpy(buf, "0.0.0.0", N2N_NETMASK_STR_SIZE - 1); | ||||
return; /* failed to receive data from UDP */ | if(is_null_mac(req_mac)) { | |||
traceEvent(TRACE_DEBUG, "MAC address is null."); | ||||
return buf; | ||||
} | } | |||
if(recvlen >= 4) | if(!(fd = fopen("/proc/net/arp", "r"))) { | |||
{ | traceEvent(TRACE_WARNING, "could not open arp table: %d - %s", errno, st | |||
if(0 == memcmp(udp_buf, "stop", 4)) | rerror(errno)); | |||
{ | return buf; | |||
traceEvent(TRACE_ERROR, "stop command received."); | } | |||
*keep_running = 0; | ||||
return; | while(!feof(fd) && fgetc(fd) != '\n'); | |||
while(!feof(fd) && (fscanf(fd, " %15[0-9.] %*s %*s %17[A-Fa-f0-9:] %*s %15s" | ||||
, ip_str, mac_str, dev_str) == 3)) { | ||||
str2mac(mac, mac_str); | ||||
if(0 == memcmp(mac, req_mac, sizeof(n2n_mac_t))) { | ||||
strncpy(buf, ip_str, N2N_NETMASK_STR_SIZE - 1); | ||||
break; | ||||
} | } | |||
} | ||||
fclose(fd); | ||||
if(0 == memcmp(udp_buf, "help", 4)) | return buf; | |||
{ | } | |||
msg_len=0; | ||||
setTraceLevel(getTraceLevel()+1); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_le | #endif | |||
n), | #endif | |||
"Help for edge management console:\n" | ||||
" stop Gracefully exit edge\n" | ||||
" help This help message\n" | ||||
" +verb Increase verbosity of logging\n" | ||||
" -verb Decrease verbosity of logging\n" | ||||
" <enter> Display statistics\n\n"); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | /** Read a datagram from the management UDP socket and take appropriate | |||
(struct sockaddr *)&sender_sock, sizeof(struct sockaddr_in)); | * action. */ | |||
static void readFromMgmtSocket (n2n_edge_t *eee) { | ||||
return; | char udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */ | |||
ssize_t recvlen; | ||||
/* ssize_t sendlen; */ | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
size_t msg_len; | ||||
time_t now; | ||||
struct peer_info *peer, *tmpPeer; | ||||
macstr_t mac_buf; | ||||
char time_buf[10]; /* 9 digits + 1 terminating zero */ | ||||
char uptime_buf[11]; /* 10 digits + 1 terminating zero */ | ||||
/* dec_ip_bit_str_t ip_bit_str = {'\0'}; */ | ||||
/* dec_ip_str_t ip_str = {'\0'}; */ | ||||
in_addr_t net; | ||||
n2n_sock_str_t sockbuf; | ||||
uint32_t num_pending_peers = 0; | ||||
uint32_t num_known_peers = 0; | ||||
uint32_t num = 0; | ||||
selection_criterion_str_t sel_buf; | ||||
now = time(NULL); | ||||
i = sizeof(sender_sock); | ||||
recvlen = recvfrom(eee->udp_mgmt_sock, udp_buf, N2N_PKT_BUF_SIZE, 0/*flags*/ | ||||
, | ||||
(struct sockaddr *) &sender_sock, (socklen_t *) &i); | ||||
if(recvlen < 0) { | ||||
traceEvent(TRACE_WARNING, "mgmt recvfrom failed: %d - %s", errno, strerr | ||||
or(errno)); | ||||
return; /* failed to receive data from UDP */ | ||||
} | ||||
/* avoid parsing any uninitialized junk from the stack */ | ||||
udp_buf[recvlen] = 0; | ||||
if((0 == memcmp(udp_buf, "help", 4)) || (0 == memcmp(udp_buf, "?", 1))) { | ||||
msg_len = 0; | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - ms | ||||
g_len), | ||||
"Help for edge management console:\n" | ||||
"\tstop | Gracefully exit edge\n" | ||||
"\thelp | This help message\n" | ||||
"\t+verb | Increase verbosity of logging\n" | ||||
"\t-verb | Decrease verbosity of logging\n" | ||||
"\tr ... | start query with JSON reply\n" | ||||
"\tw ... | start update with JSON reply\n" | ||||
"\t<enter> | Display statistics\n\n"); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
return; | ||||
} | ||||
if(0 == memcmp(udp_buf, "stop", 4)) { | ||||
traceEvent(TRACE_NORMAL, "stop command received"); | ||||
*eee->keep_running = 0; | ||||
return; | ||||
} | ||||
if(0 == memcmp(udp_buf, "+verb", 5)) { | ||||
msg_len = 0; | ||||
setTraceLevel(getTraceLevel() + 1); | ||||
traceEvent(TRACE_NORMAL, "+verb traceLevel=%u", (unsigned int) getTraceL | ||||
evel()); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - ms | ||||
g_len), | ||||
"> +OK traceLevel=%u\n", (unsigned int) getTraceLeve | ||||
l()); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
return; | ||||
} | ||||
if(0 == memcmp(udp_buf, "-verb", 5)) { | ||||
msg_len = 0; | ||||
if(getTraceLevel() > 0) { | ||||
setTraceLevel(getTraceLevel() - 1); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE | ||||
- msg_len), | ||||
"> -OK traceLevel=%u\n", getTraceLevel()); | ||||
} else { | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE | ||||
- msg_len), | ||||
"> -NOK traceLevel=%u\n", getTraceLevel()); | ||||
} | } | |||
traceEvent(TRACE_NORMAL, "-verb traceLevel=%u", (unsigned int) getTraceL | ||||
evel()); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
return; | ||||
} | ||||
if((udp_buf[0] == 'r' || udp_buf[0] == 'w') && (udp_buf[1] == ' ')) { | ||||
/* this is a JSON request */ | ||||
handleMgmtJson(eee, udp_buf, sender_sock); | ||||
return; | ||||
} | ||||
traceEvent(TRACE_DEBUG, "mgmt status requested"); | ||||
msg_len = 0; | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"COMMUNITY '%s'\n\n", | ||||
(eee->conf.header_encryption == HEADER_ENCRYPTION_NONE) | ||||
? (char*)eee->conf.community_name : "-- header encrypted --"); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
" ### | TAP | MAC | EDGE | ||||
| HINT | LAST SEEN | UPTIME\n"); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"======================================================= | ||||
======================================================\n"); | ||||
// dump nodes with forwarding through supernodes | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"SUPERNODE FORWARD\n"); | ||||
num = 0; | ||||
HASH_ITER(hh, eee->pending_peers, peer, tmpPeer) { | ||||
++num_pending_peers; | ||||
net = htonl(peer->dev_addr.net_addr); | ||||
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->l | ||||
ast_seen)); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - ms | ||||
g_len), | ||||
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n", | ||||
++num, | ||||
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(st | ||||
ruct in_addr *) &net), | ||||
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac | ||||
_buf, peer->mac_addr), | ||||
sock_to_cstr(sockbuf, &(peer->sock)), | ||||
peer->dev_desc, | ||||
(peer->last_seen) ? time_buf : ""); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
msg_len = 0; | ||||
} | ||||
// dump peer-to-peer nodes | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"------------------------------------------------------- | ||||
------------------------------------------------------\n"); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"PEER TO PEER\n"); | ||||
num = 0; | ||||
HASH_ITER(hh, eee->known_peers, peer, tmpPeer) { | ||||
++num_known_peers; | ||||
net = htonl(peer->dev_addr.net_addr); | ||||
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->l | ||||
ast_seen)); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - ms | ||||
g_len), | ||||
"%4u | %-15s | %-17s | %-21s | %-15s | %9s |\n", | ||||
++num, | ||||
(peer->dev_addr.net_addr == 0) ? "" : inet_ntoa(*(st | ||||
ruct in_addr *) &net), | ||||
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str(mac | ||||
_buf, peer->mac_addr), | ||||
sock_to_cstr(sockbuf, &(peer->sock)), | ||||
peer->dev_desc, | ||||
(peer->last_seen) ? time_buf : ""); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
msg_len = 0; | ||||
} | ||||
// dump supernodes | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"------------------------------------------------------- | ||||
------------------------------------------------------\n"); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"SUPERNODES\n"); | ||||
HASH_ITER(hh, eee->conf.supernodes, peer, tmpPeer) { | ||||
net = htonl(peer->dev_addr.net_addr); | ||||
snprintf(time_buf, sizeof(time_buf), "%9u", (unsigned int)(now - peer->l | ||||
ast_seen)); | ||||
snprintf(uptime_buf, sizeof(uptime_buf), "%10u", (unsigned int)(peer->up | ||||
time)); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - ms | ||||
g_len), | ||||
"%-19s %1s%1s | %-17s | %-21s | %-15s | %9s | %10s\n | ||||
", | ||||
peer->version, | ||||
(peer->purgeable == SN_UNPURGEABLE) ? "l" : "", | ||||
(peer == eee->curr_sn) ? (eee->sn_wait ? "." : "*" ) | ||||
: "", | ||||
is_null_mac(peer->mac_addr) ? "" : macaddr_str(mac_b | ||||
uf, peer->mac_addr), | ||||
sock_to_cstr(sockbuf, &(peer->sock)), | ||||
sn_selection_criterion_str(eee, sel_buf, peer), | ||||
(peer->last_seen) ? time_buf : "", | ||||
(peer->uptime) ? uptime_buf : ""); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct sockaddr_in)); | ||||
msg_len = 0; | ||||
} | ||||
// further stats | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"======================================================= | ||||
======================================================\n"); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"uptime %lu | ", | ||||
time(NULL) - eee->start_time); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"pend_peers %u | ", | ||||
num_pending_peers); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"known_peers %u | ", | ||||
num_known_peers); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"transop %u,%u\n", | ||||
(unsigned int) eee->transop.tx_cnt, | ||||
(unsigned int) eee->transop.rx_cnt); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"super %u,%u | ", | ||||
(unsigned int) eee->stats.tx_sup, | ||||
(unsigned int) eee->stats.rx_sup); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"p2p %u,%u\n", | ||||
(unsigned int) eee->stats.tx_p2p, | ||||
(unsigned int) eee->stats.rx_p2p); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"last_super %ld sec ago | ", | ||||
(now - eee->last_sup)); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"last_p2p %ld sec ago\n", | ||||
(now - eee->last_p2p)); | ||||
msg_len += snprintf((char *) (udp_buf + msg_len), (N2N_PKT_BUF_SIZE - msg_le | ||||
n), | ||||
"\nType \"help\" to see more commands.\n\n"); | ||||
/* sendlen = */ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | ||||
(struct sockaddr *) &sender_sock, sizeof(struct socka | ||||
ddr_in)); | ||||
} | ||||
/* ************************************** */ | ||||
static int check_query_peer_info (n2n_edge_t *eee, time_t now, n2n_mac_t mac) { | ||||
struct peer_info *scan; | ||||
HASH_FIND_PEER(eee->pending_peers, mac, scan); | ||||
if(!scan) { | ||||
scan = calloc(1, sizeof(struct peer_info)); | ||||
memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); | ||||
scan->timeout = eee->conf.register_interval; /* TODO: should correspond | ||||
to the peer supernode registration timeout */ | ||||
scan->last_seen = now; /* Don't change this it marks the pending peer fo | ||||
r removal. */ | ||||
scan->last_valid_time_stamp = initial_time_stamp(); | ||||
HASH_ADD_PEER(eee->pending_peers, scan); | ||||
} | ||||
if(now - scan->last_sent_query > eee->conf.register_interval) { | ||||
send_register(eee, &(eee->curr_sn->sock), mac, N2N_FORWARDED_REG_COOKIE) | ||||
; | ||||
send_query_peer(eee, scan->mac_addr); | ||||
scan->last_sent_query = now; | ||||
return(0); | ||||
} | } | |||
if(recvlen >= 5) | return(1); | |||
{ | } | |||
if(0 == memcmp(udp_buf, "+verb", 5)) | ||||
{ | ||||
msg_len=0; | ||||
setTraceLevel(getTraceLevel()+1); | ||||
traceEvent(TRACE_ERROR, "+verb traceLevel=%u", (unsigned int)getTraceLe | /* ************************************** */ | |||
vel()); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_le | ||||
n), | ||||
"> +OK traceLevel=%u\n", (unsigned int)getTraceLeve | ||||
l()); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | /* @return 1 if destination is a peer, 0 if destination is supernode */ | |||
(struct sockaddr *)&sender_sock, sizeof(struct sockaddr_in)); | static int find_peer_destination (n2n_edge_t * eee, | |||
n2n_mac_t mac_address, | ||||
n2n_sock_t * destination) { | ||||
struct peer_info *scan; | ||||
macstr_t mac_buf; | ||||
n2n_sock_str_t sockbuf; | ||||
int retval = 0; | ||||
time_t now = time(NULL); | ||||
return; | if(is_multi_broadcast(mac_address)) { | |||
traceEvent(TRACE_DEBUG, "multicast or broadcast destination peer, using | ||||
supernode"); | ||||
memcpy(destination, &(eee->curr_sn->sock), sizeof(struct sockaddr_in)); | ||||
return(0); | ||||
} | ||||
traceEvent(TRACE_DEBUG, "searching destination socket for %s", | ||||
macaddr_str(mac_buf, mac_address)); | ||||
HASH_FIND_PEER(eee->known_peers, mac_address, scan); | ||||
if(scan && (scan->last_seen > 0)) { | ||||
if((now - scan->last_p2p) >= (scan->timeout / 2)) { | ||||
/* Too much time passed since we saw the peer, need to register agai | ||||
n | ||||
* since the peer address may have changed. */ | ||||
traceEvent(TRACE_DEBUG, "refreshing idle known peer"); | ||||
HASH_DEL(eee->known_peers, scan); | ||||
free(scan); | ||||
/* NOTE: registration will be performed upon the receival of the nex | ||||
t response packet */ | ||||
} else { | ||||
/* Valid known peer found */ | ||||
memcpy(destination, &scan->sock, sizeof(n2n_sock_t)); | ||||
retval = 1; | ||||
} | } | |||
} | ||||
if(0 == memcmp(udp_buf, "-verb", 5)) | if(retval == 0) { | |||
{ | memcpy(destination, &(eee->curr_sn->sock), sizeof(struct sockaddr_in)); | |||
msg_len=0; | traceEvent(TRACE_DEBUG, "p2p peer %s not found, using supernode", | |||
macaddr_str(mac_buf, mac_address)); | ||||
if(getTraceLevel() > 0) | ||||
{ | ||||
setTraceLevel(getTraceLevel()-1); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-ms | ||||
g_len), | ||||
"> -OK traceLevel=%u\n", getTraceLevel()); | ||||
} | ||||
else | ||||
{ | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-ms | ||||
g_len), | ||||
"> -NOK traceLevel=%u\n", getTraceLevel()); | ||||
} | ||||
traceEvent(TRACE_ERROR, "-verb traceLevel=%u", (unsigned int)getTraceLe | ||||
vel()); | ||||
sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | check_query_peer_info(eee, now, mac_address); | |||
(struct sockaddr *)&sender_sock, sizeof(struct sockaddr_in)); | ||||
return; | ||||
} | ||||
} | } | |||
traceEvent(TRACE_DEBUG, "mgmt status rq"); | traceEvent(TRACE_DEBUG, "found peer's socket %s [%s]", | |||
macaddr_str(mac_buf, mac_address), | ||||
sock_to_cstr(sockbuf, destination)); | ||||
msg_len=0; | return retval; | |||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | } | |||
"Statistics for edge\n"); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | /* ***************************************************** */ | |||
"uptime %lu\n", | ||||
time(NULL) - eee->start_time); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | /** Send an ecapsulated ethernet PACKET to a destination edge or broadcast MAC | |||
"paths super:%u,%u p2p:%u,%u\n", | * address. */ | |||
(unsigned int)eee->stats.tx_sup, | static int send_packet (n2n_edge_t * eee, | |||
(unsigned int)eee->stats.rx_sup, | n2n_mac_t dstMac, | |||
(unsigned int)eee->stats.tx_p2p, | const uint8_t * pktbuf, | |||
(unsigned int)eee->stats.rx_p2p); | size_t pktlen) { | |||
int is_p2p; | ||||
/*ssize_t s; */ | ||||
n2n_sock_str_t sockbuf; | ||||
n2n_sock_t destination; | ||||
macstr_t mac_buf; | ||||
struct peer_info *peer, *tmp_peer; | ||||
/* hexdump(pktbuf, pktlen); */ | ||||
is_p2p = find_peer_destination(eee, dstMac, &destination); | ||||
traceEvent(TRACE_INFO, "Tx PACKET of %u bytes to %s [%s]", | ||||
pktlen, macaddr_str(mac_buf, dstMac), | ||||
sock_to_cstr(sockbuf, &destination)); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | if(is_p2p) | |||
"transop |%6u|%6u|\n", | ++(eee->stats.tx_p2p); | |||
(unsigned int)eee->transop.tx_cnt, | else | |||
(unsigned int)eee->transop.rx_cnt); | ++(eee->stats.tx_sup); | |||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | if(is_multi_broadcast(dstMac)) { | |||
"peers pend:%u full:%u\n", | ++(eee->stats.tx_sup_broadcast); | |||
HASH_COUNT(eee->pending_peers), | ||||
HASH_COUNT(eee->known_peers)); | ||||
msg_len += snprintf((char *)(udp_buf+msg_len), (N2N_PKT_BUF_SIZE-msg_len), | // if no supernode around, foward the broadcast to all known peers | |||
"last super:%lu(%ld sec ago) p2p:%lu(%ld sec ago)\n", | if(eee->sn_wait) { | |||
eee->last_sup, (now-eee->last_sup), eee->last_p2p, | HASH_ITER(hh, eee->known_peers, peer, tmp_peer) | |||
(now-eee->last_p2p)); | /* s = */ sendto_sock(eee, pktbuf, pktlen, &peer->sock); | |||
return 0; | ||||
} | ||||
// fall through otherwise | ||||
} | ||||
traceEvent(TRACE_DEBUG, "mgmt status sending: %s", udp_buf); | /* s = */ sendto_sock(eee, pktbuf, pktlen, &destination); | |||
/* sendlen = */ sendto(eee->udp_mgmt_sock, udp_buf, msg_len, 0/*flags*/, | return 0; | |||
(struct sockaddr *)&sender_sock, sizeof(struct sockaddr_ | ||||
in)); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static int check_query_peer_info(n2n_edge_t *eee, time_t now, n2n_mac_t mac) { | /** A layer-2 packet was received at the tunnel and needs to be sent via UDP. */ | |||
struct peer_info *scan; | void edge_send_packet2net (n2n_edge_t * eee, | |||
uint8_t *tap_pkt, size_t len) { | ||||
HASH_FIND_PEER(eee->pending_peers, mac, scan); | ipstr_t ip_buf; | |||
n2n_mac_t destMac; | ||||
n2n_common_t cmn; | ||||
n2n_PACKET_t pkt; | ||||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | ||||
size_t idx = 0; | ||||
n2n_transform_t tx_transop_idx = eee->transop.transform_id; | ||||
ether_hdr_t eh; | ||||
if(!scan) { | /* tap_pkt is not aligned so we have to copy to aligned memory */ | |||
scan = calloc(1, sizeof(struct peer_info)); | memcpy(&eh, tap_pkt, sizeof(ether_hdr_t)); | |||
memcpy(scan->mac_addr, mac, N2N_MAC_SIZE); | /* Discard IP packets that are not originated by this hosts */ | |||
scan->timeout = REGISTER_SUPER_INTERVAL_DFL; /* TODO: should correspond to t | if(!(eee->conf.allow_routing)) { | |||
he peer supernode registration timeout */ | if(ntohs(eh.type) == 0x0800) { | |||
scan->last_seen = now; /* Don't change this it marks the pending peer for re | /* This is an IP packet from the local source address - not forwarde | |||
moval. */ | d. */ | |||
scan->last_valid_time_stamp = initial_time_stamp (); | uint32_t *src = (uint32_t*)&tap_pkt[ETH_FRAMESIZE + IP4_SRCOFFSET]; | |||
/* Note: all elements of the_ip are in network order */ | ||||
if(*src != eee->device.ip_addr) { | ||||
/* This is a packet that needs to be routed */ | ||||
traceEvent(TRACE_INFO, "discarding routed packet destined to [%s | ||||
]", | ||||
intoa(ntohl(*src), ip_buf, sizeof(ip_buf))); | ||||
return; | ||||
} else { | ||||
/* This packet is originated by us */ | ||||
/* traceEvent(TRACE_INFO, "Sending non-routed packet"); */ | ||||
} | ||||
} | ||||
} | ||||
HASH_ADD_PEER(eee->pending_peers, scan); | /* Optionally compress then apply transforms, eg encryption. */ | |||
} | ||||
if(now - scan->last_sent_query > REGISTER_SUPER_INTERVAL_DFL) { | /* Once processed, send to destination in PACKET */ | |||
send_query_peer(eee, scan->mac_addr); | ||||
scan->last_sent_query = now; | ||||
return(0); | ||||
} | ||||
return(1); | memcpy(destMac, tap_pkt, N2N_MAC_SIZE); /* dest MAC is first in ethernet hea | |||
} | der */ | |||
/* ************************************** */ | memset(&cmn, 0, sizeof(cmn)); | |||
cmn.ttl = N2N_DEFAULT_TTL; | ||||
cmn.pc = n2n_packet; | ||||
cmn.flags = 0; /* no options, not from supernode, no socket */ | ||||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | ||||
/* @return 1 if destination is a peer, 0 if destination is supernode */ | memset(&pkt, 0, sizeof(pkt)); | |||
static int find_peer_destination(n2n_edge_t * eee, | memcpy(pkt.srcMac, eee->device.mac_addr, N2N_MAC_SIZE); | |||
n2n_mac_t mac_address, | memcpy(pkt.dstMac, destMac, N2N_MAC_SIZE); | |||
n2n_sock_t * destination) { | ||||
struct peer_info *scan; | ||||
macstr_t mac_buf; | ||||
n2n_sock_str_t sockbuf; | ||||
int retval=0; | ||||
time_t now = time(NULL); | ||||
if(!memcmp(mac_address, broadcast_mac, N2N_MAC_SIZE)) { | ||||
traceEvent(TRACE_DEBUG, "Broadcast destination peer, using supernode"); | ||||
memcpy(destination, &(eee->supernode), sizeof(struct sockaddr_in)); | ||||
return(0); | ||||
} | ||||
traceEvent(TRACE_DEBUG, "Searching destination peer for MAC %02X:%02X:%02X:%02 | pkt.transform = tx_transop_idx; | |||
X:%02X:%02X", | ||||
mac_address[0] & 0xFF, mac_address[1] & 0xFF, mac_address[2] & 0xFF, | ||||
mac_address[3] & 0xFF, mac_address[4] & 0xFF, mac_address[5] & 0xFF) | ||||
; | ||||
HASH_FIND_PEER(eee->known_peers, mac_address, scan); | ||||
if(scan && (scan->last_seen > 0)) { | ||||
if((now - scan->last_p2p) >= (scan->timeout / 2)) { | ||||
/* Too much time passed since we saw the peer, need to register again | ||||
* since the peer address may have changed. */ | ||||
traceEvent(TRACE_DEBUG, "Refreshing idle known peer"); | ||||
HASH_DEL(eee->known_peers, scan); | ||||
free(scan); | ||||
/* NOTE: registration will be performed upon the receival of the next resp | ||||
onse packet */ | ||||
} else { | ||||
/* Valid known peer found */ | ||||
memcpy(destination, &scan->sock, sizeof(n2n_sock_t)); | ||||
retval=1; | ||||
} | ||||
} | ||||
if(retval == 0) { | ||||
memcpy(destination, &(eee->supernode), sizeof(struct sockaddr_in)); | ||||
traceEvent(TRACE_DEBUG, "P2P Peer [MAC=%02X:%02X:%02X:%02X:%02X:%02X] not fo | ||||
und, using supernode", | ||||
mac_address[0] & 0xFF, mac_address[1] & 0xFF, mac_address[2] & 0xF | ||||
F, | ||||
mac_address[3] & 0xFF, mac_address[4] & 0xFF, mac_address[5] & 0xF | ||||
F); | ||||
check_query_peer_info(eee, now, mac_address); | ||||
} | ||||
traceEvent(TRACE_DEBUG, "find_peer_address (%s) -> [%s]", | ||||
macaddr_str(mac_buf, mac_address), | ||||
sock_to_cstr(sockbuf, destination)); | ||||
return retval; | // compression needs to be tried before encode_PACKET is called for compress | |||
} | ion indication gets encoded there | |||
pkt.compression = N2N_COMPRESSION_ID_NONE; | ||||
/* ***************************************************** */ | if(eee->conf.compression) { | |||
uint8_t * compression_buffer = NULL; | ||||
int32_t compression_len; | ||||
/** Send an ecapsulated ethernet PACKET to a destination edge or broadcast MAC | switch(eee->conf.compression) { | |||
* address. */ | case N2N_COMPRESSION_ID_LZO: | |||
static int send_packet(n2n_edge_t * eee, | compression_buffer = malloc(len + len / 16 + 64 + 3); | |||
n2n_mac_t dstMac, | if(lzo1x_1_compress(tap_pkt, len, compression_buffer, (lzo_uint* | |||
const uint8_t * pktbuf, | )&compression_len, wrkmem) == LZO_E_OK) { | |||
size_t pktlen) { | if(compression_len < len) { | |||
int is_p2p; | pkt.compression = N2N_COMPRESSION_ID_LZO; | |||
/*ssize_t s; */ | } | |||
n2n_sock_str_t sockbuf; | } | |||
n2n_sock_t destination; | break; | |||
macstr_t mac_buf; | #ifdef N2N_HAVE_ZSTD | |||
case N2N_COMPRESSION_ID_ZSTD: | ||||
/* hexdump(pktbuf, pktlen); */ | compression_len = N2N_PKT_BUF_SIZE + 128; | |||
compression_buffer = malloc(compression_len); // leaves enough | ||||
is_p2p = find_peer_destination(eee, dstMac, &destination); | room, for exact size call compression_len = ZSTD_compressBound (len); (slower) | |||
compression_len = (int32_t)ZSTD_compress(compression_buffer, com | ||||
if(is_p2p) | pression_len, tap_pkt, len, ZSTD_COMPRESSION_LEVEL); | |||
++(eee->stats.tx_p2p); | if(!ZSTD_isError(compression_len)) { | |||
else { | if(compression_len < len) { | |||
++(eee->stats.tx_sup); | pkt.compression = N2N_COMPRESSION_ID_ZSTD; | |||
} | ||||
if(!memcmp(dstMac, broadcast_mac, N2N_MAC_SIZE)) | } else { | |||
++(eee->stats.tx_sup_broadcast); | traceEvent(TRACE_ERROR, "payload compression failed with zst | |||
} | d error '%s'.", | |||
ZSTD_getErrorName(compression_len)); | ||||
traceEvent(TRACE_INFO, "Tx PACKET to %s (dest=%s) [%u B]", | free(compression_buffer); | |||
sock_to_cstr(sockbuf, &destination), | // continue with unset without pkt.compression --> will send | |||
macaddr_str(mac_buf, dstMac), pktlen); | uncompressed | |||
} | ||||
break; | ||||
#endif | ||||
default: | ||||
break; | ||||
} | ||||
/* s = */ sendto_sock(eee->udp_sock, pktbuf, pktlen, &destination); | if(pkt.compression != N2N_COMPRESSION_ID_NONE) { | |||
traceEvent(TRACE_DEBUG, "payload compression [%s]: compressed %u byt | ||||
es to %u bytes\n", | ||||
compression_str(pkt.compression), len, compression_len); | ||||
return 0; | memcpy(tap_pkt, compression_buffer, compression_len); | |||
} | len = compression_len; | |||
} | ||||
/* ************************************** */ | if(compression_buffer) { | |||
free(compression_buffer); | ||||
} | ||||
} | ||||
/** A layer-2 packet was received at the tunnel and needs to be sent via UDP. */ | idx = 0; | |||
void edge_send_packet2net(n2n_edge_t * eee, | encode_PACKET(pktbuf, &idx, &cmn, &pkt); | |||
uint8_t *tap_pkt, size_t len) { | ||||
ipstr_t ip_buf; | uint16_t headerIdx = idx; | |||
n2n_mac_t destMac; | ||||
idx += eee->transop.fwd(&eee->transop, | ||||
n2n_common_t cmn; | pktbuf + idx, N2N_PKT_BUF_SIZE - idx, | |||
n2n_PACKET_t pkt; | tap_pkt, len, pkt.dstMac); | |||
uint8_t pktbuf[N2N_PKT_BUF_SIZE]; | traceEvent(TRACE_DEBUG, "encode PACKET of %u bytes, %u bytes data, %u bytes | |||
size_t idx=0; | overhead, transform %u", | |||
n2n_transform_t tx_transop_idx = eee->transop.transform_id; | (u_int)idx, (u_int)len, (u_int)(idx - len), tx_transop_idx); | |||
ether_hdr_t eh; | if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | |||
// in case of user-password auth, also encrypt the iv of payload assumin | ||||
/* tap_pkt is not aligned so we have to copy to aligned memory */ | g ChaCha20 and SPECK having the same iv size | |||
memcpy(&eh, tap_pkt, sizeof(ether_hdr_t)); | packet_header_encrypt(pktbuf, headerIdx + (NULL != eee->conf.shared_secr | |||
et) * min(idx - headerIdx, N2N_SPECK_IVEC_SIZE), idx, | ||||
/* Discard IP packets that are not originated by this hosts */ | eee->conf.header_encryption_ctx_dynamic, eee->conf | |||
if(!(eee->conf.allow_routing)) { | .header_iv_ctx_dynamic, | |||
if(ntohs(eh.type) == 0x0800) { | time_stamp()); | |||
/* This is an IP packet from the local source address - not forwarded. */ | ||||
uint32_t *src = (uint32_t*)&tap_pkt[ETH_FRAMESIZE + IP4_SRCOFFSET]; | ||||
/* Note: all elements of the_ip are in network order */ | ||||
if(*src != eee->device.ip_addr) { | ||||
/* This is a packet that needs to be routed */ | ||||
traceEvent(TRACE_INFO, "Discarding routed packet [%s]", | ||||
intoa(ntohl(*src), ip_buf, sizeof(ip_buf))); | ||||
return; | ||||
} else { | ||||
/* This packet is originated by us */ | ||||
/* traceEvent(TRACE_INFO, "Sending non-routed packet"); */ | ||||
} | ||||
} | ||||
} | ||||
/* Optionally compress then apply transforms, eg encryption. */ | ||||
/* Once processed, send to destination in PACKET */ | ||||
memcpy(destMac, tap_pkt, N2N_MAC_SIZE); /* dest MAC is first in ethernet heade | ||||
r */ | ||||
memset(&cmn, 0, sizeof(cmn)); | ||||
cmn.ttl = N2N_DEFAULT_TTL; | ||||
cmn.pc = n2n_packet; | ||||
cmn.flags=0; /* no options, not from supernode, no socket */ | ||||
memcpy(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE); | ||||
memset(&pkt, 0, sizeof(pkt)); | ||||
memcpy(pkt.srcMac, eee->device.mac_addr, N2N_MAC_SIZE); | ||||
memcpy(pkt.dstMac, destMac, N2N_MAC_SIZE); | ||||
pkt.sock.family=0; /* do not encode sock */ | ||||
pkt.transform = tx_transop_idx; | ||||
// compression needs to be tried before encode_PACKET is called for compressio | ||||
n indication gets encoded there | ||||
pkt.compression = N2N_COMPRESSION_ID_NONE; | ||||
if(eee->conf.compression) { | ||||
uint8_t * compression_buffer = NULL; | ||||
int32_t compression_len; | ||||
switch (eee->conf.compression) { | ||||
case N2N_COMPRESSION_ID_LZO: | ||||
compression_buffer = malloc (len + len / 16 + 64 + 3); | ||||
if(lzo1x_1_compress(tap_pkt, len, compression_buffer, (lzo_uint*)&compress | ||||
ion_len, wrkmem) == LZO_E_OK) { | ||||
if(compression_len < len) { | ||||
pkt.compression = N2N_COMPRESSION_ID_LZO; | ||||
} | ||||
} | ||||
break; | ||||
#ifdef N2N_HAVE_ZSTD | ||||
case N2N_COMPRESSION_ID_ZSTD: | ||||
compression_len = N2N_PKT_BUF_SIZE + 128; | ||||
compression_buffer = malloc (compression_len); // leaves enough room, for | ||||
exact size call compression_len = ZSTD_compressBound (len); (slower) | ||||
compression_len = (int32_t)ZSTD_compress(compression_buffer, compression_l | ||||
en, tap_pkt, len, ZSTD_COMPRESSION_LEVEL) ; | ||||
if(!ZSTD_isError(compression_len)) { | ||||
if(compression_len < len) { | ||||
pkt.compression = N2N_COMPRESSION_ID_ZSTD; | ||||
} | ||||
} else { | ||||
traceEvent (TRACE_ERROR, "payload compression failed with zstd error '%s' | ||||
.", | ||||
ZSTD_getErrorName(compression_len)); | ||||
free (compression_buffer); | ||||
// continue with unset without pkt.compression --> will send uncompressed | ||||
} | ||||
break; | ||||
#endif | ||||
default: | ||||
break; | ||||
} | ||||
if(pkt.compression) { | ||||
traceEvent (TRACE_DEBUG, "payload compression [%s]: compressed %u bytes to | ||||
%u bytes\n", | ||||
compression_str(pkt.compression), len, compression_len); | ||||
memcpy (tap_pkt, compression_buffer, compression_len); | ||||
len = compression_len; | ||||
free (compression_buffer); | ||||
} | ||||
} | ||||
/* optional compression is encoded in uppermost bits of transform field. | ||||
* this is an intermediate solution to maintain compatibility until some | ||||
* upcoming major release (3.0?) brings up changes in packet structure anyway | ||||
* in the course of which a dedicated compression field could be spent. | ||||
* REVISIT then. */ | ||||
pkt.transform = pkt.transform | (pkt.compression << (8*sizeof(pkt.transform)-N | ||||
2N_COMPRESSION_ID_BITLEN)); | ||||
idx=0; | ||||
encode_PACKET(pktbuf, &idx, &cmn, &pkt); | ||||
uint16_t headerIdx = idx; | ||||
idx += eee->transop.fwd(&eee->transop, | ||||
pktbuf+idx, N2N_PKT_BUF_SIZE-idx, | ||||
tap_pkt, len, pkt.dstMac); | ||||
traceEvent(TRACE_DEBUG, "Encode %u B PACKET [%u B data, %u B overhead] transfo | ||||
rm %u", | ||||
(u_int)idx, (u_int)len, (u_int)(idx-len), tx_transop_idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (pktbuf, headerIdx, eee->conf.header_encryption_ctx, | ||||
eee->conf.header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (pk | ||||
tbuf, idx)); | ||||
#ifdef MTU_ASSERT_VALUE | #ifdef MTU_ASSERT_VALUE | |||
{ | { | |||
const u_int eth_udp_overhead = ETH_FRAMESIZE + IP4_MIN_SIZE + UDP_SIZE; | const u_int eth_udp_overhead = ETH_FRAMESIZE + IP4_MIN_SIZE + UDP_SIZE; | |||
// MTU assertion which avoids fragmentation by N2N | // MTU assertion which avoids fragmentation by N2N | |||
assert(idx + eth_udp_overhead <= MTU_ASSERT_VALUE); | assert(idx + eth_udp_overhead <= MTU_ASSERT_VALUE); | |||
} | } | |||
#endif | #endif | |||
eee->transop.tx_cnt++; /* stats */ | eee->transop.tx_cnt++; /* stats */ | |||
send_packet(eee, destMac, pktbuf, idx); /* to peer or supernode */ | send_packet(eee, destMac, pktbuf, idx); /* to peer or supernode */ | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Read a single packet from the TAP interface, process it and write out the | /** Read a single packet from the TAP interface, process it and write out the | |||
* corresponding packet to the cooked socket. | * corresponding packet to the cooked socket. | |||
*/ | */ | |||
void edge_read_from_tap(n2n_edge_t * eee) { | void edge_read_from_tap (n2n_edge_t * eee) { | |||
/* tun -> remote */ | ||||
uint8_t eth_pkt[N2N_PKT_BUF_SIZE]; | ||||
macstr_t mac_buf; | ||||
ssize_t len; | ||||
len = tuntap_read( &(eee->device), eth_pkt, N2N_PKT_BUF_SIZE ); | /* tun -> remote */ | |||
if((len <= 0) || (len > N2N_PKT_BUF_SIZE)) | uint8_t eth_pkt[N2N_PKT_BUF_SIZE]; | |||
{ | macstr_t mac_buf; | |||
traceEvent(TRACE_WARNING, "read()=%d [%d/%s]", | ssize_t len; | |||
(signed int)len, errno, strerror(errno)); | ||||
traceEvent(TRACE_WARNING, "TAP I/O operation aborted, restart later."); | len = tuntap_read( &(eee->device), eth_pkt, N2N_PKT_BUF_SIZE ); | |||
sleep(3); | if((len <= 0) || (len > N2N_PKT_BUF_SIZE)) { | |||
tuntap_close(&(eee->device)); | traceEvent(TRACE_WARNING, "read()=%d [%d/%s]", | |||
tuntap_open(&(eee->device), eee->tuntap_priv_conf.tuntap_dev_name, eee->tu | (signed int)len, errno, strerror(errno)); | |||
ntap_priv_conf.ip_mode, eee->tuntap_priv_conf.ip_addr, | traceEvent(TRACE_WARNING, "TAP I/O operation aborted, restart later."); | |||
eee->tuntap_priv_conf.netmask, eee->tuntap_priv_conf.device_mac | sleep(3); | |||
, eee->tuntap_priv_conf.mtu); | tuntap_close(&(eee->device)); | |||
} | tuntap_open(&(eee->device), eee->tuntap_priv_conf.tuntap_dev_name, eee-> | |||
else | tuntap_priv_conf.ip_mode, eee->tuntap_priv_conf.ip_addr, | |||
{ | eee->tuntap_priv_conf.netmask, eee->tuntap_priv_conf.device_ | |||
const uint8_t * mac = eth_pkt; | mac, eee->tuntap_priv_conf.mtu | |||
traceEvent(TRACE_DEBUG, "### Rx TAP packet (%4d) for %s", | #ifdef WIN32 | |||
(signed int)len, macaddr_str(mac_buf, mac)); | ,eee->tuntap_priv_conf.metric | |||
#endif | ||||
); | ||||
} else { | ||||
const uint8_t * mac = eth_pkt; | ||||
traceEvent(TRACE_DEBUG, "Rx TAP packet (%4d) for %s", | ||||
(signed int)len, macaddr_str(mac_buf, mac)); | ||||
if(eee->conf.drop_multicast && | ||||
(is_ip6_discovery(eth_pkt, len) || | ||||
is_ethMulticast(eth_pkt, len))) { | ||||
traceEvent(TRACE_INFO, "dropping Tx multicast"); | ||||
} else { | ||||
if(!eee->last_sup) { | ||||
// drop packets before first registration with supernode | ||||
traceEvent(TRACE_DEBUG, "DROP packet before first registration w | ||||
ith supernode"); | ||||
return; | ||||
} | ||||
if(eee->conf.drop_multicast && | if(eee->network_traffic_filter) { | |||
(is_ip6_discovery(eth_pkt, len) || | if(eee->network_traffic_filter->filter_packet_from_tap(eee->netw | |||
is_ethMulticast(eth_pkt, len) | ork_traffic_filter, eee, eth_pkt, | |||
) | len) | |||
) | == N2N_DROP) { | |||
{ | traceEvent(TRACE_DEBUG, "filtered packet of size %u", (unsig | |||
traceEvent(TRACE_INFO, "Dropping TX multicast"); | ned int)len); | |||
} | return; | |||
else | } | |||
{ | } | |||
if(eee->cb.packet_from_tap) { | ||||
uint16_t tmp_len = len; | ||||
if(eee->cb.packet_from_tap(eee, eth_pkt, &tmp_len) == N2N_DROP) { | ||||
traceEvent(TRACE_DEBUG, "DROP packet %u", (unsigned int)len); | ||||
return; | if(eee->cb.packet_from_tap) { | |||
} | uint16_t tmp_len = len; | |||
len = tmp_len; | if(eee->cb.packet_from_tap(eee, eth_pkt, &tmp_len) == N2N_DROP) | |||
} | { | |||
traceEvent(TRACE_DEBUG, "DROP packet of size %u", (unsigned | ||||
int)len); | ||||
return; | ||||
} | ||||
len = tmp_len; | ||||
} | ||||
edge_send_packet2net(eee, eth_pkt, len); | edge_send_packet2net(eee, eth_pkt, len); | |||
} | } | |||
} | } | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/* ************************************** */ | /** handle a datagram from the main UDP socket to the internet. */ | |||
void process_udp (n2n_edge_t *eee, const struct sockaddr_in *sender_sock, const | ||||
SOCKET in_sock, | ||||
uint8_t *udp_buf, size_t udp_size, time_t now) { | ||||
n2n_common_t cmn; /* common fields in the packet header */ | ||||
n2n_sock_str_t sockbuf1; | ||||
n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two add | ||||
resses to trace */ | ||||
macstr_t mac_buf1; | ||||
macstr_t mac_buf2; | ||||
uint8_t hash_buf[16]; | ||||
size_t rem; | ||||
size_t idx; | ||||
size_t msg_type; | ||||
uint8_t from_supernode; | ||||
uint8_t via_multicast; | ||||
peer_info_t *sn = NULL; | ||||
n2n_sock_t sender; | ||||
n2n_sock_t * orig_sender = NULL; | ||||
uint32_t header_enc = 0; | ||||
uint64_t stamp = 0; | ||||
int skip_add = 0; | ||||
/* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which | ||||
* IP transport version the packet arrived on. May need to UDP sockets. */ | ||||
memset(&sender, 0, sizeof(n2n_sock_t)); | ||||
if(eee->conf.connect_tcp) | ||||
// TCP expects that we know our comm partner and does not deliver the se | ||||
nder | ||||
memcpy(&sender, &(eee->curr_sn->sock), sizeof(struct sockaddr_in)); | ||||
else { | ||||
sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */ | ||||
sender.port = ntohs(sender_sock->sin_port); | ||||
memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE); | ||||
} | ||||
/* The packet may not have an orig_sender socket spec. So default to last | ||||
* hop as sender. */ | ||||
orig_sender = &sender; | ||||
/** Read a datagram from the main UDP socket to the internet. */ | #ifdef SKIP_MULTICAST_PEERS_DISCOVERY | |||
static void readFromIPSocket(n2n_edge_t * eee, int in_sock) { | via_multicast = 0; | |||
n2n_common_t cmn; /* common fields in the packet header */ | #else | |||
via_multicast = (in_sock == eee->udp_multicast_sock); | ||||
#endif | ||||
n2n_sock_str_t sockbuf1; | traceEvent(TRACE_DEBUG, "Rx N2N_UDP of size %d from [%s]", | |||
n2n_sock_str_t sockbuf2; /* don't clobber sockbuf1 if writing two address | (signed int)udp_size, sock_to_cstr(sockbuf1, &sender)); | |||
es to trace */ | ||||
macstr_t mac_buf1; | ||||
macstr_t mac_buf2; | ||||
uint8_t udp_buf[N2N_PKT_BUF_SIZE]; /* Compete UDP packet */ | if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | |||
ssize_t recvlen; | // match with static (1) or dynamic (2) ctx? | |||
size_t rem; | // check dynamic first as it is identical to static in normal header enc | |||
size_t idx; | ryption mode | |||
size_t msg_type; | if(packet_header_decrypt(udp_buf, udp_size, | |||
uint8_t from_supernode; | (char *)eee->conf.community_name, | |||
struct sockaddr_in sender_sock; | eee->conf.header_encryption_ctx_dynamic, ee | |||
n2n_sock_t sender; | e->conf.header_iv_ctx_dynamic, | |||
n2n_sock_t * orig_sender=NULL; | &stamp)) { | |||
time_t now=0; | header_enc = 2; /* not accurate with normal header encryption bu | |||
uint64_t stamp = 0; | t does not matter */ | |||
} | ||||
if(!header_enc) { | ||||
// check static now (very likely to be REGISTER_SUPER_ACK, REGISTER_ | ||||
SUPER_NAK or invalid) | ||||
if(eee->conf.shared_secret) { | ||||
// hash the still encrypted packet to eventually be able to chec | ||||
k it later (required for REGISTER_SUPER_ACK with user/pw auth) | ||||
pearson_hash_128(hash_buf, udp_buf, max(0, (int)udp_size - (int) | ||||
N2N_REG_SUP_HASH_CHECK_LEN)); | ||||
} | ||||
header_enc = packet_header_decrypt(udp_buf, max(0, (int)udp_size - ( | ||||
int)N2N_REG_SUP_HASH_CHECK_LEN), | ||||
(char *)eee->conf.community_name, | ||||
eee->conf.header_encryption_ctx_stati | ||||
c, eee->conf.header_iv_ctx_static, | ||||
&stamp); | ||||
} | ||||
if(!header_enc) { | ||||
traceEvent(TRACE_DEBUG, "failed to decrypt header"); | ||||
return; | ||||
} | ||||
// time stamp verification follows in the packet specific section as it | ||||
requires to determine the | ||||
// sender from the hash list by its MAC, or the packet might be from the | ||||
supernode, this all depends | ||||
// on packet type, path taken (via supernode) and packet structure (MAC | ||||
is not always in the same place) | ||||
} | ||||
size_t i; | rem = udp_size; /* Counts down bytes of packet to protect against buffer ove | |||
rruns. */ | ||||
idx = 0; /* marches through packet header as parts are decoded. */ | ||||
if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) { | ||||
if(via_multicast) { | ||||
// from some other edge on local network, possibly header encrypted | ||||
traceEvent(TRACE_DEBUG, "dropped packet arriving via multicast due t | ||||
o error while decoding N2N_UDP"); | ||||
} else { | ||||
traceEvent(TRACE_INFO, "failed to decode common section in N2N_UDP") | ||||
; | ||||
} | ||||
return; /* failed to decode packet */ | ||||
} | ||||
i = sizeof(sender_sock); | msg_type = cmn.pc; /* packet code */ | |||
recvlen = recvfrom(in_sock, udp_buf, N2N_PKT_BUF_SIZE, 0/*flags*/, | ||||
(struct sockaddr *)&sender_sock, (socklen_t*)&i); | ||||
if(recvlen < 0) { | // special case for user/pw auth | |||
#ifdef WIN32 | // community's auth scheme and message type need to match the used key (dyna | |||
if(WSAGetLastError() != WSAECONNRESET) | mic) | |||
#endif | if((eee->conf.shared_secret) | |||
{ | && (msg_type != MSG_TYPE_REGISTER_SUPER_ACK) | |||
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", recvlen, er | && (msg_type != MSG_TYPE_REGISTER_SUPER_NAK)) { | |||
rno, strerror(errno)); | if(header_enc != 2) { | |||
#ifdef WIN32 | traceEvent(TRACE_INFO, "dropped packet encrypted with static key whe | |||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); | re dynamic key expected"); | |||
#endif | return; | |||
} | } | |||
} | ||||
return; /* failed to receive data from UDP */ | // check if packet is from supernode and find the corresponding supernode in | |||
} | list | |||
from_supernode = cmn.flags & N2N_FLAGS_FROM_SUPERNODE; | ||||
if(from_supernode) { | ||||
skip_add = SN_ADD_SKIP; | ||||
sn = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), &sender, nul | ||||
l_mac, &skip_add); | ||||
if(!sn) { | ||||
traceEvent(TRACE_DEBUG, "dropped incoming data from unknown supernod | ||||
e"); | ||||
return; | ||||
} | ||||
} | ||||
/* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which | if(0 == memcmp(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE)) | |||
* IP transport version the packet arrived on. May need to UDP sockets. */ | { | |||
sender.family = AF_INET; /* UDP socket was opened PF_INET v4 */ | switch(msg_type) { | |||
sender.port = ntohs(sender_sock.sin_port); | case MSG_TYPE_PACKET: { | |||
memcpy(&(sender.addr.v4), &(sender_sock.sin_addr.s_addr), IPV4_SIZE); | /* process PACKET - most frequent so first in list. */ | |||
n2n_PACKET_t pkt; | ||||
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, pkt.srcMac, sta | ||||
mp, TIME_STAMP_ALLOW_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped PACKET due to time stam | ||||
p error"); | ||||
return; | ||||
} | ||||
} | ||||
/* The packet may not have an orig_sender socket spec. So default to last | if(!eee->last_sup) { | |||
* hop as sender. */ | // drop packets received before first registration with supe | |||
orig_sender=&sender; | rnode | |||
traceEvent(TRACE_DEBUG, "dropped PACKET recevied before firs | ||||
t registration with supernode"); | ||||
return; | ||||
} | ||||
traceEvent(TRACE_DEBUG, "### Rx N2N UDP (%d) from %s", | if(is_valid_peer_sock(&pkt.sock)) | |||
(signed int)recvlen, sock_to_cstr(sockbuf1, &sender)); | orig_sender = &(pkt.sock); | |||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | if(!from_supernode) { | |||
uint16_t checksum = 0; | /* This is a P2P packet from the peer. We purge a pending | |||
if( packet_header_decrypt (udp_buf, recvlen, (char *)eee->conf.community_nam | * registration towards the possibly nat-ted peer address as | |||
e, eee->conf.header_encryption_ctx, | we now have | |||
eee->conf.header_iv_ctx, | * a valid channel. We still use check_peer_registration_nee | |||
&stamp, &checksum) == 0) { | ded in | |||
traceEvent(TRACE_DEBUG, "readFromIPSocket failed to decrypt header."); | * handle_PACKET to double check this. | |||
return; | */ | |||
} | traceEvent(TRACE_DEBUG, "[p2p] from %s", | |||
macaddr_str(mac_buf1, pkt.srcMac)); | ||||
find_and_remove_peer(&eee->pending_peers, pkt.srcMac); | ||||
} else { | ||||
/* [PsP] : edge Peer->Supernode->edge Peer */ | ||||
traceEvent(TRACE_DEBUG, "[pSp] from %s via [%s]", | ||||
macaddr_str(mac_buf1, pkt.srcMac), | ||||
sock_to_cstr(sockbuf1, &sender)); | ||||
} | ||||
// time stamp verification follows in the packet specific section as it requ | /* Update the sender in peer table entry */ | |||
ires to determine the | check_peer_registration_needed(eee, from_supernode, via_multicas | |||
// sender from the hash list by its MAC, or the packet might be from the sup | t, | |||
ernode, this all depends | pkt.srcMac, | |||
// on packet type, path taken (via supernode) and packet structure (MAC is n | // REVISIT: also consider PORT_RE | |||
ot always in the same place) | G_COOKIEs when implemented | |||
from_supernode ? N2N_FORWARDED_RE | ||||
G_COOKIE : N2N_REGULAR_REG_COOKIE, | ||||
NULL, NULL, orig_sender); | ||||
if (checksum != pearson_hash_16 (udp_buf, recvlen)) { | handle_PACKET(eee, from_supernode, &pkt, orig_sender, udp_buf + | |||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped packet due to checksum e | idx, udp_size - idx); | |||
rror."); | break; | |||
return; | } | |||
} | ||||
} | ||||
/* hexdump(udp_buf, recvlen); */ | case MSG_TYPE_REGISTER: { | |||
/* Another edge is registering with us */ | ||||
n2n_REGISTER_t reg; | ||||
decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); | ||||
via_multicast &= is_null_mac(reg.dstMac); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, reg.srcMac, sta | ||||
mp, | ||||
via_multicast ? TIME_STA | ||||
MP_ALLOW_JITTER : TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER due to time st | ||||
amp error"); | ||||
return; | ||||
} | ||||
} | ||||
rem = recvlen; /* Counts down bytes of packet to protect against buffer overru | if(is_valid_peer_sock(®.sock)) | |||
ns. */ | orig_sender = &(reg.sock); | |||
idx = 0; /* marches through packet header as parts are decoded. */ | ||||
if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) | ||||
{ | ||||
traceEvent(TRACE_ERROR, "Failed to decode common section in N2N_UDP"); | ||||
return; /* failed to decode packet */ | ||||
} | ||||
now = time(NULL); | if(via_multicast && !memcmp(reg.srcMac, eee->device.mac_addr, N2 | |||
N_MAC_SIZE)) { | ||||
traceEvent(TRACE_DEBUG, "skipping REGISTER from self"); | ||||
break; | ||||
} | ||||
msg_type = cmn.pc; /* packet code */ | if(!via_multicast && memcmp(reg.dstMac, eee->device.mac_addr, N2 | |||
from_supernode= cmn.flags & N2N_FLAGS_FROM_SUPERNODE; | N_MAC_SIZE)) { | |||
traceEvent(TRACE_DEBUG, "skipping REGISTER for other peer"); | ||||
break; | ||||
} | ||||
if(0 == memcmp(cmn.community, eee->conf.community_name, N2N_COMMUNITY_SIZE)) { | if(!from_supernode) { | |||
switch(msg_type) { | /* This is a P2P registration from the peer. We purge a pend | |||
case MSG_TYPE_PACKET: | ing | |||
{ | * registration towards the possibly nat-ted peer address as | |||
/* process PACKET - most frequent so first in list. */ | we now have | |||
n2n_PACKET_t pkt; | * a valid channel. We still use check_peer_registration_nee | |||
ded below | ||||
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); | * to double check this. | |||
*/ | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | traceEvent(TRACE_INFO, "[p2p] Rx REGISTER from %s [%s]%s", | |||
if(!find_peer_time_stamp_and_verify (eee, from_supernode, pkt.srcMac | macaddr_str(mac_buf1, reg.srcMac), | |||
, stamp)) { | sock_to_cstr(sockbuf1, &sender), | |||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PACKET due to ti | (reg.cookie & N2N_LOCAL_REG_COOKIE) ? | |||
me stamp error."); | " (local)" : ""); | |||
return; | find_and_remove_peer(&eee->pending_peers, reg.srcMac); | |||
} | ||||
} | /* NOTE: only ACK to peers */ | |||
send_register_ack(eee, orig_sender, ®); | ||||
if(is_valid_peer_sock(&pkt.sock)) | } else { | |||
orig_sender = &(pkt.sock); | traceEvent(TRACE_INFO, "[pSp] Rx REGISTER from %s [%s] to %s | |||
via [%s]", | ||||
if(!from_supernode) { | macaddr_str(mac_buf1, reg.srcMac), sock_to_cstr(s | |||
/* This is a P2P packet from the peer. We purge a pending | ockbuf2, orig_sender), | |||
* registration towards the possibly nat-ted peer address as we now h | macaddr_str(mac_buf2, reg.dstMac), sock_to_cstr(s | |||
ave | ockbuf1, &sender)); | |||
* a valid channel. We still use check_peer_registration_needed in | } | |||
* handle_PACKET to double check this. | ||||
*/ | ||||
traceEvent(TRACE_DEBUG, "Got P2P packet"); | ||||
traceEvent(TRACE_DEBUG, "[P2P] Rx data from %s [%u B]", sock_to_cstr(sockb | ||||
uf1, &sender), recvlen); | ||||
find_and_remove_peer(&eee->pending_peers, pkt.srcMac); | ||||
} | ||||
else { | ||||
/* [PsP] : edge Peer->Supernode->edge Peer */ | ||||
traceEvent(TRACE_DEBUG, "[PsP] Rx data from %s (Via=%s) [%u B]", | ||||
sock_to_cstr(sockbuf2, orig_sender), sock_to_cstr(sockbuf1, &se | ||||
nder), recvlen); | ||||
} | ||||
handle_PACKET(eee, &cmn, &pkt, orig_sender, udp_buf+idx, recvlen-idx); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER: | ||||
{ | ||||
/* Another edge is registering with us */ | ||||
n2n_REGISTER_t reg; | ||||
int via_multicast; | ||||
decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify (eee, from_supernode, reg.srcMac | ||||
, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER due to | ||||
time stamp error."); | ||||
return; | ||||
} | ||||
} | ||||
if(is_valid_peer_sock(®.sock)) | ||||
orig_sender = &(reg.sock); | ||||
via_multicast = !memcmp(reg.dstMac, null_mac, N2N_MAC_SIZE); | ||||
if(via_multicast && !memcmp(reg.srcMac, eee->device.mac_addr, N2N_MAC_SIZ | ||||
E)) { | ||||
traceEvent(TRACE_DEBUG, "Skipping REGISTER from self"); | ||||
break; | ||||
} | ||||
if(!via_multicast && memcmp(reg.dstMac, eee->device.mac_addr, N2N_MAC_SIZ | ||||
E)) { | ||||
traceEvent(TRACE_DEBUG, "Skipping REGISTER for other peer"); | ||||
break; | ||||
} | ||||
if(!from_supernode) { | ||||
/* This is a P2P registration from the peer. We purge a pending | ||||
* registration towards the possibly nat-ted peer address as we now hav | ||||
e | ||||
* a valid channel. We still use check_peer_registration_needed below | ||||
* to double check this. | ||||
*/ | ||||
traceEvent(TRACE_DEBUG, "Got P2P register"); | ||||
traceEvent(TRACE_NORMAL, "[P2P] Rx REGISTER from %s", sock_to_cstr(sockbuf1, | ||||
&sender)); | ||||
find_and_remove_peer(&eee->pending_peers, reg.srcMac); | ||||
/* NOTE: only ACK to peers */ | ||||
send_register_ack(eee, orig_sender, ®); | ||||
} | ||||
else { | ||||
traceEvent(TRACE_NORMAL, "[PsP] Rx REGISTER src=%s dst=%s from sn=%s (edge:% | ||||
s)", | ||||
macaddr_str(mac_buf1, reg.srcMac), macaddr_str(mac_buf2, reg.dstM | ||||
ac), | ||||
sock_to_cstr(sockbuf1, &sender), sock_to_cstr(sockbuf2, orig_send | ||||
er)); | ||||
} | ||||
check_peer_registration_needed(eee, from_supernode, reg.srcMac, orig_send | ||||
er); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_ACK: | ||||
{ | ||||
/* Peer edge is acknowledging our register request */ | ||||
n2n_REGISTER_ACK_t ra; | ||||
decode_REGISTER_ACK(&ra, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify (eee, !definitely_from_supernode | ||||
, ra.srcMac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_ACK due | ||||
to time stamp error."); | ||||
return; | ||||
} | ||||
} | ||||
if(is_valid_peer_sock(&ra.sock)) | ||||
orig_sender = &(ra.sock); | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_ACK src=%s dst=%s from peer %s (%s)", | ||||
macaddr_str(mac_buf1, ra.srcMac), | ||||
macaddr_str(mac_buf2, ra.dstMac), | ||||
sock_to_cstr(sockbuf1, &sender), | ||||
sock_to_cstr(sockbuf2, orig_sender)); | ||||
peer_set_p2p_confirmed(eee, ra.srcMac, &sender, now); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_SUPER_ACK: | ||||
{ | ||||
// Indicates successful connection between the edge and SN nodes | ||||
static int bTrace = 1; | ||||
if (bTrace) | ||||
{ | ||||
traceEvent(TRACE_NORMAL, "[OK] Edge Peer <<< ================ >>> Supe | ||||
r Node"); | ||||
bTrace = 0; | ||||
} | ||||
n2n_REGISTER_SUPER_ACK_t ra; | ||||
if(eee->sn_wait) | ||||
{ | ||||
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify (eee, definitely_from_supern | ||||
ode, null_mac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped REGISTER_SUP | ||||
ER_ACK due to time stamp error."); | ||||
return; | ||||
} | ||||
} | ||||
if(is_valid_peer_sock(&ra.sock)) | ||||
orig_sender = &(ra.sock); | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK myMAC=%s [%s] (extern | ||||
al %s). Attempts %u", | ||||
macaddr_str(mac_buf1, ra.edgeMac), | ||||
sock_to_cstr(sockbuf1, &sender), | ||||
sock_to_cstr(sockbuf2, orig_sender), | ||||
(unsigned int)eee->sup_attempts); | ||||
if(memcmp(ra.edgeMac, eee->device.mac_addr, N2N_MAC_SIZE)) { | ||||
traceEvent(TRACE_INFO, "readFromIPSocket dropped REGISTER_SUPER_A | ||||
CK due to wrong addressing."); | ||||
return; | ||||
} | ||||
if(0 == memcmp(ra.cookie, eee->last_cookie, N2N_COOKIE_SIZE)) | ||||
{ | ||||
if(ra.num_sn > 0) | ||||
{ | ||||
traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK backup supe | ||||
rnode at %s", | ||||
sock_to_cstr(sockbuf1, &(ra.sn_bak))); | ||||
} | ||||
eee->last_sup = now; | ||||
eee->sn_wait=0; | ||||
eee->sup_attempts = N2N_EDGE_SUP_ATTEMPTS; /* refresh because w | ||||
e got a response */ | ||||
if(eee->cb.sn_registration_updated) | ||||
eee->cb.sn_registration_updated(eee, now, &sender); | ||||
/* NOTE: the register_interval should be chosen by the edge nod | ||||
e | ||||
* based on its NAT configuration. */ | ||||
//eee->conf.register_interval = ra.lifetime; | ||||
} | ||||
else | ||||
{ | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old | ||||
cookie."); | ||||
} | ||||
} | ||||
else | ||||
{ | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with no outstanding R | ||||
EGISTER_SUPER."); | ||||
} | ||||
break; | ||||
} case MSG_TYPE_PEER_INFO: { | ||||
n2n_PEER_INFO_t pi; | ||||
struct peer_info * scan; | ||||
decode_PEER_INFO( &pi, &cmn, udp_buf, &rem, &idx ); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | check_peer_registration_needed(eee, from_supernode, via_multicas | |||
if(!find_peer_time_stamp_and_verify (eee, definitely_from_supernode, n | t, | |||
ull_mac, stamp)) { | reg.srcMac, reg.cookie, ®.dev_ | |||
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to t | addr, (const n2n_desc_t*)®.dev_desc, orig_sender); | |||
ime stamp error."); | break; | |||
return; | } | |||
} | ||||
} | case MSG_TYPE_REGISTER_ACK: { | |||
/* Peer edge is acknowledging our register request */ | ||||
n2n_REGISTER_ACK_t ra; | ||||
decode_REGISTER_ACK(&ra, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, ra.srcMac, stam | ||||
p, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_ACK due to tim | ||||
e stamp error"); | ||||
return; | ||||
} | ||||
} | ||||
if(is_valid_peer_sock(&ra.sock)) | ||||
orig_sender = &(ra.sock); | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_ACK from %s [%s] to %s via [ | ||||
%s]%s", | ||||
macaddr_str(mac_buf1, ra.srcMac), | ||||
sock_to_cstr(sockbuf2, orig_sender), | ||||
macaddr_str(mac_buf2, ra.dstMac), | ||||
sock_to_cstr(sockbuf1, &sender), | ||||
(ra.cookie & N2N_LOCAL_REG_COOKIE) ? " (local)" : ""); | ||||
peer_set_p2p_confirmed(eee, ra.srcMac, | ||||
ra.cookie, | ||||
&sender, now); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_SUPER_ACK: { | ||||
in_addr_t net; | ||||
char * ip_str = NULL; | ||||
n2n_REGISTER_SUPER_ACK_t ra; | ||||
uint8_t tmpbuf[REG_SUPER_ACK_PAYLOAD_SPACE]; | ||||
char ip_tmp[N2N_EDGE_SN_HOST_SIZE]; | ||||
n2n_REGISTER_SUPER_ACK_payload_t *payload; | ||||
int i; | ||||
int skip_add; | ||||
if(!(eee->sn_wait)) { | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_ACK with no outst | ||||
anding REGISTER_SUPER"); | ||||
return; | ||||
} | ||||
memset(&ra, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); | ||||
decode_REGISTER_SUPER_ACK(&ra, &cmn, udp_buf, &rem, &idx, tmpbuf | ||||
); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, ra.srcMac, stam | ||||
p, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER_ACK due | ||||
to time stamp error"); | ||||
return; | ||||
} | ||||
} | ||||
// hash check (user/pw auth only) | ||||
if(eee->conf.shared_secret) { | ||||
speck_128_encrypt(hash_buf, (speck_context_t*)eee->conf.shar | ||||
ed_secret_ctx); | ||||
if(memcmp(hash_buf, udp_buf + udp_size - N2N_REG_SUP_HASH_CH | ||||
ECK_LEN /* length is has already been checked */, N2N_REG_SUP_HASH_CHECK_LEN)) { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong | ||||
hash"); | ||||
return; | ||||
} | ||||
} | ||||
if(ra.cookie != eee->curr_sn->last_cookie) { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or | ||||
old cookie"); | ||||
return; | ||||
} | ||||
if(handle_remote_auth(eee, sn, &(ra.auth))) { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or | ||||
old response to challenge"); | ||||
if(eee->conf.shared_secret) { | ||||
traceEvent(TRACE_NORMAL, "Rx REGISTER_SUPER_ACK with wro | ||||
ng or old response to challenge, maybe indicating wrong federation public key (- | ||||
P)"); | ||||
} | ||||
return; | ||||
} | ||||
if(is_valid_peer_sock(&ra.sock)) | ||||
orig_sender = &(ra.sock); | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK from %s [%s] (exte | ||||
rnal %s) with %u attempts left", | ||||
macaddr_str(mac_buf1, ra.srcMac), | ||||
sock_to_cstr(sockbuf1, &sender), | ||||
sock_to_cstr(sockbuf2, orig_sender), | ||||
(unsigned int)eee->sup_attempts); | ||||
if(is_null_mac(eee->curr_sn->mac_addr)) { | ||||
HASH_DEL(eee->conf.supernodes, eee->curr_sn); | ||||
memcpy(&eee->curr_sn->mac_addr, ra.srcMac, N2N_MAC_SIZE); | ||||
HASH_ADD_PEER(eee->conf.supernodes, eee->curr_sn); | ||||
} | ||||
payload = (n2n_REGISTER_SUPER_ACK_payload_t*)tmpbuf; | ||||
// from here on, 'sn' gets used differently | ||||
for(i = 0; i < ra.num_sn; i++) { | ||||
skip_add = SN_ADD; | ||||
sn = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes), | ||||
&(payload->sock), payload->mac, &skip_add); | ||||
if(skip_add == SN_ADD_ADDED) { | ||||
sn->ip_addr = calloc(1, N2N_EDGE_SN_HOST_SIZE); | ||||
if(sn->ip_addr != NULL) { | ||||
inet_ntop(payload->sock.family, | ||||
(payload->sock.family == AF_INET) ? (void* | ||||
)&(payload->sock.addr.v4) : (void*)&(payload->sock.addr.v6), | ||||
sn->ip_addr, N2N_EDGE_SN_HOST_SIZE - 1); | ||||
sprintf(ip_tmp, "%s:%u", (char*)sn->ip_addr, (uint16 | ||||
_t)(payload->sock.port)); | ||||
memcpy(sn->ip_addr, ip_tmp, sizeof(ip_tmp)); | ||||
} | ||||
sn_selection_criterion_default(&(sn->selection_criterion | ||||
)); | ||||
sn->last_seen = 0; /* as opposed to payload handling in | ||||
supernode */ | ||||
traceEvent(TRACE_NORMAL, "supernode '%s' added to the li | ||||
st of supernodes.", sn->ip_addr); | ||||
} | ||||
// shift to next payload entry | ||||
payload++; | ||||
} | ||||
if(eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_SN_ASSIGN) { | ||||
if((ra.dev_addr.net_addr != 0) && (ra.dev_addr.net_bitlen != | ||||
0)) { | ||||
net = htonl(ra.dev_addr.net_addr); | ||||
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NUL | ||||
L) { | ||||
strncpy(eee->tuntap_priv_conf.ip_addr, ip_str, N2N_N | ||||
ETMASK_STR_SIZE); | ||||
eee->tuntap_priv_conf.ip_addr[N2N_NETMASK_STR_SIZE - | ||||
1] = '\0'; | ||||
} | ||||
net = htonl(bitlen2mask(ra.dev_addr.net_bitlen)); | ||||
if((ip_str = inet_ntoa(*(struct in_addr *) &net)) != NUL | ||||
L) { | ||||
strncpy(eee->tuntap_priv_conf.netmask, ip_str, N2N_N | ||||
ETMASK_STR_SIZE); | ||||
eee->tuntap_priv_conf.netmask[N2N_NETMASK_STR_SIZE - | ||||
1] = '\0'; | ||||
} | ||||
} | ||||
} | ||||
eee->sn_wait = 0; | ||||
reset_sup_attempts(eee); /* refresh because we got a response */ | ||||
// update last_sup only on 'real' REGISTER_SUPER_ACKs, not on bo | ||||
otstrap ones (own MAC address | ||||
// still null_mac) this allows reliable in/out PACKET drop if no | ||||
t really registered with a supernode yet | ||||
if(!is_null_mac(eee->device.mac_addr)) { | ||||
if(!eee->last_sup) { | ||||
// indicates first successful connection between the edg | ||||
e and a supernode | ||||
traceEvent(TRACE_NORMAL, "[OK] edge <<< ================ | ||||
>>> supernode"); | ||||
// send gratuitous ARP only upon first registration with | ||||
supernode | ||||
send_grat_arps(eee); | ||||
} | ||||
eee->last_sup = now; | ||||
} | ||||
// NOTE: the register_interval should be chosen by the edge node | ||||
based on its NAT configuration. | ||||
// eee->conf.register_interval = ra.lifetime; | ||||
if(eee->cb.sn_registration_updated && !is_null_mac(eee->device.m | ||||
ac_addr)) | ||||
eee->cb.sn_registration_updated(eee, now, &sender); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_SUPER_NAK: { | ||||
n2n_REGISTER_SUPER_NAK_t nak; | ||||
if(!(eee->sn_wait)) { | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_NAK with no outst | ||||
anding REGISTER_SUPER"); | ||||
return; | ||||
} | ||||
memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t)); | ||||
decode_REGISTER_SUPER_NAK(&nak, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, nak.srcMac, sta | ||||
mp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER_NAK due | ||||
to time stamp error"); | ||||
return; | ||||
} | ||||
} | ||||
if(nak.cookie != eee->curr_sn->last_cookie) { | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER_NAK with wrong or | ||||
old cookie"); | ||||
return; | ||||
} | ||||
// REVISIT: authenticate the NAK packet really originating from | ||||
the supernode along the auth token. | ||||
// this must follow a different scheme because it needs | ||||
to prove authenticity although the | ||||
// edge-provided credentials are wrong | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_NAK"); | ||||
if((memcmp(nak.srcMac, eee->device.mac_addr, sizeof(n2n_mac_t))) | ||||
== 0) { | ||||
if(eee->conf.shared_secret) { | ||||
traceEvent(TRACE_ERROR, "authentication error, username | ||||
or password not recognized by supernode"); | ||||
} else { | ||||
traceEvent(TRACE_ERROR, "authentication error, MAC or IP | ||||
address already in use or not released yet by supernode"); | ||||
} | ||||
// REVISIT: the following portion is too harsh, repeated err | ||||
or warning should be sufficient until it eventually is resolved, | ||||
// preventing de-auth attacks | ||||
/* exit(1); this is too harsh, repeated error warning should | ||||
be sufficient until it eventually is resolved, preventing de-auth attacks | ||||
} else { | ||||
HASH_FIND_PEER(eee->known_peers, nak.srcMac, peer); | ||||
if(peer != NULL) { | ||||
HASH_DEL(eee->known_peers, peer); | ||||
} | ||||
HASH_FIND_PEER(eee->pending_peers, nak.srcMac, scan); | ||||
if(scan != NULL) { | ||||
HASH_DEL(eee->pending_peers, scan); | ||||
} */ | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_PEER_INFO: { | ||||
n2n_PEER_INFO_t pi; | ||||
struct peer_info * scan; | ||||
int skip_add; | ||||
decode_PEER_INFO(&pi, &cmn, udp_buf, &rem, &idx); | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, null_mac, stamp | ||||
, TIME_STAMP_ALLOW_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped PEER_INFO due to time s | ||||
tamp error"); | ||||
return; | ||||
} | ||||
} | ||||
if((cmn.flags & N2N_FLAGS_SOCKET) && !is_valid_peer_sock(&pi.soc | ||||
k)) { | ||||
traceEvent(TRACE_DEBUG, "skip invalid PEER_INFO from %s [%s] | ||||
", | ||||
macaddr_str(mac_buf1, pi.mac), | ||||
sock_to_cstr(sockbuf1, &pi.sock)); | ||||
break; | ||||
} | ||||
if(is_null_mac(pi.mac)) { | ||||
// PONG - answer to PING (QUERY_PEER_INFO with null mac) | ||||
skip_add = SN_ADD_SKIP; | ||||
scan = add_sn_to_list_by_mac_or_sock(&(eee->conf.supernodes) | ||||
, &sender, pi.srcMac, &skip_add); | ||||
if(scan != NULL) { | ||||
eee->sn_pong = 1; | ||||
scan->last_seen = now; | ||||
scan->uptime = pi.uptime; | ||||
memcpy(scan->version, pi.version, sizeof(n2n_version_t)) | ||||
; | ||||
/* The data type depends on the actual selection strateg | ||||
y that has been chosen. */ | ||||
SN_SELECTION_CRITERION_DATA_TYPE sn_sel_tmp = pi.load; | ||||
sn_selection_criterion_calculate(eee, scan, &sn_sel_tmp) | ||||
; | ||||
traceEvent(TRACE_INFO, "Rx PONG from supernode %s", | ||||
macaddr_str(mac_buf1, pi.srcMac)); | ||||
if(!is_valid_peer_sock(&pi.sock)) { | break; | |||
traceEvent(TRACE_DEBUG, "Skip invalid PEER_INFO %s [%s]", | } | |||
sock_to_cstr(sockbuf1, &pi.sock), | } else { | |||
macaddr_str(mac_buf1, pi.mac) ); | // regular PEER_INFO | |||
break; | HASH_FIND_PEER(eee->pending_peers, pi.mac, scan); | |||
} | if(!scan) | |||
// just in case the remote edge has been upgraded by the | ||||
HASH_FIND_PEER(eee->pending_peers, pi.mac, scan); | REG/ACK mechanism in the meantime | |||
if(scan) { | HASH_FIND_PEER(eee->known_peers, pi.mac, scan); | |||
scan->sock = pi.sock; | ||||
traceEvent(TRACE_INFO, "Rx PEER_INFO for %s: is at %s", | if(scan) { | |||
macaddr_str(mac_buf1, pi.mac), | scan->sock = pi.sock; | |||
sock_to_cstr(sockbuf1, &pi.sock)); | ||||
send_register(eee, &scan->sock, scan->mac_addr); | traceEvent(TRACE_INFO, "Rx PEER_INFO %s can be found at | |||
} else { | [%s]", | |||
traceEvent(TRACE_INFO, "Rx PEER_INFO unknown peer %s", | macaddr_str(mac_buf1, pi.mac), | |||
macaddr_str(mac_buf1, pi.mac) ); | sock_to_cstr(sockbuf1, &pi.sock)); | |||
} | ||||
if(cmn.flags & N2N_FLAGS_SOCKET) { | ||||
break; | scan->preferred_sock = pi.preferred_sock; | |||
} | send_register(eee, &scan->preferred_sock, scan->mac_ | |||
default: | addr, N2N_LOCAL_REG_COOKIE); | |||
/* Not a known message type */ | ||||
traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored", (sig | traceEvent(TRACE_INFO, "%s has preferred local socke | |||
ned int)msg_type); | t at [%s]", | |||
return; | macaddr_str(mac_buf1, pi.mac), | |||
} /* switch(msg_type) */ | sock_to_cstr(sockbuf1, &pi.preferred_sock | |||
} else if(from_supernode) /* if(community match) */ | )); | |||
traceEvent(TRACE_WARNING, "Received packet with unknown community"); | } | |||
else | ||||
traceEvent(TRACE_INFO, "Ignoring packet with unknown community"); | send_register(eee, &scan->sock, scan->mac_addr, N2N_REGU | |||
LAR_REG_COOKIE); | ||||
} else { | ||||
traceEvent(TRACE_INFO, "Rx PEER_INFO unknown peer %s", | ||||
macaddr_str(mac_buf1, pi.mac)); | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_RE_REGISTER_SUPER: { | ||||
if(eee->conf.header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_peer_time_stamp_and_verify(eee, sn, null_mac, stamp | ||||
, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped RE_REGISTER due to time | ||||
stamp error"); | ||||
return; | ||||
} | ||||
} | ||||
// only accept in user/pw mode for immediate re-registration bec | ||||
ause the new | ||||
// key is required for continous traffic flow, in other modes ed | ||||
ge will realize | ||||
// changes with regular recurring REGISTER_SUPER | ||||
if(!eee->conf.shared_secret) { | ||||
traceEvent(TRACE_DEBUG, "dropped RE_REGISTER_SUPER as not in | ||||
user/pw auth mode"); | ||||
return; | ||||
} | ||||
traceEvent(TRACE_INFO, "Rx RE_REGISTER_SUPER"); | ||||
eee->sn_wait = 2; /* immediately */ | ||||
break; | ||||
} | ||||
default: | ||||
/* Not a known message type */ | ||||
traceEvent(TRACE_INFO, "unable to handle packet type %d: ignored | ||||
", (signed int)msg_type); | ||||
return; | ||||
} /* switch(msg_type) */ | ||||
} else if(from_supernode) /* if(community match) */ | ||||
traceEvent(TRACE_INFO, "received packet with unknown community"); | ||||
else | ||||
traceEvent(TRACE_INFO, "ignoring packet with unknown community"); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void print_edge_stats(const n2n_edge_t *eee) { | int fetch_and_eventually_process_data (n2n_edge_t *eee, SOCKET sock, | |||
const struct n2n_edge_stats *s = &eee->stats; | uint8_t *pktbuf, uint16_t *expected, uint | |||
16_t *position, | ||||
traceEvent(TRACE_NORMAL, "**********************************"); | time_t now) { | |||
traceEvent(TRACE_NORMAL, "Packet stats:"); | ||||
traceEvent(TRACE_NORMAL, " TX P2P: %u pkts", s->tx_p2p); | ssize_t bread = 0; | |||
traceEvent(TRACE_NORMAL, " RX P2P: %u pkts", s->rx_p2p); | ||||
traceEvent(TRACE_NORMAL, " TX Supernode: %u pkts (%u broadcast)", s->tx_sup | if((!eee->conf.connect_tcp) | |||
, s->tx_sup_broadcast); | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
traceEvent(TRACE_NORMAL, " RX Supernode: %u pkts (%u broadcast)", s->rx_sup | || (sock == eee->udp_multicast_sock) | |||
, s->rx_sup_broadcast); | #endif | |||
traceEvent(TRACE_NORMAL, "**********************************"); | ) { | |||
// udp | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
i = sizeof(sender_sock); | ||||
bread = recvfrom(sock, pktbuf, N2N_PKT_BUF_SIZE, 0 /*flags*/, | ||||
(struct sockaddr *)&sender_sock, (socklen_t *)&i); | ||||
if((bread < 0) | ||||
#ifdef WIN32 | ||||
&& (WSAGetLastError() != WSAECONNRESET) | ||||
#endif | ||||
) { | ||||
/* For UDP bread of zero just means no data (unlike TCP). */ | ||||
/* The fd is no good now. Maybe we lost our interface. */ | ||||
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, | ||||
errno, strerror(errno)); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); | ||||
#endif | ||||
return -1; | ||||
} | ||||
// we have a datagram to process... | ||||
if(bread > 0) { | ||||
// ...and the datagram has data (not just a header) | ||||
process_udp(eee, &sender_sock, sock, pktbuf, bread, now); | ||||
} | ||||
} else { | ||||
// tcp | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
i = sizeof(sender_sock); | ||||
bread = recvfrom(sock, | ||||
pktbuf + *position, *expected - *position, 0 /*flags*/, | ||||
(struct sockaddr *)&sender_sock, (socklen_t *)&i); | ||||
if((bread <= 0) && (errno)) { | ||||
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)", bread, | ||||
errno, strerror(errno)); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); | ||||
#endif | ||||
supernode_disconnect(eee); | ||||
eee->sn_wait = 1; | ||||
traceEvent(TRACE_DEBUG, "disconnected supernode due to connection er | ||||
ror"); | ||||
goto tcp_done; | ||||
} | ||||
*position = *position + bread; | ||||
if(*position == *expected) { | ||||
if(*position == sizeof(uint16_t)) { | ||||
// the prepended length has been read, preparing for the packet | ||||
*expected = *expected + be16toh(*(uint16_t*)(pktbuf)); | ||||
if(*expected > N2N_PKT_BUF_SIZE) { | ||||
supernode_disconnect(eee); | ||||
eee->sn_wait = 1; | ||||
traceEvent(TRACE_DEBUG, "disconnected supernode due to too m | ||||
any bytes expected"); | ||||
goto tcp_done; | ||||
} | ||||
} else { | ||||
// full packet read, handle it | ||||
process_udp(eee, (struct sockaddr_in*)&sender_sock, sock, | ||||
pktbuf + sizeof(uint16_t), *position - sizeof(u | ||||
int16_t), now); | ||||
// reset, await new prepended length | ||||
*expected = sizeof(uint16_t); | ||||
*position = 0; | ||||
} | ||||
} | ||||
} | ||||
tcp_done: | ||||
; | ||||
return 0; | ||||
} | ||||
void print_edge_stats (const n2n_edge_t *eee) { | ||||
const struct n2n_edge_stats *s = &eee->stats; | ||||
traceEvent(TRACE_NORMAL, "**********************************"); | ||||
traceEvent(TRACE_NORMAL, "Packet stats:"); | ||||
traceEvent(TRACE_NORMAL, " TX P2P: %u pkts", s->tx_p2p); | ||||
traceEvent(TRACE_NORMAL, " RX P2P: %u pkts", s->rx_p2p); | ||||
traceEvent(TRACE_NORMAL, " TX Supernode: %u pkts (%u broadcast)", s->tx | ||||
_sup, s->tx_sup_broadcast); | ||||
traceEvent(TRACE_NORMAL, " RX Supernode: %u pkts (%u broadcast)", s->rx | ||||
_sup, s->rx_sup_broadcast); | ||||
traceEvent(TRACE_NORMAL, "**********************************"); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
int run_edge_loop(n2n_edge_t * eee, int *keep_running) { | int run_edge_loop (n2n_edge_t *eee) { | |||
size_t numPurged; | ||||
time_t lastIfaceCheck=0; | size_t numPurged; | |||
time_t lastTransop=0; | time_t lastIfaceCheck = 0; | |||
time_t last_purge_known = 0; | time_t lastTransop = 0; | |||
time_t last_purge_pending = 0; | time_t last_purge_known = 0; | |||
time_t last_purge_pending = 0; | ||||
uint16_t expected = sizeof(uint16_t); | ||||
uint16_t position = 0; | ||||
uint8_t pktbuf[N2N_PKT_BUF_SIZE + sizeof(uint16_t)]; /* buffer + prepended | ||||
buffer length in case of tcp */ | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
struct tunread_arg arg; | struct tunread_arg arg; | |||
arg.eee = eee; | arg.eee = eee; | |||
arg.keep_running = keep_running; | HANDLE tun_read_thread = startTunReadThread(&arg); | |||
HANDLE tun_read_thread = startTunReadThread(&arg); | #endif | |||
#endif | ||||
*eee->keep_running = 1; | ||||
*keep_running = 1; | update_supernode_reg(eee, time(NULL)); | |||
update_supernode_reg(eee, time(NULL)); | ||||
/* Main loop | ||||
/* Main loop | * | |||
* | * select() is used to wait for input on either the TAP fd or the UDP/TCP | |||
* select() is used to wait for input on either the TAP fd or the UDP/TCP | * socket. When input is present the data is read and processed by either | |||
* socket. When input is present the data is read and processed by either | * readFromIPSocket() or edge_read_from_tap() | |||
* readFromIPSocket() or edge_read_from_tap() | */ | |||
*/ | ||||
while(*eee->keep_running) { | ||||
while(*keep_running) { | ||||
int rc, max_sock = 0; | int rc, max_sock = 0; | |||
fd_set socket_mask; | fd_set socket_mask; | |||
struct timeval wait_time; | struct timeval wait_time; | |||
time_t nowTime; | time_t now; | |||
FD_ZERO(&socket_mask); | FD_ZERO(&socket_mask); | |||
FD_SET(eee->udp_sock, &socket_mask); | ||||
FD_SET(eee->udp_mgmt_sock, &socket_mask); | ||||
max_sock = max(eee->udp_sock, eee->udp_mgmt_sock); | ||||
FD_SET(eee->udp_mgmt_sock, &socket_mask); | ||||
max_sock = eee->udp_mgmt_sock; | ||||
if(eee->sock >= 0) { | ||||
FD_SET(eee->sock, &socket_mask); | ||||
max_sock = max(eee->sock, eee->udp_mgmt_sock); | ||||
} | ||||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
FD_SET(eee->udp_multicast_sock, &socket_mask); | if((eee->conf.allow_p2p) | |||
max_sock = max(eee->udp_sock, eee->udp_multicast_sock); | && (eee->conf.preferred_sock.family == (uint8_t)AF_INVALID)) { | |||
FD_SET(eee->udp_multicast_sock, &socket_mask); | ||||
max_sock = max(eee->sock, eee->udp_multicast_sock); | ||||
} | ||||
#endif | #endif | |||
#ifndef WIN32 | #ifndef WIN32 | |||
FD_SET(eee->device.fd, &socket_mask); | FD_SET(eee->device.fd, &socket_mask); | |||
max_sock = max(max_sock, eee->device.fd); | max_sock = max(max_sock, eee->device.fd); | |||
#endif | #endif | |||
wait_time.tv_sec = SOCKET_TIMEOUT_INTERVAL_SECS; wait_time.tv_usec = 0; | wait_time.tv_sec = (eee->sn_wait) ? (SOCKET_TIMEOUT_INTERVAL_SECS / 10 + | |||
1) : (SOCKET_TIMEOUT_INTERVAL_SECS); | ||||
wait_time.tv_usec = 0; | ||||
rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time); | ||||
now = time(NULL); | ||||
rc = select(max_sock+1, &socket_mask, NULL, NULL, &wait_time); | // make sure ciphers are updated before the packet is treated | |||
nowTime=time(NULL); | if((now - lastTransop) > TRANSOP_TICK_INTERVAL) { | |||
lastTransop = now; | ||||
/* Make sure ciphers are updated before the packet is treated. */ | eee->transop.tick(&eee->transop, now); | |||
if((nowTime - lastTransop) > TRANSOP_TICK_INTERVAL) { | } | |||
lastTransop = nowTime; | ||||
eee->transop.tick(&eee->transop, nowTime); | if(rc > 0) { | |||
} | // any or all of the FDs could have input; check them all | |||
if(rc > 0) { | // external | |||
/* Any or all of the FDs could have input; check them all. */ | if(FD_ISSET(eee->sock, &socket_mask)) { | |||
if(0 != fetch_and_eventually_process_data(eee, eee->sock, | ||||
pktbuf, &expected, &po | ||||
sition, | ||||
now)) { | ||||
*eee->keep_running = 0; | ||||
break; | ||||
} | ||||
if(eee->conf.connect_tcp) { | ||||
if((expected >= N2N_PKT_BUF_SIZE) || (position >= N2N_PKT_BU | ||||
F_SIZE)) { | ||||
// something went wrong, possibly even before | ||||
// e.g. connection failure/closure in the middle of tran | ||||
smission (between len & data) | ||||
supernode_disconnect(eee); | ||||
eee->sn_wait = 1; | ||||
if(FD_ISSET(eee->udp_sock, &socket_mask)) { | expected = sizeof(uint16_t); | |||
/* Read a cooked socket from the internet socket (unicast). Writes on the | position = 0; | |||
TAP | } | |||
* socket. */ | } | |||
readFromIPSocket(eee, eee->udp_sock); | } | |||
} | ||||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
if(FD_ISSET(eee->udp_multicast_sock, &socket_mask)) { | if(FD_ISSET(eee->udp_multicast_sock, &socket_mask)) { | |||
/* Read a cooked socket from the internet socket (multicast). Writes on t | if(0 != fetch_and_eventually_process_data(eee, eee->udp_multicas | |||
he TAP | t_sock, | |||
* socket. */ | pktbuf, &expected, &po | |||
traceEvent(TRACE_DEBUG, "Received packet from multicast socket"); | sition, | |||
readFromIPSocket(eee, eee->udp_multicast_sock); | now)) { | |||
} | *eee->keep_running = 0; | |||
#endif | break; | |||
} | ||||
if(FD_ISSET(eee->udp_mgmt_sock, &socket_mask)) { | } | |||
/* Read a cooked socket from the internet socket. Writes on the TAP | #endif | |||
* socket. */ | ||||
readFromMgmtSocket(eee, keep_running); | if(FD_ISSET(eee->udp_mgmt_sock, &socket_mask)) { | |||
// read from the management port socket | ||||
if(!(*keep_running)) | readFromMgmtSocket(eee); | |||
break; | ||||
} | if(!(*eee->keep_running)) | |||
break; | ||||
} | ||||
#ifndef WIN32 | #ifndef WIN32 | |||
if(FD_ISSET(eee->device.fd, &socket_mask)) { | if(FD_ISSET(eee->device.fd, &socket_mask)) { | |||
/* Read an ethernet frame from the TAP socket. Write on the IP | // read an ethernet frame from the TAP socket; write on the IP s | |||
* socket. */ | ocket | |||
edge_read_from_tap(eee); | edge_read_from_tap(eee); | |||
} | } | |||
#endif | #endif | |||
} | } | |||
/* Finished processing select data. */ | // finished processing select data | |||
update_supernode_reg(eee, nowTime); | update_supernode_reg(eee, now); | |||
numPurged = purge_expired_registrations(&eee->known_peers, &last_purge_know | numPurged = 0; | |||
n); | // keep, i.e. do not purge, the known peers while no supernode supernode | |||
numPurged += purge_expired_registrations(&eee->pending_peers, &last_purge_pe | connection | |||
nding); | if(!eee->sn_wait) | |||
numPurged = purge_expired_nodes(&eee->known_peers, | ||||
eee->sock, NULL, | ||||
&last_purge_known, | ||||
PURGE_REGISTRATION_FREQUENCY, REGIST | ||||
RATION_TIMEOUT); | ||||
numPurged += purge_expired_nodes(&eee->pending_peers, | ||||
eee->sock, NULL, | ||||
&last_purge_pending, | ||||
PURGE_REGISTRATION_FREQUENCY, REGISTRAT | ||||
ION_TIMEOUT); | ||||
if(numPurged > 0) { | ||||
traceEvent(TRACE_INFO, "%u peers removed. now: pending=%u, operation | ||||
al=%u", | ||||
numPurged, | ||||
HASH_COUNT(eee->pending_peers), | ||||
HASH_COUNT(eee->known_peers)); | ||||
} | ||||
if(numPurged > 0) { | if((eee->conf.tuntap_ip_mode == TUNTAP_IP_MODE_DHCP) && | |||
traceEvent(TRACE_INFO, "%u peers removed. now: pending=%u, operational=%u" | ((now - lastIfaceCheck) > IFACE_UPDATE_INTERVAL)) { | |||
, | uint32_t old_ip = eee->device.ip_addr; | |||
numPurged, | ||||
HASH_COUNT(eee->pending_peers), | traceEvent(TRACE_NORMAL, "re-checking dynamic IP address"); | |||
HASH_COUNT(eee->known_peers)); | tuntap_get_address(&(eee->device)); | |||
} | lastIfaceCheck = now; | |||
if(eee->conf.dyn_ip_mode && | if((old_ip != eee->device.ip_addr) && eee->cb.ip_address_changed) | |||
((nowTime - lastIfaceCheck) > IFACE_UPDATE_INTERVAL)) { | eee->cb.ip_address_changed(eee, old_ip, eee->device.ip_addr); | |||
uint32_t old_ip = eee->device.ip_addr; | } | |||
traceEvent(TRACE_NORMAL, "Re-checking dynamic IP address."); | sort_supernodes(eee, now); | |||
tuntap_get_address(&(eee->device)); | ||||
lastIfaceCheck = nowTime; | ||||
if((old_ip != eee->device.ip_addr) && eee->cb.ip_address_changed) | eee->resolution_request = resolve_check(eee->resolve_parameter, eee->res | |||
eee->cb.ip_address_changed(eee, old_ip, eee->device.ip_addr); | olution_request, now); | |||
} | ||||
if(eee->cb.main_loop_period) | ||||
eee->cb.main_loop_period(eee, now); | ||||
if (eee->cb.main_loop_period) | } /* while */ | |||
eee->cb.main_loop_period(eee, nowTime); | ||||
} /* while */ | send_unregister_super(eee); | |||
#ifdef WIN32 | #ifdef WIN32 | |||
WaitForSingleObject(tun_read_thread, INFINITE); | WaitForSingleObject(tun_read_thread, INFINITE); | |||
#endif | #endif | |||
send_deregister(eee, &(eee->supernode)); | closesocket(eee->sock); | |||
closesocket(eee->udp_sock); | return(0); | |||
return(0); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
/** Deinitialise the edge and deallocate any owned memory. */ | /** Deinitialise the edge and deallocate any owned memory. */ | |||
void edge_term(n2n_edge_t * eee) { | void edge_term (n2n_edge_t * eee) { | |||
if(eee->udp_sock >= 0) | ||||
closesocket(eee->udp_sock); | resolve_cancel_thread(eee->resolve_parameter); | |||
if(eee->udp_mgmt_sock >= 0) | if(eee->sock >= 0) | |||
closesocket(eee->udp_mgmt_sock); | closesocket(eee->sock); | |||
if(eee->udp_mgmt_sock >= 0) | ||||
closesocket(eee->udp_mgmt_sock); | ||||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
if(eee->udp_multicast_sock >= 0) | if(eee->udp_multicast_sock >= 0) | |||
closesocket(eee->udp_multicast_sock); | closesocket(eee->udp_multicast_sock); | |||
#endif | #endif | |||
clear_peer_list(&eee->pending_peers); | clear_peer_list(&eee->pending_peers); | |||
clear_peer_list(&eee->known_peers); | clear_peer_list(&eee->known_peers); | |||
eee->transop.deinit(&eee->transop); | ||||
eee->transop.deinit(&eee->transop); | edge_cleanup_routes(eee); | |||
edge_cleanup_routes(eee); | destroy_network_traffic_filter(eee->network_traffic_filter); | |||
closeTraceFile(); | closeTraceFile(); | |||
free(eee); | free(eee); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static int edge_init_sockets(n2n_edge_t *eee, int udp_local_port, int mgmt_port, | static int edge_init_sockets (n2n_edge_t *eee) { | |||
uint8_t tos) { | ||||
int sockopt; | ||||
if(udp_local_port > 0) | ||||
traceEvent(TRACE_NORMAL, "Binding to local port %d", udp_local_port); | ||||
eee->udp_sock = open_socket(udp_local_port, 1 /* bind ANY */); | ||||
if(eee->udp_sock < 0) { | ||||
traceEvent(TRACE_ERROR, "Failed to bind main UDP port %u", udp_local_port); | ||||
return(-1); | ||||
} | ||||
if(tos) { | if(eee->udp_mgmt_sock >= 0) | |||
/* https://www.tucny.com/Home/dscp-tos */ | closesocket(eee->udp_mgmt_sock); | |||
sockopt = tos; | ||||
if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_TOS, (char *)&sockopt, sizeof(so | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
ckopt)) == 0) | if(eee->udp_multicast_sock >= 0) | |||
traceEvent(TRACE_NORMAL, "TOS set to 0x%x", tos); | closesocket(eee->udp_multicast_sock); | |||
else | ||||
traceEvent(TRACE_ERROR, "Could not set TOS 0x%x[%d]: %s", tos, errno, stre | ||||
rror(errno)); | ||||
} | ||||
#ifdef IP_PMTUDISC_DO | ||||
sockopt = (eee->conf.disable_pmtu_discovery) ? IP_PMTUDISC_DONT : IP_PMTUDISC_ | ||||
DO; | ||||
if(setsockopt(eee->udp_sock, IPPROTO_IP, IP_MTU_DISCOVER, &sockopt, sizeof(soc | ||||
kopt)) < 0) | ||||
traceEvent(TRACE_WARNING, "Could not %s PMTU discovery[%d]: %s", | ||||
(eee->conf.disable_pmtu_discovery) ? "disable" : "enable", errno, | ||||
strerror(errno)); | ||||
else | ||||
traceEvent(TRACE_DEBUG, "PMTU discovery %s", (eee->conf.disable_pmtu_discove | ||||
ry) ? "disabled" : "enabled"); | ||||
#endif | #endif | |||
eee->udp_mgmt_sock = open_socket(mgmt_port, 0 /* bind LOOPBACK */); | eee->udp_mgmt_sock = open_socket(eee->conf.mgmt_port, INADDR_LOOPBACK, 0 /* | |||
if(eee->udp_mgmt_sock < 0) { | UDP */); | |||
traceEvent(TRACE_ERROR, "Failed to bind management UDP port %u", mgmt_port); | if(eee->udp_mgmt_sock < 0) { | |||
return(-2); | traceEvent(TRACE_ERROR, "failed to bind management UDP port %u", eee->co | |||
} | nf.mgmt_port); | |||
return(-2); | ||||
} | ||||
#ifndef SKIP_MULTICAST_PEERS_DISCOVERY | #ifndef SKIP_MULTICAST_PEERS_DISCOVERY | |||
/* Populate the multicast group for local edge */ | /* Populate the multicast group for local edge */ | |||
eee->multicast_peer.family = AF_INET; | eee->multicast_peer.family = AF_INET; | |||
eee->multicast_peer.port = N2N_MULTICAST_PORT; | eee->multicast_peer.port = N2N_MULTICAST_PORT; | |||
eee->multicast_peer.addr.v4[0] = 224; /* N2N_MULTICAST_GROUP */ | eee->multicast_peer.addr.v4[0] = 224; /* N2N_MULTICAST_GROUP */ | |||
eee->multicast_peer.addr.v4[1] = 0; | eee->multicast_peer.addr.v4[1] = 0; | |||
eee->multicast_peer.addr.v4[2] = 0; | eee->multicast_peer.addr.v4[2] = 0; | |||
eee->multicast_peer.addr.v4[3] = 68; | eee->multicast_peer.addr.v4[3] = 68; | |||
eee->udp_multicast_sock = open_socket(N2N_MULTICAST_PORT, 1 /* bind ANY */); | eee->udp_multicast_sock = open_socket(N2N_MULTICAST_PORT, INADDR_ANY, 0 /* U | |||
if(eee->udp_multicast_sock < 0) | DP */); | |||
return(-3); | if(eee->udp_multicast_sock < 0) | |||
else { | return(-3); | |||
u_int enable_reuse = 1; | else { | |||
u_int enable_reuse = 1; | ||||
/* allow multiple sockets to use the same PORT number */ | /* allow multiple sockets to use the same PORT number */ | |||
setsockopt(eee->udp_multicast_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&enabl | setsockopt(eee->udp_multicast_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&e | |||
e_reuse, sizeof(enable_reuse)); | nable_reuse, sizeof(enable_reuse)); | |||
#ifdef SO_REUSEPORT /* no SO_REUSEPORT in Windows / old linux versions */ | #ifdef SO_REUSEPORT /* no SO_REUSEPORT in Windows / old linux versions */ | |||
setsockopt(eee->udp_multicast_sock, SOL_SOCKET, SO_REUSEPORT, &enable_reuse, sizeof(enable_reuse)); | setsockopt(eee->udp_multicast_sock, SOL_SOCKET, SO_REUSEPORT, &enable_re use, sizeof(enable_reuse)); | |||
#endif | #endif | |||
} | } | |||
#endif | #endif | |||
return(0); | return(0); | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
#ifdef __linux__ | #ifdef __linux__ | |||
static uint32_t get_gateway_ip() { | static uint32_t get_gateway_ip () { | |||
FILE *fd; | ||||
char *token = NULL; | ||||
char *gateway_ip_str = NULL; | ||||
char buf[256]; | ||||
uint32_t gateway = 0; | ||||
if(!(fd = fopen("/proc/net/route", "r"))) | FILE *fd; | |||
return(0); | char *token = NULL; | |||
char *gateway_ip_str = NULL; | ||||
char buf[256]; | ||||
uint32_t gateway = 0; | ||||
if(!(fd = fopen("/proc/net/route", "r"))) | ||||
return(0); | ||||
while(fgets(buf, sizeof(buf), fd)) { | ||||
if(strtok(buf, "\t") && (token = strtok(NULL, "\t")) && (!strcmp(token, | ||||
"00000000"))) { | ||||
token = strtok(NULL, "\t"); | ||||
if(token) { | ||||
struct in_addr addr; | ||||
addr.s_addr = strtoul(token, NULL, 16); | ||||
gateway_ip_str = inet_ntoa(addr); | ||||
if(gateway_ip_str) { | ||||
gateway = addr.s_addr; | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
} | ||||
while(fgets(buf, sizeof(buf), fd)) { | fclose(fd); | |||
if(strtok(buf, "\t") && (token = strtok(NULL, "\t")) && (!strcmp(token, "000 | ||||
00000"))) { | ||||
token = strtok(NULL, "\t"); | ||||
if(token) { | return(gateway); | |||
struct in_addr addr; | } | |||
addr.s_addr = strtoul(token, NULL, 16); | static char* route_cmd_to_str (int cmd, const n2n_route_t *route, char *buf, siz | |||
gateway_ip_str = inet_ntoa(addr); | e_t bufsize) { | |||
if(gateway_ip_str) { | const char *cmd_str; | |||
gateway = addr.s_addr; | struct in_addr addr; | |||
break; | char netbuf[64], gwbuf[64]; | |||
} | ||||
} | switch(cmd) { | |||
case RTM_NEWROUTE: | ||||
cmd_str = "Add"; | ||||
break; | ||||
case RTM_DELROUTE: | ||||
cmd_str = "Delete"; | ||||
break; | ||||
default: | ||||
cmd_str = "?"; | ||||
} | } | |||
} | ||||
fclose(fd); | addr.s_addr = route->net_addr; | |||
inet_ntop(AF_INET, &addr, netbuf, sizeof(netbuf)); | ||||
addr.s_addr = route->gateway; | ||||
inet_ntop(AF_INET, &addr, gwbuf, sizeof(gwbuf)); | ||||
return(gateway); | snprintf(buf, bufsize, "%s %s/%d via %s", cmd_str, netbuf, route->net_bitlen | |||
, gwbuf); | ||||
return(buf); | ||||
} | } | |||
static char* route_cmd_to_str(int cmd, const n2n_route_t *route, char *buf, size | /* Adapted from https://olegkutkov.me/2019/08/29/modifying-linux-network-routes- | |||
_t bufsize) { | using-netlink/ */ | |||
const char *cmd_str; | #define NLMSG_TAIL(nmsg) | |||
struct in_addr addr; | \ | |||
char netbuf[64], gwbuf[64]; | ((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) | |||
switch(cmd) { | /* Add new data to rtattr */ | |||
case RTM_NEWROUTE: | static int rtattr_add (struct nlmsghdr *n, int maxlen, int type, const void *dat | |||
cmd_str = "Add"; | a, int alen) { | |||
break; | ||||
case RTM_DELROUTE: | int len = RTA_LENGTH(alen); | |||
cmd_str = "Delete"; | struct rtattr *rta; | |||
break; | ||||
default: | if(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { | |||
cmd_str = "?"; | traceEvent(TRACE_ERROR, "rtattr_add error: message exceeded bound of %d\ | |||
} | n", maxlen); | |||
return -1; | ||||
} | ||||
rta = NLMSG_TAIL(n); | ||||
rta->rta_type = type; | ||||
rta->rta_len = len; | ||||
addr.s_addr = route->net_addr; | if(alen) | |||
inet_ntop(AF_INET, &addr, netbuf, sizeof(netbuf)); | memcpy(RTA_DATA(rta), data, alen); | |||
addr.s_addr = route->gateway; | ||||
inet_ntop(AF_INET, &addr, gwbuf, sizeof(gwbuf)); | ||||
snprintf(buf, bufsize, "%s %s/%d via %s", cmd_str, netbuf, route->net_bitlen, gwbuf); | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); | |||
return(buf); | return 0; | |||
} | } | |||
/* Adapted from https://olegkutkov.me/2019/08/29/modifying-linux-network-routes- | static int routectl (int cmd, int flags, n2n_route_t *route, int if_idx) { | |||
using-netlink/ */ | ||||
#define NLMSG_TAIL(nmsg) \ | ||||
((struct rtattr *) (((char *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) | ||||
/* Add new data to rtattr */ | int rv = -1; | |||
static int rtattr_add(struct nlmsghdr *n, int maxlen, int type, const void *data | int rv2; | |||
, int alen) | char nl_buf[8192]; /* >= 8192 to avoid truncation, see "man 7 netlink" */ | |||
{ | char route_buf[256]; | |||
int len = RTA_LENGTH(alen); | struct iovec iov; | |||
struct rtattr *rta; | struct msghdr msg; | |||
struct sockaddr_nl sa; | ||||
if(NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { | uint8_t read_reply = 1; | |||
traceEvent(TRACE_ERROR, "rtattr_add error: message exceeded bound of %d\n", | int nl_sock; | |||
maxlen); | ||||
return -1; | struct { | |||
} | struct nlmsghdr n; | |||
struct rtmsg r; | ||||
rta = NLMSG_TAIL(n); | char buf[4096]; | |||
rta->rta_type = type; | } nl_request; | |||
rta->rta_len = len; | ||||
if((nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { | ||||
if(alen) | traceEvent(TRACE_ERROR, "netlink socket creation failed [%d]: %s", errno | |||
memcpy(RTA_DATA(rta), data, alen); | , strerror(errno)); | |||
return(-1); | ||||
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); | } | |||
return 0; | /* Subscribe to route change events */ | |||
} | iov.iov_base = nl_buf; | |||
iov.iov_len = sizeof(nl_buf); | ||||
static int routectl(int cmd, int flags, n2n_route_t *route, int if_idx) { | ||||
int rv = -1; | memset(&sa, 0, sizeof(sa)); | |||
int rv2; | sa.nl_family = PF_NETLINK; | |||
char nl_buf[8192]; /* >= 8192 to avoid truncation, see "man 7 netlink" */ | sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY; | |||
char route_buf[256]; | sa.nl_pid = getpid(); | |||
struct iovec iov; | ||||
struct msghdr msg; | memset(&msg, 0, sizeof(msg)); | |||
struct sockaddr_nl sa; | msg.msg_name = &sa; | |||
uint8_t read_reply = 1; | msg.msg_namelen = sizeof(sa); | |||
int nl_sock; | msg.msg_iov = &iov; | |||
msg.msg_iovlen = 1; | ||||
struct { | ||||
struct nlmsghdr n; | /* Subscribe to route events */ | |||
struct rtmsg r; | if(bind(nl_sock, (struct sockaddr*)&sa, sizeof(sa)) == -1) { | |||
char buf[4096]; | traceEvent(TRACE_ERROR, "netlink socket bind failed [%d]: %s", errno, st | |||
} nl_request; | rerror(errno)); | |||
goto out; | ||||
if((nl_sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) == -1) { | } | |||
traceEvent(TRACE_ERROR, "netlink socket creation failed [%d]: %s", errno, st | ||||
rerror(errno)); | /* Initialize request structure */ | |||
return(-1); | memset(&nl_request, 0, sizeof(nl_request)); | |||
} | nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | |||
nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; | ||||
/* Subscribe to route change events */ | nl_request.n.nlmsg_type = cmd; | |||
iov.iov_base = nl_buf; | nl_request.r.rtm_family = AF_INET; | |||
iov.iov_len = sizeof(nl_buf); | nl_request.r.rtm_table = RT_TABLE_MAIN; | |||
nl_request.r.rtm_scope = RT_SCOPE_NOWHERE; | ||||
memset(&sa, 0, sizeof(sa)); | ||||
sa.nl_family = PF_NETLINK; | /* Set additional flags if NOT deleting route */ | |||
sa.nl_groups = RTMGRP_IPV4_ROUTE | RTMGRP_NOTIFY; | if(cmd != RTM_DELROUTE) { | |||
sa.nl_pid = getpid(); | nl_request.r.rtm_protocol = RTPROT_BOOT; | |||
nl_request.r.rtm_type = RTN_UNICAST; | ||||
memset(&msg, 0, sizeof(msg)); | } | |||
msg.msg_name = &sa; | ||||
msg.msg_namelen = sizeof(sa); | ||||
msg.msg_iov = &iov; | ||||
msg.msg_iovlen = 1; | ||||
/* Subscribe to route events */ | ||||
if(bind(nl_sock, (struct sockaddr*)&sa, sizeof(sa)) == -1) { | ||||
traceEvent(TRACE_ERROR, "netlink socket bind failed [%d]: %s", errno, strerr | ||||
or(errno)); | ||||
goto out; | ||||
} | ||||
/* Initialize request structure */ | ||||
memset(&nl_request, 0, sizeof(nl_request)); | ||||
nl_request.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); | ||||
nl_request.n.nlmsg_flags = NLM_F_REQUEST | flags; | ||||
nl_request.n.nlmsg_type = cmd; | ||||
nl_request.r.rtm_family = AF_INET; | ||||
nl_request.r.rtm_table = RT_TABLE_MAIN; | ||||
nl_request.r.rtm_scope = RT_SCOPE_NOWHERE; | ||||
/* Set additional flags if NOT deleting route */ | ||||
if(cmd != RTM_DELROUTE) { | ||||
nl_request.r.rtm_protocol = RTPROT_BOOT; | ||||
nl_request.r.rtm_type = RTN_UNICAST; | ||||
} | ||||
nl_request.r.rtm_family = AF_INET; | ||||
nl_request.r.rtm_dst_len = route->net_bitlen; | ||||
/* Select scope, for simplicity we supports here only IPv6 and IPv4 */ | ||||
if(nl_request.r.rtm_family == AF_INET6) | ||||
nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; | ||||
else | ||||
nl_request.r.rtm_scope = RT_SCOPE_LINK; | ||||
/* Set gateway */ | ||||
if(route->net_bitlen) { | ||||
if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &route->gatewa | ||||
y, 4) < 0) | ||||
goto out; | ||||
nl_request.r.rtm_scope = 0; | ||||
nl_request.r.rtm_family = AF_INET; | nl_request.r.rtm_family = AF_INET; | |||
} | nl_request.r.rtm_dst_len = route->net_bitlen; | |||
/* Don't set destination and interface in case of default gateways */ | /* Select scope, for simplicity we supports here only IPv6 and IPv4 */ | |||
if(route->net_bitlen) { | if(nl_request.r.rtm_family == AF_INET6) | |||
/* Set destination network */ | nl_request.r.rtm_scope = RT_SCOPE_UNIVERSE; | |||
if(rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, &ro | else | |||
ute->net_addr, 4) < 0) | nl_request.r.rtm_scope = RT_SCOPE_LINK; | |||
goto out; | ||||
/* Set interface */ | ||||
if(if_idx > 0) { | ||||
if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, sizeof( | ||||
int)) < 0) | ||||
goto out; | ||||
} | ||||
} | ||||
/* Send message to the netlink */ | ||||
if((rv2 = send(nl_sock, &nl_request, sizeof(nl_request), 0)) != sizeof(nl_requ | ||||
est)) { | ||||
traceEvent(TRACE_ERROR, "netlink send failed [%d]: %s", errno, strerror(errn | ||||
o)); | ||||
goto out; | ||||
} | ||||
/* Wait for the route notification. Assume that the first reply we get is the | ||||
correct one. */ | ||||
traceEvent(TRACE_DEBUG, "waiting for netlink response..."); | ||||
while(read_reply) { | ||||
ssize_t len = recvmsg(nl_sock, &msg, 0); | ||||
struct nlmsghdr *nh; | ||||
for(nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, l | ||||
en)) { | ||||
/* Stop after the first reply */ | ||||
read_reply = 0; | ||||
if(nh->nlmsg_type == NLMSG_ERROR) { | ||||
struct nlmsgerr *err = NLMSG_DATA(nh); | ||||
int errcode = err->error; | ||||
if(errcode < 0) | ||||
errcode = -errcode; | ||||
/* Ignore EEXIST as existing rules are ok */ | ||||
if(errcode != EEXIST) { | ||||
traceEvent(TRACE_ERROR, "[err=%d] route: %s", errcode, route_cmd_to_str | ||||
(cmd, route, route_buf, sizeof(route_buf))); | ||||
goto out; | ||||
} | ||||
} | ||||
if(nh->nlmsg_type == NLMSG_DONE) | /* Set gateway */ | |||
break; | if(route->net_bitlen) { | |||
if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_GATEWAY, &route->ga | ||||
teway, 4) < 0) | ||||
goto out; | ||||
nl_request.r.rtm_scope = 0; | ||||
nl_request.r.rtm_family = AF_INET; | ||||
} | ||||
/* Don't set destination and interface in case of default gateways */ | ||||
if(route->net_bitlen) { | ||||
/* Set destination network */ | ||||
if(rtattr_add(&nl_request.n, sizeof(nl_request), /*RTA_NEWDST*/ RTA_DST, | ||||
&route->net_addr, 4) < 0) | ||||
goto out; | ||||
/* Set interface */ | ||||
if(if_idx > 0) { | ||||
if(rtattr_add(&nl_request.n, sizeof(nl_request), RTA_OIF, &if_idx, s | ||||
izeof(int)) < 0) | ||||
goto out; | ||||
} | ||||
} | ||||
if(nh->nlmsg_type == cmd) { | /* Send message to the netlink */ | |||
traceEvent(TRACE_DEBUG, "Found netlink reply"); | if((rv2 = send(nl_sock, &nl_request, sizeof(nl_request), 0)) != sizeof(nl_re | |||
break; | quest)) { | |||
} | traceEvent(TRACE_ERROR, "netlink send failed [%d]: %s", errno, strerror( | |||
errno)); | ||||
goto out; | ||||
} | ||||
/* Wait for the route notification. Assume that the first reply we get is th | ||||
e correct one. */ | ||||
traceEvent(TRACE_DEBUG, "waiting for netlink response..."); | ||||
while(read_reply) { | ||||
ssize_t len = recvmsg(nl_sock, &msg, 0); | ||||
struct nlmsghdr *nh; | ||||
for(nh = (struct nlmsghdr *)nl_buf; NLMSG_OK(nh, len); nh = NLMSG_NEXT(n | ||||
h, len)) { | ||||
/* Stop after the first reply */ | ||||
read_reply = 0; | ||||
if(nh->nlmsg_type == NLMSG_ERROR) { | ||||
struct nlmsgerr *err = NLMSG_DATA(nh); | ||||
int errcode = err->error; | ||||
if(errcode < 0) | ||||
errcode = -errcode; | ||||
/* Ignore EEXIST as existing rules are ok */ | ||||
if(errcode != EEXIST) { | ||||
traceEvent(TRACE_ERROR, "[err=%d] route: %s", errcode, route | ||||
_cmd_to_str(cmd, route, route_buf, sizeof(route_buf))); | ||||
goto out; | ||||
} | ||||
} | ||||
if(nh->nlmsg_type == NLMSG_DONE) | ||||
break; | ||||
if(nh->nlmsg_type == cmd) { | ||||
traceEvent(TRACE_DEBUG, "Found netlink reply"); | ||||
break; | ||||
} | ||||
} | ||||
} | } | |||
} | ||||
traceEvent(TRACE_DEBUG, route_cmd_to_str(cmd, route, route_buf, sizeof(route_b | traceEvent(TRACE_DEBUG, route_cmd_to_str(cmd, route, route_buf, sizeof(route | |||
uf))); | _buf))); | |||
rv = 0; | rv = 0; | |||
out: | out: | |||
close(nl_sock); | close(nl_sock); | |||
return(rv); | return(rv); | |||
} | } | |||
#endif | #endif | |||
/* ************************************** */ | /* ************************************** */ | |||
static int edge_init_routes_linux(n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) { | ||||
#ifdef __linux__ | #ifdef __linux__ | |||
int i; | ||||
for (i = 0; i<num_routes; i++) { | ||||
n2n_route_t *route = &routes[i]; | ||||
if ((route->net_addr == 0) && (route->net_bitlen == 0)) { | ||||
/* This is a default gateway rule. We need to: | ||||
* | ||||
* 1. Add a route to the supernode via the host internet gateway | ||||
* 2. Add the new default gateway route | ||||
* | ||||
* Instead of modifying the system default gateway, we use the trick | ||||
* of adding a route to the networks 0.0.0.0/1 and 128.0.0.0/1, thus | ||||
* covering the whole IPv4 range. Such routes in linux take precedence | ||||
* over the default gateway (0.0.0.0/0) since are more specific. | ||||
* This leaves the default gateway unchanged so that after n2n is | ||||
* stopped the cleanup is easier. | ||||
* See https://github.com/zerotier/ZeroTierOne/issues/178#issuecomment-204 | ||||
599227 | ||||
*/ | ||||
n2n_sock_t sn; | ||||
n2n_route_t custom_route; | ||||
uint32_t *a; | ||||
if (eee->sn_route_to_clean) { | ||||
traceEvent(TRACE_ERROR, "Only one default gateway route allowed"); | ||||
return(-1); | ||||
} | ||||
if (eee->conf.sn_num != 1) { | ||||
traceEvent(TRACE_ERROR, "Only one supernode supported with routes"); | ||||
return(-1); | ||||
} | ||||
if (supernode2addr(&sn, eee->conf.sn_ip_array[0]) < 0) | ||||
return(-1); | ||||
if (sn.family != AF_INET) { | ||||
traceEvent(TRACE_ERROR, "Only IPv4 routes supported"); | ||||
return(-1); | ||||
} | ||||
a = (u_int32_t*)sn.addr.v4; | ||||
custom_route.net_addr = *a; | ||||
custom_route.net_bitlen = 32; | ||||
custom_route.gateway = get_gateway_ip(); | ||||
if (!custom_route.gateway) { | ||||
traceEvent(TRACE_ERROR, "could not determine the gateway IP address"); | ||||
return(-1); | ||||
} | ||||
/* ip route add supernode via internet_gateway */ | ||||
if (routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, -1) < | ||||
0) | ||||
return(-1); | ||||
/* Save the route to delete it when n2n is stopped */ | ||||
eee->sn_route_to_clean = calloc(1, sizeof(n2n_route_t)); | ||||
/* Store a copy of the rules into the runtime to delete it during shutdown | ||||
*/ | ||||
if (eee->sn_route_to_clean) | ||||
*eee->sn_route_to_clean = custom_route; | ||||
/* ip route add 0.0.0.0/1 via n2n_gateway */ | ||||
custom_route.net_addr = 0; | ||||
custom_route.net_bitlen = 1; | ||||
custom_route.gateway = route->gateway; | ||||
if (routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, eee-> | ||||
device.if_idx) < 0) | ||||
return(-1); | ||||
/* ip route add 128.0.0.0/1 via n2n_gateway */ | ||||
custom_route.net_addr = 128; | ||||
custom_route.net_bitlen = 1; | ||||
custom_route.gateway = route->gateway; | ||||
if (routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, eee-> | static int edge_init_routes_linux (n2n_edge_t *eee, n2n_route_t *routes, uint16_ | |||
device.if_idx) < 0) | t num_routes) { | |||
return(-1); | int i; | |||
} | for(i = 0; i<num_routes; i++) { | |||
else { | n2n_route_t *route = &routes[i]; | |||
/* ip route add net via n2n_gateway */ | ||||
if (routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, route, eee->device.i | if((route->net_addr == 0) && (route->net_bitlen == 0)) { | |||
f_idx) < 0) | /* This is a default gateway rule. We need to: | |||
return(-1); | * | |||
* 1. Add a route to the supernode via the host internet gateway | ||||
* 2. Add the new default gateway route | ||||
* | ||||
* Instead of modifying the system default gateway, we use the trick | ||||
* of adding a route to the networks 0.0.0.0/1 and 128.0.0.0/1, thus | ||||
* covering the whole IPv4 range. Such routes in linux take preceden | ||||
ce | ||||
* over the default gateway (0.0.0.0/0) since are more specific. | ||||
* This leaves the default gateway unchanged so that after n2n is | ||||
* stopped the cleanup is easier. | ||||
* See https://github.com/zerotier/ZeroTierOne/issues/178#issuecomme | ||||
nt-204599227 | ||||
*/ | ||||
n2n_sock_t sn; | ||||
n2n_route_t custom_route; | ||||
uint32_t *a; | ||||
if(eee->sn_route_to_clean) { | ||||
traceEvent(TRACE_ERROR, "only one default gateway route allowed" | ||||
); | ||||
return(-1); | ||||
} | ||||
if(eee->conf.sn_num != 1) { | ||||
traceEvent(TRACE_ERROR, "only one supernode supported with route | ||||
s"); | ||||
return(-1); | ||||
} | ||||
if(supernode2sock(&sn, eee->conf.supernodes->ip_addr) < 0) | ||||
return(-1); | ||||
if(sn.family != AF_INET) { | ||||
traceEvent(TRACE_ERROR, "only IPv4 routes supported"); | ||||
return(-1); | ||||
} | ||||
a = (u_int32_t*)sn.addr.v4; | ||||
custom_route.net_addr = *a; | ||||
custom_route.net_bitlen = 32; | ||||
custom_route.gateway = get_gateway_ip(); | ||||
if(!custom_route.gateway) { | ||||
traceEvent(TRACE_ERROR, "could not determine the gateway IP addr | ||||
ess"); | ||||
return(-1); | ||||
} | ||||
/* ip route add supernode via internet_gateway */ | ||||
if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, | ||||
-1) < 0) | ||||
return(-1); | ||||
/* Save the route to delete it when n2n is stopped */ | ||||
eee->sn_route_to_clean = calloc(1, sizeof(n2n_route_t)); | ||||
/* Store a copy of the rules into the runtime to delete it during sh | ||||
utdown */ | ||||
if(eee->sn_route_to_clean) | ||||
*eee->sn_route_to_clean = custom_route; | ||||
/* ip route add 0.0.0.0/1 via n2n_gateway */ | ||||
custom_route.net_addr = 0; | ||||
custom_route.net_bitlen = 1; | ||||
custom_route.gateway = route->gateway; | ||||
if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, | ||||
eee->device.if_idx) < 0) | ||||
return(-1); | ||||
/* ip route add 128.0.0.0/1 via n2n_gateway */ | ||||
custom_route.net_addr = 128; | ||||
custom_route.net_bitlen = 1; | ||||
custom_route.gateway = route->gateway; | ||||
if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, &custom_route, | ||||
eee->device.if_idx) < 0) | ||||
return(-1); | ||||
} else { | ||||
/* ip route add net via n2n_gateway */ | ||||
if(routectl(RTM_NEWROUTE, NLM_F_CREATE | NLM_F_EXCL, route, eee->dev | ||||
ice.if_idx) < 0) | ||||
return(-1); | ||||
} | ||||
} | } | |||
} | ||||
#endif | ||||
return(0); | return(0); | |||
} | } | |||
#endif | ||||
/* ************************************** */ | /* ************************************** */ | |||
static int edge_init_routes_win(n2n_edge_t *eee, n2n_route_t *routes, uint16_t n | ||||
um_routes) | ||||
{ | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
int i; | static int edge_init_routes_win (n2n_edge_t *eee, n2n_route_t *routes, uint16_t | |||
struct in_addr net_addr, gateway; | num_routes, uint8_t verb /* 0 = add, 1 = delete */) { | |||
char c_net_addr[32]; | int i; | |||
char c_gateway[32]; | struct in_addr net_addr, gateway; | |||
char cmd[256]; | char c_net_addr[32]; | |||
char c_gateway[32]; | ||||
for (i = 0; i < num_routes; i++) | char c_interface[32]; | |||
{ | char c_verb[32]; | |||
n2n_route_t *route = &routes[i]; | char cmd[256]; | |||
if ((route->net_addr == 0) && (route->net_bitlen == 0)) | ||||
{ | for(i = 0; i < num_routes; i++) { | |||
traceEvent(TRACE_NORMAL, "Warning: The 0.0.0.0/0 route settings are not | n2n_route_t *route = &routes[i]; | |||
supported on Windows"); | if((route->net_addr == 0) && (route->net_bitlen == 0)) { | |||
return (-1); | // REVISIT: there might be a chance to get it working on Windows fol | |||
} | lowing the hints at | |||
else | // https://docs.microsoft.com/en-us/windows/win32/api/netio | |||
{ | api/ns-netioapi-mib_ipinterface_row | |||
/* ip route add net via n2n_gateway */ | // | |||
memcpy(&net_addr, &(route->net_addr), sizeof(net_addr)); | // " The DisableDefaultRoutes member of the MIB_IPINTERFACE_R | |||
memcpy(&gateway, &(route->gateway), sizeof(gateway)); | OW structure can be used to disable | |||
_snprintf(c_net_addr, sizeof(c_net_addr), inet_ntoa(net_addr)); | // using the default route on an interface. This member can | |||
_snprintf(c_gateway, sizeof(c_gateway), inet_ntoa(gateway)); | be used as a security measure by | |||
_snprintf(cmd, sizeof(cmd), "route add %s/%d %s > nul", c_net_addr, rou | // VPN clients to restrict split tunneling when split tunne | |||
te->net_bitlen, c_gateway); | ling is not required by the VPN client. | |||
traceEvent(TRACE_NORMAL, "ROUTE CMD = '%s'\n", cmd); | // A VPN client can call the SetIpInterfaceEntry function t | |||
system(cmd); | o set the DisableDefaultRoutes member | |||
// to TRUE when required. A VPN client can query the curren | ||||
t state of the DisableDefaultRoutes | ||||
// member by calling the GetIpInterfaceEntry function. " | ||||
traceEvent(TRACE_WARNING, "the 0.0.0.0/0 route settings are not supp | ||||
orted on Windows"); | ||||
return(-1); | ||||
} else { | ||||
/* ip route add net via n2n_gateway */ | ||||
memcpy(&net_addr, &(route->net_addr), sizeof(net_addr)); | ||||
memcpy(&gateway, &(route->gateway), sizeof(gateway)); | ||||
_snprintf(c_net_addr, sizeof(c_net_addr), inet_ntoa(net_addr)); | ||||
_snprintf(c_gateway, sizeof(c_gateway), inet_ntoa(gateway)); | ||||
_snprintf(c_interface, sizeof(c_interface), "if %u", eee->device.if_ | ||||
idx); | ||||
_snprintf(c_verb, sizeof(c_verb), verb ? "delete" : "add"); | ||||
_snprintf(cmd, sizeof(cmd), "route %s %s/%d %s %s > nul", c_verb, c_ | ||||
net_addr, route->net_bitlen, c_gateway, c_interface); | ||||
traceEvent(TRACE_NORMAL, "ROUTE CMD = '%s'\n", cmd); | ||||
system(cmd); | ||||
} | } | |||
} | } | |||
#endif // WIN32 | return (0); | |||
return (0); | ||||
} | } | |||
#endif // WIN32 | ||||
/* ************************************** */ | /* ************************************** */ | |||
/* Add the user-provided routes to the linux routing table. Network routes | /* Add the user-provided routes to the linux routing table. Network routes | |||
* are bound to the n2n TAP device, so they are automatically removed when | * are bound to the n2n TAP device, so they are automatically removed when | |||
* the TAP device is destroyed. */ | * the TAP device is destroyed. */ | |||
static int edge_init_routes(n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_r outes) { | int edge_init_routes (n2n_edge_t *eee, n2n_route_t *routes, uint16_t num_routes) { | |||
#ifdef __linux__ | #ifdef __linux__ | |||
return edge_init_routes_linux(eee, routes, num_routes); | return edge_init_routes_linux(eee, routes, num_routes); | |||
#endif | #endif | |||
#ifdef WIN32 | #ifdef WIN32 | |||
return edge_init_routes_win(eee, routes, num_routes); | return edge_init_routes_win(eee, routes, num_routes, 0 /* add */); | |||
#endif | #endif | |||
return 0; | return 0; | |||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
static void edge_cleanup_routes(n2n_edge_t *eee) { | static void edge_cleanup_routes (n2n_edge_t *eee) { | |||
#ifdef __linux__ | #ifdef __linux__ | |||
if(eee->sn_route_to_clean) { | if(eee->sn_route_to_clean) { | |||
/* ip route del supernode via internet_gateway */ | /* ip route del supernode via internet_gateway */ | |||
routectl(RTM_DELROUTE, 0, eee->sn_route_to_clean, -1); | routectl(RTM_DELROUTE, 0, eee->sn_route_to_clean, -1); | |||
free(eee->sn_route_to_clean); | free(eee->sn_route_to_clean); | |||
} | } | |||
#endif | #endif | |||
#ifdef WIN32 | ||||
edge_init_routes_win(eee, eee->conf.routes, eee->conf.num_routes, 1 /* del * | ||||
/); | ||||
#endif | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void edge_init_conf_defaults(n2n_edge_conf_t *conf) { | void edge_init_conf_defaults (n2n_edge_conf_t *conf) { | |||
memset(conf, 0, sizeof(*conf)); | ||||
conf->local_port = 0 /* any port */; | char *tmp_string; | |||
conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ | ||||
conf->transop_id = N2N_TRANSFORM_ID_NULL; | ||||
conf->header_encryption = HEADER_ENCRYPTION_NONE; | ||||
conf->compression = N2N_COMPRESSION_ID_NONE; | ||||
conf->drop_multicast = 1; | ||||
conf->allow_p2p = 1; | ||||
conf->disable_pmtu_discovery = 1; | ||||
conf->register_interval = REGISTER_SUPER_INTERVAL_DFL; | ||||
if(getenv("N2N_KEY")) { | memset(conf, 0, sizeof(*conf)); | |||
conf->encrypt_key = strdup(getenv("N2N_KEY")); | ||||
conf->transop_id = N2N_TRANSFORM_ID_TWOFISH; | conf->bind_address = INADDR_ANY; /* any address */ | |||
} | conf->local_port = 0 /* any port */; | |||
conf->preferred_sock.family = AF_INVALID; | ||||
conf->mgmt_port = N2N_EDGE_MGMT_PORT; /* 5644 by default */ | ||||
conf->transop_id = N2N_TRANSFORM_ID_NULL; | ||||
conf->header_encryption = HEADER_ENCRYPTION_NONE; | ||||
conf->compression = N2N_COMPRESSION_ID_NONE; | ||||
conf->drop_multicast = 1; | ||||
conf->allow_p2p = 1; | ||||
conf->disable_pmtu_discovery = 1; | ||||
conf->register_interval = REGISTER_SUPER_INTERVAL_DFL; | ||||
conf->tuntap_ip_mode = TUNTAP_IP_MODE_SN_ASSIGN; | ||||
/* reserve possible last char as null terminator. */ | ||||
gethostname((char*)conf->dev_desc, N2N_DESC_SIZE-1); | ||||
if(getenv("N2N_KEY")) { | ||||
conf->encrypt_key = strdup(getenv("N2N_KEY")); | ||||
conf->transop_id = N2N_TRANSFORM_ID_AES; | ||||
} | ||||
if(getenv("N2N_COMMUNITY")) { | ||||
strncpy((char*)conf->community_name, getenv("N2N_COMMUNITY"), N2N_COMMUN | ||||
ITY_SIZE); | ||||
conf->community_name[N2N_COMMUNITY_SIZE - 1] = '\0'; | ||||
} | ||||
if(getenv("N2N_PASSWORD")) { | ||||
conf->shared_secret = calloc(1, sizeof(n2n_private_public_key_t)); | ||||
if(conf->shared_secret) | ||||
generate_private_key(*(conf->shared_secret), getenv("N2N_PASSWORD")) | ||||
; | ||||
} | ||||
tmp_string = calloc(1, strlen(N2N_MGMT_PASSWORD) + 1); | ||||
if(tmp_string) { | ||||
strncpy((char*)tmp_string, N2N_MGMT_PASSWORD, strlen(N2N_MGMT_PASSWORD) | ||||
+ 1); | ||||
conf->mgmt_password_hash = pearson_hash_64((uint8_t*)tmp_string, strlen( | ||||
N2N_MGMT_PASSWORD)); | ||||
free(tmp_string); | ||||
} | ||||
conf->sn_selection_strategy = SN_SELECTION_STRATEGY_LOAD; | ||||
conf->metric = 0; | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
void edge_term_conf(n2n_edge_conf_t *conf) { | void edge_term_conf (n2n_edge_conf_t *conf) { | |||
if(conf->routes) free(conf->routes); | ||||
if(conf->routes) free(conf->routes); | ||||
if(conf->encrypt_key) free(conf->encrypt_key); | ||||
if(conf->network_traffic_filter_rules) { | ||||
filter_rule_t *el = 0, *tmp = 0; | ||||
HASH_ITER(hh, conf->network_traffic_filter_rules, el, tmp) { | ||||
HASH_DEL(conf->network_traffic_filter_rules, el); | ||||
free(el); | ||||
} | ||||
} | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
const n2n_edge_conf_t* edge_get_conf(const n2n_edge_t *eee) { | const n2n_edge_conf_t* edge_get_conf (const n2n_edge_t *eee) { | |||
return(&eee->conf); | ||||
return(&eee->conf); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
int edge_conf_add_supernode(n2n_edge_conf_t *conf, const char *ip_and_port) { | int edge_conf_add_supernode (n2n_edge_conf_t *conf, const char *ip_and_port) { | |||
if(conf->sn_num >= N2N_EDGE_NUM_SUPERNODES) | ||||
return(-1); | ||||
strncpy((conf->sn_ip_array[conf->sn_num]), ip_and_port, N2N_EDGE_SN_HOST_SIZE) | struct peer_info *sn; | |||
; | n2n_sock_t *sock; | |||
traceEvent(TRACE_NORMAL, "Adding supernode[%u] = %s", (unsigned int)conf->sn_n | int skip_add; | |||
um, (conf->sn_ip_array[conf->sn_num])); | int rv = -1; | |||
conf->sn_num++; | ||||
return(0); | sock = (n2n_sock_t*)calloc(1,sizeof(n2n_sock_t)); | |||
} | rv = supernode2sock(sock, ip_and_port); | |||
/* ************************************** */ | if(rv < -2) { /* we accept resolver failure as it might resolve later */ | |||
traceEvent(TRACE_WARNING, "invalid supernode parameter."); | ||||
free(sock); | ||||
return 1; | ||||
} | ||||
skip_add = SN_ADD; | ||||
sn = add_sn_to_list_by_mac_or_sock(&(conf->supernodes), sock, null_mac, &ski | ||||
p_add); | ||||
if(sn != NULL) { | ||||
sn->ip_addr = calloc(1, N2N_EDGE_SN_HOST_SIZE); | ||||
int quick_edge_init(char *device_name, char *community_name, | if(sn->ip_addr != NULL) { | |||
char *encrypt_key, char *device_mac, | strncpy(sn->ip_addr, ip_and_port, N2N_EDGE_SN_HOST_SIZE - 1); | |||
char *local_ip_address, | memcpy(&(sn->sock), sock, sizeof(n2n_sock_t)); | |||
char *supernode_ip_address_port, | memcpy(sn->mac_addr, null_mac, sizeof(n2n_mac_t)); | |||
int *keep_on_running) { | sn->purgeable = SN_UNPURGEABLE; | |||
tuntap_dev tuntap; | } | |||
n2n_edge_t *eee; | } | |||
n2n_edge_conf_t conf; | ||||
int rv; | ||||
/* Setup the configuration */ | free(sock); | |||
edge_init_conf_defaults(&conf); | ||||
conf.encrypt_key = encrypt_key; | ||||
conf.transop_id = N2N_TRANSFORM_ID_TWOFISH; | ||||
snprintf((char*)conf.community_name, sizeof(conf.community_name), "%s", commun | ||||
ity_name); | ||||
edge_conf_add_supernode(&conf, supernode_ip_address_port); | ||||
/* Validate configuration */ | traceEvent(TRACE_NORMAL, "adding supernode = %s", sn->ip_addr); | |||
if(edge_verify_conf(&conf) != 0) | conf->sn_num++; | |||
return(-1); | ||||
/* Open the tuntap device */ | return 0; | |||
if(tuntap_open(&tuntap, device_name, "static", | } | |||
local_ip_address, "255.255.255.0", | ||||
device_mac, DEFAULT_MTU) < 0) | ||||
return(-2); | ||||
/* Init edge */ | /* ************************************** */ | |||
if((eee = edge_init(&tuntap, &conf, &rv)) == NULL) | ||||
goto quick_edge_init_end; | ||||
rv = run_edge_loop(eee, keep_on_running); | int quick_edge_init (char *device_name, char *community_name, | |||
edge_term(eee); | char *encrypt_key, char *device_mac, | |||
edge_term_conf(&conf); | char *local_ip_address, | |||
char *supernode_ip_address_port, | ||||
int *keep_on_running) { | ||||
tuntap_dev tuntap; | ||||
n2n_edge_t *eee; | ||||
n2n_edge_conf_t conf; | ||||
int rv; | ||||
/* Setup the configuration */ | ||||
edge_init_conf_defaults(&conf); | ||||
conf.encrypt_key = encrypt_key; | ||||
conf.transop_id = N2N_TRANSFORM_ID_AES; | ||||
conf.compression = N2N_COMPRESSION_ID_NONE; | ||||
snprintf((char*)conf.community_name, sizeof(conf.community_name), "%s", comm | ||||
unity_name); | ||||
edge_conf_add_supernode(&conf, supernode_ip_address_port); | ||||
/* Validate configuration */ | ||||
if(edge_verify_conf(&conf) != 0) | ||||
return(-1); | ||||
/* Open the tuntap device */ | ||||
if(tuntap_open(&tuntap, device_name, "static", | ||||
local_ip_address, "255.255.255.0", | ||||
device_mac, DEFAULT_MTU | ||||
#ifdef WIN32 | ||||
, 0 | ||||
#endif | ||||
) < 0) | ||||
return(-2); | ||||
quick_edge_init_end: | /* Init edge */ | |||
tuntap_close(&tuntap); | if((eee = edge_init(&conf, &rv)) == NULL) | |||
return(rv); | goto quick_edge_init_end; | |||
eee->keep_running = keep_on_running; | ||||
rv = run_edge_loop(eee); | ||||
edge_term(eee); | ||||
edge_term_conf(&conf); | ||||
quick_edge_init_end: | ||||
tuntap_close(&tuntap); | ||||
return(rv); | ||||
} | } | |||
/* ************************************** */ | /* ************************************** */ | |||
End of changes. 316 change blocks. | ||||
2250 lines changed or deleted | 3671 lines changed or added |