"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/edge_utils.c" between
n2n-2.8.tar.gz and n2n-3.0.tar.gz

About: n2n is a layer-two peer-to-peer virtual private network (VPN) which allows bypassing intermediate firewalls.

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(&reg, 0, sizeof(reg)); memset(&cmn, 0, sizeof(cmn));
cmn.ttl=N2N_DEFAULT_TTL; memset(&reg, 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); 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, &reg);
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(&reg, 0, sizeof(reg));
cmn.ttl=N2N_DEFAULT_TTL; memset(&cmn, 0, sizeof(cmn));
cmn.pc = n2n_register; memset(&reg, 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); 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, &reg);
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*)&eth_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*)&eth_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(&reg, &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(&reg.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, &reg);
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(&reg, &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(&reg.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, &reg);
}
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, &reg.dev_
traceEvent(TRACE_DEBUG, "readFromIPSocket dropped PEER_INFO due to t addr, (const n2n_desc_t*)&reg.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

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)