sn_utils.c (n2n-2.8) | : | sn_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" | |||
#define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) | #define HASH_FIND_COMMUNITY(head, name, out) HASH_FIND_STR(head, name, out) | |||
static int try_forward(n2n_sn_t * sss, | int resolve_create_thread (n2n_resolve_parameter_t **param, struct peer_info *sn | |||
const struct sn_community *comm, | _list); | |||
const n2n_common_t * cmn, | int resolve_check (n2n_resolve_parameter_t *param, uint8_t resolution_request, t | |||
const n2n_mac_t dstMac, | ime_t now); | |||
const uint8_t * pktbuf, | int resolve_cancel_thread (n2n_resolve_parameter_t *param); | |||
size_t pktsize); | ||||
static ssize_t sendto_peer (n2n_sn_t *sss, | ||||
const struct peer_info *peer, | ||||
const uint8_t *pktbuf, | ||||
size_t pktsize); | ||||
static ssize_t sendto_sock(n2n_sn_t *sss, | static int sendto_mgmt (n2n_sn_t *sss, | |||
const n2n_sock_t *sock, | const struct sockaddr_in *sender_sock, | |||
const uint8_t *pktbuf, | const uint8_t *mgmt_buf, | |||
size_t pktsize); | size_t mgmt_size); | |||
static int sendto_mgmt(n2n_sn_t *sss, | static uint16_t reg_lifetime (n2n_sn_t *sss); | |||
const struct sockaddr_in *sender_sock, | ||||
const uint8_t *mgmt_buf, | static int update_edge (n2n_sn_t *sss, | |||
size_t mgmt_size); | const n2n_common_t* cmn, | |||
const n2n_REGISTER_SUPER_t* reg, | ||||
static int try_broadcast(n2n_sn_t * sss, | struct sn_community *comm, | |||
const struct sn_community *comm, | const n2n_sock_t *sender_sock, | |||
const n2n_common_t * cmn, | const SOCKET socket_fd, | |||
const n2n_mac_t srcMac, | n2n_auth_t *answer_auth, | |||
const uint8_t * pktbuf, | int skip_add, | |||
size_t pktsize); | time_t now); | |||
static uint16_t reg_lifetime(n2n_sn_t *sss); | static int re_register_and_purge_supernodes (n2n_sn_t *sss, | |||
struct sn_community *comm, | ||||
static int update_edge(n2n_sn_t *sss, | time_t *p_last_re_reg_and_purge, | |||
const n2n_mac_t edgeMac, | time_t now, | |||
struct sn_community *comm, | uint8_t forced); | |||
const n2n_sock_t *sender_sock, | ||||
time_t now); | static int purge_expired_communities (n2n_sn_t *sss, | |||
time_t* p_last_purge, | ||||
static int purge_expired_communities(n2n_sn_t *sss, | time_t now); | |||
time_t* p_last_purge, | ||||
time_t now); | ||||
static int sort_communities (n2n_sn_t *sss, | static int sort_communities (n2n_sn_t *sss, | |||
time_t* p_last_sort, | time_t* p_last_sort, | |||
time_t now); | time_t now); | |||
static int process_mgmt(n2n_sn_t *sss, | static int process_mgmt (n2n_sn_t *sss, | |||
const struct sockaddr_in *sender_sock, | ||||
char *mgmt_buf, | ||||
size_t mgmt_size, | ||||
time_t now); | ||||
static int process_udp (n2n_sn_t *sss, | ||||
const struct sockaddr_in *sender_sock, | const struct sockaddr_in *sender_sock, | |||
const uint8_t *mgmt_buf, | const SOCKET socket_fd, | |||
size_t mgmt_size, | uint8_t *udp_buf, | |||
size_t udp_size, | ||||
time_t now); | time_t now); | |||
static int process_udp(n2n_sn_t *sss, | ||||
const struct sockaddr_in *sender_sock, | ||||
uint8_t *udp_buf, | ||||
size_t udp_size, | ||||
time_t now); | ||||
/* ************************************** */ | /* ************************************** */ | |||
static int try_forward(n2n_sn_t * sss, | void close_tcp_connection (n2n_sn_t *sss, n2n_tcp_connection_t *conn) { | |||
const struct sn_community *comm, | ||||
const n2n_common_t * cmn, | struct sn_community *comm, *tmp_comm; | |||
const n2n_mac_t dstMac, | struct peer_info *edge, *tmp_edge; | |||
const uint8_t * pktbuf, | ||||
size_t pktsize) | if(!conn) | |||
{ | return; | |||
struct peer_info * scan; | ||||
macstr_t mac_buf; | // find peer by file descriptor | |||
n2n_sock_str_t sockbuf; | HASH_ITER(hh, sss->communities, comm, tmp_comm) { | |||
HASH_ITER(hh, comm->edges, edge, tmp_edge) { | ||||
HASH_FIND_PEER(comm->edges, dstMac, scan); | if(edge->socket_fd == conn->socket_fd) { | |||
// remove peer | ||||
if(NULL != scan) | HASH_DEL(comm->edges, edge); | |||
{ | free(edge); | |||
int data_sent_len; | goto close_conn; /* break - level 2 */ | |||
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); | } | |||
} | ||||
if(data_sent_len == pktsize) | } | |||
{ | ||||
++(sss->stats.fwd); | close_conn: | |||
traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s", | // close the connection | |||
pktsize, | shutdown(conn->socket_fd, SHUT_RDWR); | |||
sock_to_cstr(sockbuf, &(scan->sock)), | closesocket(conn->socket_fd); | |||
macaddr_str(mac_buf, scan->mac_addr)); | // forget about the connection, will be deleted later | |||
} | conn->inactive = 1; | |||
else | } | |||
{ | ||||
++(sss->stats.errors); | /* *************************************************** */ | |||
traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)", | ||||
pktsize, | // generate shared secrets for user authentication; can be done only after | |||
sock_to_cstr(sockbuf, &(scan->sock)), | // federation name is known (-F) and community list completely read (-c) | |||
macaddr_str(mac_buf, scan->mac_addr), | void calculate_shared_secrets (n2n_sn_t *sss) { | |||
errno, strerror(errno)); | ||||
} | struct sn_community *comm, *tmp_comm; | |||
} | sn_user_t *user, *tmp_user; | |||
else | ||||
{ | traceEvent(TRACE_INFO, "started shared secrets calculation for edge authenti | |||
traceEvent(TRACE_DEBUG, "try_forward unknown MAC"); | cation"); | |||
generate_private_key(sss->private_key, sss->federation->community + 1); /* s | ||||
kip '*' federation leading character */ | ||||
HASH_ITER(hh, sss->communities, comm, tmp_comm) { | ||||
if(comm->is_federation) { | ||||
continue; | ||||
} | ||||
HASH_ITER(hh, comm->allowed_users, user, tmp_user) { | ||||
// calculate common shared secret (ECDH) | ||||
generate_shared_secret(user->shared_secret, sss->private_key, user-> | ||||
public_key); | ||||
// prepare for use as key | ||||
user->shared_secret_ctx = (he_context_t*)calloc(1, sizeof(speck_cont | ||||
ext_t)); | ||||
speck_init((speck_context_t**)&user->shared_secret_ctx, user->shared | ||||
_secret, 128); | ||||
} | ||||
} | ||||
traceEvent(TRACE_NORMAL, "calculated shared secrets for edge authentication" | ||||
); | ||||
} | ||||
// calculate dynamic keys | ||||
void calculate_dynamic_keys (n2n_sn_t *sss) { | ||||
struct sn_community *comm, *tmp_comm = NULL; | ||||
traceEvent(TRACE_INFO, "calculating dynamic keys"); | ||||
HASH_ITER(hh, sss->communities, comm, tmp_comm) { | ||||
// skip federation | ||||
if(comm->is_federation) { | ||||
continue; | ||||
} | ||||
// calculate dynamic keys if this is a user/pw auth'ed community | ||||
if(comm->allowed_users) { | ||||
calculate_dynamic_key(comm->dynamic_key, /* destination */ | ||||
sss->dynamic_key_time, /* time - same fo | ||||
r all */ | ||||
(uint8_t *)comm->community, /* community name | ||||
*/ | ||||
(uint8_t *)sss->federation->community); /* fed | ||||
eration name */ | ||||
packet_header_change_dynamic_key(comm->dynamic_key, | ||||
&(comm->header_encryption_ctx_dynam | ||||
ic), | ||||
&(comm->header_iv_ctx_dynamic)); | ||||
traceEvent(TRACE_DEBUG, "calculated dynamic key for community '%s'", | ||||
comm->community); | ||||
} | ||||
} | ||||
} | ||||
// send RE_REGISTER_SUPER to all edges from user/pw auth'ed communites | ||||
void send_re_register_super (n2n_sn_t *sss) { | ||||
struct sn_community *comm, *tmp_comm = NULL; | ||||
struct peer_info *edge, *tmp_edge = NULL; | ||||
n2n_common_t cmn; | ||||
uint8_t rereg_buf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
n2n_sock_str_t sockbuf; | ||||
HASH_ITER(hh, sss->communities, comm, tmp_comm) { | ||||
if(comm->is_federation) { | ||||
continue; | ||||
} | ||||
// send RE_REGISTER_SUPER to edges if this is a user/pw auth community | ||||
if(comm->allowed_users) { | ||||
// prepare | ||||
cmn.ttl = N2N_DEFAULT_TTL; | ||||
cmn.pc = n2n_re_register_super; | ||||
cmn.flags = N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn.community, comm->community, N2N_COMMUNITY_SIZE); | ||||
HASH_ITER(hh, comm->edges, edge, tmp_edge) { | ||||
// encode | ||||
encx = 0; | ||||
encode_common(rereg_buf, &encx, &cmn); | ||||
// send | ||||
traceEvent(TRACE_DEBUG, "send RE_REGISTER_SUPER to %s", | ||||
sock_to_cstr(sockbuf, &(edge->sock))); | ||||
packet_header_encrypt(rereg_buf, encx, encx, | ||||
comm->header_encryption_ctx_dynamic, comm- | ||||
>header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
/* sent = */ sendto_peer(sss, edge, rereg_buf, encx); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
/** Load the list of allowed communities. Existing/previous ones will be removed | ||||
, | ||||
* return 0 on success, -1 if file not found, -2 if no valid entries found | ||||
*/ | ||||
int load_allowed_sn_community (n2n_sn_t *sss) { | ||||
char buffer[4096], *line, *cmn_str, net_str[20], format[20]; | ||||
sn_user_t *user, *tmp_user; | ||||
n2n_desc_t username; | ||||
n2n_private_public_key_t public_key; | ||||
char ascii_public_key[(N2N_PRIVATE_PUBLIC_KEY_SIZE * 8 + 5) / 6 + 1]; | ||||
dec_ip_str_t ip_str = {'\0'}; | ||||
uint8_t bitlen; | ||||
in_addr_t net; | ||||
uint32_t mask; | ||||
FILE *fd = fopen(sss->community_file, "r"); | ||||
struct sn_community *comm, *tmp_comm, *last_added_comm = NULL; | ||||
struct peer_info *edge, *tmp_edge; | ||||
node_supernode_association_t *assoc, *tmp_assoc; | ||||
n2n_tcp_connection_t *conn; | ||||
time_t any_time = 0; | ||||
uint32_t num_communities = 0; | ||||
struct sn_community_regular_expression *re, *tmp_re; | ||||
uint32_t num_regex = 0; | ||||
int has_net; | ||||
if(fd == NULL) { | ||||
traceEvent(TRACE_WARNING, "File %s not found", sss->community_file); | ||||
return -1; | ||||
} | ||||
// reset data structures ------------------------------ | ||||
// send RE_REGISTER_SUPER to all edges from user/pw auth communites, this is | ||||
safe because | ||||
// follow-up REGISTER_SUPER cannot be handled before this function ends | ||||
send_re_register_super(sss); | ||||
// remove communities (not: federation) | ||||
HASH_ITER(hh, sss->communities, comm, tmp_comm) { | ||||
if(comm->is_federation) { | ||||
continue; | ||||
} | ||||
// remove all edges from community | ||||
HASH_ITER(hh, comm->edges, edge, tmp_edge) { | ||||
// remove all edge associations (with other supernodes) | ||||
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) { | ||||
HASH_DEL(comm->assoc, assoc); | ||||
free(assoc); | ||||
} | ||||
// close TCP connections, if any (also causes reconnect) | ||||
// and delete edge from list | ||||
if((edge->socket_fd != sss->sock) && (edge->socket_fd >= 0)) { | ||||
HASH_FIND_INT(sss->tcp_connections, &(edge->socket_fd), conn); | ||||
close_tcp_connection(sss, conn); /* also deletes the edge */ | ||||
} else { | ||||
HASH_DEL(comm->edges, edge); | ||||
free(edge); | ||||
} | ||||
} | ||||
// remove allowed users from community | ||||
HASH_ITER(hh, comm->allowed_users, user, tmp_user) { | ||||
free(user->shared_secret_ctx); | ||||
HASH_DEL(comm->allowed_users, user); | ||||
free(user); | ||||
} | ||||
// remove community | ||||
HASH_DEL(sss->communities, comm); | ||||
if(NULL != comm->header_encryption_ctx_static) { | ||||
// remove header encryption keys | ||||
free(comm->header_encryption_ctx_static); | ||||
free(comm->header_iv_ctx_static); | ||||
free(comm->header_encryption_ctx_dynamic); | ||||
free(comm->header_iv_ctx_dynamic); | ||||
} | ||||
free(comm); | ||||
} | ||||
// remove all regular expressions for allowed communities | ||||
HASH_ITER(hh, sss->rules, re, tmp_re) { | ||||
HASH_DEL(sss->rules, re); | ||||
free(re); | ||||
} | ||||
// prepare reading data ------------------------------- | ||||
// new key_time for all communities, requires dynamic keys to be recalculate | ||||
d (see further below), | ||||
// and edges to re-register (see above) and ... | ||||
sss->dynamic_key_time = time(NULL); | ||||
// ... federated supernodes to re-register | ||||
re_register_and_purge_supernodes(sss, sss->federation, &any_time, any_time, | ||||
1 /* forced */); | ||||
// format definition for possible user-key entries | ||||
sprintf(format, "%c %%%ds %%%lds", N2N_USER_KEY_LINE_STARTER, N2N_DESC_SIZE | ||||
- 1, sizeof(ascii_public_key)-1); | ||||
while((line = fgets(buffer, sizeof(buffer), fd)) != NULL) { | ||||
int len = strlen(line); | ||||
if((len < 2) || line[0] == '#') { | ||||
continue; | ||||
} | ||||
len--; | ||||
while(len > 0) { | ||||
if((line[len] == '\n') || (line[len] == '\r')) { | ||||
line[len] = '\0'; | ||||
len--; | ||||
} else { | ||||
break; | ||||
} | ||||
} | ||||
// the loop above does not always determine correct 'len' | ||||
len = strlen(line); | ||||
// user-key line for edge authentication? | ||||
if(line[0] == N2N_USER_KEY_LINE_STARTER) { /* special first character */ | ||||
if(sscanf(line, format, username, ascii_public_key) == 2) { /* corre | ||||
ct format */ | ||||
if(last_added_comm) { /* is there a valid community to add users | ||||
to */ | ||||
user = (sn_user_t*)calloc(1, sizeof(sn_user_t)); | ||||
if(user) { | ||||
// username | ||||
memcpy(user->name, username, sizeof(username)); | ||||
// public key | ||||
ascii_to_bin(public_key, ascii_public_key); | ||||
memcpy(user->public_key, public_key, sizeof(public_key)) | ||||
; | ||||
// common shared secret will be calculated later | ||||
// add to list | ||||
HASH_ADD(hh, last_added_comm->allowed_users, public_key, | ||||
sizeof(n2n_private_public_key_t), user); | ||||
traceEvent(TRACE_INFO, "added user '%s' with public key | ||||
'%s' to community '%s'", | ||||
user->name, ascii_public_key, las | ||||
t_added_comm->community); | ||||
// enable header encryption | ||||
last_added_comm->header_encryption = HEADER_ENCRYPTION_E | ||||
NABLED; | ||||
packet_header_setup_key(last_added_comm->community, | ||||
&(last_added_comm->header_encryp | ||||
tion_ctx_static), | ||||
&(last_added_comm->header_encryp | ||||
tion_ctx_dynamic), | ||||
&(last_added_comm->header_iv_ctx | ||||
_static), | ||||
&(last_added_comm->header_iv_ctx | ||||
_dynamic)); | ||||
// dynamic key setup follows at a later point in code | ||||
} | ||||
continue; | ||||
} | ||||
} | ||||
} | ||||
// --- community name or regular expression | ||||
// cut off any IP sub-network upfront | ||||
cmn_str = (char*)calloc(len + 1, sizeof(char)); | ||||
has_net = (sscanf(line, "%s %s", cmn_str, net_str) == 2); | ||||
// if it contains typical characters... | ||||
if(NULL != strpbrk(cmn_str, ".*+?[]\\")) { | ||||
// ...it is treated as regular expression | ||||
re = (struct sn_community_regular_expression*)calloc(1, sizeof(struc | ||||
t sn_community_regular_expression)); | ||||
if(re) { | ||||
re->rule = re_compile(cmn_str); | ||||
HASH_ADD_PTR(sss->rules, rule, re); | ||||
num_regex++; | ||||
traceEvent(TRACE_INFO, "added regular expression for allowed com | ||||
munities '%s'", cmn_str); | ||||
free(cmn_str); | ||||
last_added_comm = NULL; | ||||
continue; | ||||
} | ||||
} | ||||
comm = (struct sn_community*)calloc(1,sizeof(struct sn_community)); | ||||
if(comm != NULL) { | ||||
comm_init(comm, cmn_str); | ||||
/* loaded from file, this community is unpurgeable */ | ||||
comm->purgeable = COMMUNITY_UNPURGEABLE; | ||||
/* we do not know if header encryption is used in this community, | ||||
* first packet will show. just in case, setup the key. */ | ||||
comm->header_encryption = HEADER_ENCRYPTION_UNKNOWN; | ||||
packet_header_setup_key(comm->community, | ||||
&(comm->header_encryption_ctx_static), | ||||
&(comm->header_encryption_ctx_dynamic), | ||||
&(comm->header_iv_ctx_static), | ||||
&(comm->header_iv_ctx_dynamic)); | ||||
HASH_ADD_STR(sss->communities, community, comm); | ||||
last_added_comm = comm; | ||||
num_communities++; | ||||
traceEvent(TRACE_INFO, "added allowed community '%s' [total: %u]", | ||||
(char*)comm->community, num_communities); | ||||
// check for sub-network address | ||||
if(has_net) { | ||||
if(sscanf(net_str, "%15[^/]/%hhu", ip_str, &bitlen) != 2) { | ||||
traceEvent(TRACE_WARNING, "bad net/bit format '%s' for commu | ||||
nity '%c', ignoring; see comments inside community.list file", | ||||
net_str, cmn_str); | ||||
has_net = 0; | ||||
} | ||||
net = inet_addr(ip_str); | ||||
mask = bitlen2mask(bitlen); | ||||
if((net == (in_addr_t)(-1)) || (net == INADDR_NONE) || (net == I | ||||
NADDR_ANY) | ||||
|| ((ntohl(net) & ~mask) != 0)) { | ||||
traceEvent(TRACE_WARNING, "bad network '%s/%u' in '%s' for c | ||||
ommunity '%s', ignoring", | ||||
ip_str, bitlen, net_str, cmn_str); | ||||
has_net = 0; | ||||
} | ||||
if((bitlen > 30) || (bitlen == 0)) { | ||||
traceEvent(TRACE_WARNING, "bad prefix '%hhu' in '%s' for com | ||||
munity '%s', ignoring", | ||||
bitlen, net_str, cmn_str); | ||||
has_net = 0; | ||||
} | ||||
} | ||||
if(has_net) { | ||||
comm->auto_ip_net.net_addr = ntohl(net); | ||||
comm->auto_ip_net.net_bitlen = bitlen; | ||||
traceEvent(TRACE_INFO, "assigned sub-network %s/%u to community | ||||
'%s'", | ||||
inet_ntoa(*(struct in_addr *) &net), | ||||
comm->auto_ip_net.net_bitlen, | ||||
comm->community); | ||||
} else { | ||||
assign_one_ip_subnet(sss, comm); | ||||
} | ||||
} | ||||
free(cmn_str); | ||||
} | ||||
fclose(fd); | ||||
if((num_regex + num_communities) == 0) { | ||||
traceEvent(TRACE_WARNING, "file %s does not contain any valid community | ||||
names or regular expressions", sss->community_file); | ||||
return -2; | ||||
} | ||||
traceEvent(TRACE_NORMAL, "loaded %u fixed-name communities from %s", | ||||
num_communities, sss->community_file); | ||||
traceEvent(TRACE_NORMAL, "loaded %u regular expressions for community name m | ||||
atching from %s", | ||||
num_regex, sss->community_file); | ||||
/* Not a known MAC so drop. */ | // calculate allowed user's shared secrets (shared with federation) | |||
return(-2); | calculate_shared_secrets(sss); | |||
// calculcate communties' dynamic keys | ||||
calculate_dynamic_keys(sss); | ||||
// no new communities will be allowed | ||||
sss->lock_communities = 1; | ||||
return 0; | ||||
} | ||||
/* *************************************************** */ | ||||
/** Send a datagram to a file descriptor socket. | ||||
* | ||||
* @return -1 on error otherwise number of bytes sent | ||||
*/ | ||||
static ssize_t sendto_fd (n2n_sn_t *sss, | ||||
SOCKET socket_fd, | ||||
const struct sockaddr *socket, | ||||
const uint8_t *pktbuf, | ||||
size_t pktsize) { | ||||
ssize_t sent = 0; | ||||
n2n_tcp_connection_t *conn; | ||||
sent = sendto(socket_fd, (void *)pktbuf, pktsize, 0 /* flags */, | ||||
socket, sizeof(struct sockaddr_in)); | ||||
if((sent <= 0) && (errno)) { | ||||
char * c = strerror(errno); | ||||
traceEvent(TRACE_ERROR, "sendto failed (%d) %s", errno, c); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastError()); | ||||
#endif | ||||
// if the erroneous connection is tcp, i.e. not the regular sock... | ||||
if((socket_fd >= 0) && (socket_fd != sss->sock)) { | ||||
// ...forget about the corresponding peer and the connection | ||||
HASH_FIND_INT(sss->tcp_connections, &socket_fd, conn); | ||||
close_tcp_connection(sss, conn); | ||||
return -1; | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "sendto sent=%d to ", (signed int)sent); | ||||
} | } | |||
return(0); | return sent; | |||
} | } | |||
/** Send a datagram to the destination embodied in a n2n_sock_t. | /** Send a datagram to a network order socket of type struct sockaddr. | |||
* | * | |||
* @return -1 on error otherwise number of bytes sent | * @return -1 on error otherwise number of bytes sent | |||
*/ | */ | |||
static ssize_t sendto_sock(n2n_sn_t *sss, | static ssize_t sendto_sock(n2n_sn_t *sss, | |||
const n2n_sock_t *sock, | SOCKET socket_fd, | |||
const struct sockaddr *socket, | ||||
const uint8_t *pktbuf, | const uint8_t *pktbuf, | |||
size_t pktsize) | size_t pktsize) { | |||
{ | ||||
ssize_t sent = 0; | ||||
int value = 0; | ||||
// if the connection is tcp, i.e. not the regular sock... | ||||
if((socket_fd >= 0) && (socket_fd != sss->sock)) { | ||||
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); | ||||
value = 1; | ||||
#ifdef LINUX | ||||
setsockopt(socket_fd, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); | ||||
#endif | ||||
// prepend packet length... | ||||
uint16_t pktsize16 = htobe16(pktsize); | ||||
sent = sendto_fd(sss, socket_fd, socket, (uint8_t*)&pktsize16, sizeof(pk | ||||
tsize16)); | ||||
if(sent <= 0) | ||||
return -1; | ||||
// ...before sending the actual data | ||||
} | ||||
sent = sendto_fd(sss, socket_fd, socket, pktbuf, pktsize); | ||||
// if the connection is tcp, i.e. not the regular sock... | ||||
if((socket_fd >= 0) && (socket_fd != sss->sock)) { | ||||
value = 1; /* value should still be set to 1 */ | ||||
setsockopt(socket_fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); | ||||
#ifdef LINUX | ||||
value = 0; | ||||
setsockopt(socket_fd, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)); | ||||
#endif | ||||
} | ||||
return sent; | ||||
} | ||||
/** Send a datagram to a peer whose destination socket is embodied in its sock f | ||||
ield of type n2n_sock_t. | ||||
* It calls sendto_sock to do the final send. | ||||
* | ||||
* @return -1 on error otherwise number of bytes sent | ||||
*/ | ||||
static ssize_t sendto_peer (n2n_sn_t *sss, | ||||
const struct peer_info *peer, | ||||
const uint8_t *pktbuf, | ||||
size_t pktsize) { | ||||
n2n_sock_str_t sockbuf; | n2n_sock_str_t sockbuf; | |||
if (AF_INET == sock->family) | if(AF_INET == peer->sock.family) { | |||
{ | ||||
struct sockaddr_in udpsock; | // network order socket | |||
struct sockaddr_in socket; | ||||
udpsock.sin_family = AF_INET; | fill_sockaddr((struct sockaddr *)&socket, sizeof(socket), &(peer->sock)) | |||
udpsock.sin_port = htons(sock->port); | ; | |||
memcpy(&(udpsock.sin_addr.s_addr), &(sock->addr.v4), IPV4_SIZE); | ||||
traceEvent(TRACE_DEBUG, "sendto_sock %lu to [%s]", | traceEvent(TRACE_DEBUG, "sent %lu bytes to [%s]", | |||
pktsize, | pktsize, | |||
sock_to_cstr(sockbuf, sock)); | sock_to_cstr(sockbuf, &(peer->sock))); | |||
return sendto(sss->sock, pktbuf, pktsize, 0, | return sendto_sock(sss, | |||
(const struct sockaddr *)&udpsock, sizeof(struct sockaddr_ | (peer->socket_fd >= 0) ? peer->socket_fd : sss->sock, | |||
in)); | (const struct sockaddr*)&socket, pktbuf, pktsize); | |||
} | } else { | |||
else | ||||
{ | ||||
/* AF_INET6 not implemented */ | /* AF_INET6 not implemented */ | |||
errno = EAFNOSUPPORT; | errno = EAFNOSUPPORT; | |||
return -1; | return -1; | |||
} | } | |||
} | } | |||
/** Try and broadcast a message to all edges in the community. | /** Try and broadcast a message to all edges in the community. | |||
* | * | |||
* This will send the exact same datagram to zero or more edges registered to | * This will send the exact same datagram to zero or more edges registered to | |||
* the supernode. | * the supernode. | |||
*/ | */ | |||
static int try_broadcast(n2n_sn_t * sss, | static int try_broadcast (n2n_sn_t * sss, | |||
const struct sn_community *comm, | const struct sn_community *comm, | |||
const n2n_common_t * cmn, | const n2n_common_t * cmn, | |||
const n2n_mac_t srcMac, | const n2n_mac_t srcMac, | |||
const uint8_t * pktbuf, | uint8_t from_supernode, | |||
size_t pktsize) | const uint8_t * pktbuf, | |||
{ | size_t pktsize) { | |||
struct peer_info *scan, *tmp; | ||||
macstr_t mac_buf; | struct peer_info *scan, *tmp; | |||
n2n_sock_str_t sockbuf; | macstr_t mac_buf; | |||
n2n_sock_str_t sockbuf; | ||||
traceEvent(TRACE_DEBUG, "try_broadcast"); | ||||
traceEvent(TRACE_DEBUG, "try_broadcast"); | ||||
HASH_ITER(hh, comm->edges, scan, tmp) { | ||||
if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) { | /* We have to make sure that a broadcast reaches the other supernodes and ed | |||
/* REVISIT: exclude if the destination socket is where the packet came fro | ges | |||
m. */ | * connected to them. try_broadcast needs a from_supernode parameter: if set | |||
int data_sent_len; | , | |||
* do forward to edges of community only. If unset, forward to all locally k | ||||
nown | ||||
* nodes of community AND all supernodes associated with the community */ | ||||
if (!from_supernode) { | ||||
HASH_ITER(hh, sss->federation->edges, scan, tmp) { | ||||
int data_sent_len; | ||||
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize); | ||||
if(data_sent_len != pktsize) { | ||||
++(sss->stats.errors); | ||||
traceEvent(TRACE_WARNING, "multicast %lu to supernode [%s] %s fa | ||||
iled %s", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr), | ||||
strerror(errno)); | ||||
} else { | ||||
++(sss->stats.broadcast); | ||||
traceEvent(TRACE_DEBUG, "multicast %lu to supernode [%s] %s", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr)); | ||||
} | ||||
} | ||||
} | ||||
data_sent_len = sendto_sock(sss, &(scan->sock), pktbuf, pktsize); | if(comm) { | |||
HASH_ITER(hh, comm->edges, scan, tmp) { | ||||
if(memcmp(srcMac, scan->mac_addr, sizeof(n2n_mac_t)) != 0) { | ||||
/* REVISIT: exclude if the destination socket is where the packe | ||||
t came from. */ | ||||
int data_sent_len; | ||||
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize); | ||||
if(data_sent_len != pktsize) { | ||||
++(sss->stats.errors); | ||||
traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed % | ||||
s", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr), | ||||
strerror(errno)); | ||||
} else { | ||||
++(sss->stats.broadcast); | ||||
traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr)); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
if(data_sent_len != pktsize) | return 0; | |||
{ | } | |||
++(sss->stats.errors); | ||||
traceEvent(TRACE_WARNING, "multicast %lu to [%s] %s failed %s", | static int try_forward (n2n_sn_t * sss, | |||
pktsize, | const struct sn_community *comm, | |||
sock_to_cstr(sockbuf, &(scan->sock)), | const n2n_common_t * cmn, | |||
macaddr_str(mac_buf, scan->mac_addr), | const n2n_mac_t dstMac, | |||
strerror(errno)); | uint8_t from_supernode, | |||
} | const uint8_t * pktbuf, | |||
else | size_t pktsize) { | |||
{ | ||||
++(sss->stats.broadcast); | struct peer_info * scan; | |||
traceEvent(TRACE_DEBUG, "multicast %lu to [%s] %s", | node_supernode_association_t *assoc; | |||
pktsize, | macstr_t mac_buf; | |||
sock_to_cstr(sockbuf, &(scan->sock)), | n2n_sock_str_t sockbuf; | |||
macaddr_str(mac_buf, scan->mac_addr)); | ||||
} | HASH_FIND_PEER(comm->edges, dstMac, scan); | |||
if(NULL != scan) { | ||||
int data_sent_len; | ||||
data_sent_len = sendto_peer(sss, scan, pktbuf, pktsize); | ||||
if(data_sent_len == pktsize) { | ||||
++(sss->stats.fwd); | ||||
traceEvent(TRACE_DEBUG, "unicast %lu to [%s] %s", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr)); | ||||
} else { | ||||
++(sss->stats.errors); | ||||
traceEvent(TRACE_ERROR, "unicast %lu to [%s] %s FAILED (%d: %s)", | ||||
pktsize, | ||||
sock_to_cstr(sockbuf, &(scan->sock)), | ||||
macaddr_str(mac_buf, scan->mac_addr), | ||||
errno, strerror(errno)); | ||||
return -1; | ||||
} | ||||
} else { | ||||
if(!from_supernode) { | ||||
// check if target edge is associated with a certain supernode | ||||
HASH_FIND(hh, comm->assoc, dstMac, sizeof(n2n_mac_t), assoc); | ||||
if(assoc) { | ||||
traceEvent(TRACE_DEBUG, "found mac address associated with a kno | ||||
wn supernode, forwarding packet to that supernode"); | ||||
sendto_sock(sss, sss->sock, | ||||
(const struct sockaddr*)&(assoc->sock), | ||||
pktbuf, pktsize); | ||||
} else { | ||||
// forwarding packet to all federated supernodes | ||||
traceEvent(TRACE_DEBUG, "unknown mac address, broadcasting packe | ||||
t to all federated supernodes"); | ||||
try_broadcast(sss, NULL, cmn, sss->mac_addr, from_supernode, pkt | ||||
buf, pktsize); | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "unknown mac address in packet from a supern | ||||
ode, dropping the packet"); | ||||
/* Not a known MAC so drop. */ | ||||
return -2; | ||||
} | ||||
} | } | |||
} | ||||
return 0; | return 0; | |||
} | ||||
/** Initialise some fields of the community structure **/ | ||||
int comm_init (struct sn_community *comm, char *cmn) { | ||||
strncpy((char*)comm->community, cmn, N2N_COMMUNITY_SIZE); | ||||
comm->community[N2N_COMMUNITY_SIZE - 1] = '\0'; | ||||
comm->is_federation = IS_NO_FEDERATION; | ||||
return 0; /* OK */ | ||||
} | } | |||
/** Initialise the supernode structure */ | /** Initialise the supernode structure */ | |||
int sn_init(n2n_sn_t *sss) | int sn_init_defaults (n2n_sn_t *sss) { | |||
{ | ||||
char *tmp_string; | ||||
#ifdef WIN32 | #ifdef WIN32 | |||
initWin32(); | initWin32(); | |||
#endif | #endif | |||
pearson_hash_init(); | pearson_hash_init(); | |||
memset(sss, 0, sizeof(n2n_sn_t)); | memset(sss, 0, sizeof(n2n_sn_t)); | |||
strncpy(sss->version, GIT_RELEASE, sizeof(n2n_version_t)); | ||||
sss->version[sizeof(n2n_version_t) - 1] = '\0'; | ||||
sss->daemon = 1; /* By defult run as a daemon. */ | sss->daemon = 1; /* By defult run as a daemon. */ | |||
sss->lport = N2N_SN_LPORT_DEFAULT; | sss->lport = N2N_SN_LPORT_DEFAULT; | |||
sss->mport = N2N_SN_MGMT_PORT; | sss->mport = N2N_SN_MGMT_PORT; | |||
sss->sock = -1; | sss->sock = -1; | |||
sss->mgmt_sock = -1; | sss->mgmt_sock = -1; | |||
sss->min_auto_ip_net.net_addr = inet_addr(N2N_SN_MIN_AUTO_IP_NET_DEFAULT); | ||||
sss->min_auto_ip_net.net_addr = ntohl(sss->min_auto_ip_net.net_addr); | ||||
sss->min_auto_ip_net.net_bitlen = N2N_SN_AUTO_IP_NET_BIT_DEFAULT; | ||||
sss->max_auto_ip_net.net_addr = inet_addr(N2N_SN_MAX_AUTO_IP_NET_DEFAULT); | ||||
sss->max_auto_ip_net.net_addr = ntohl(sss->max_auto_ip_net.net_addr); | ||||
sss->max_auto_ip_net.net_bitlen = N2N_SN_AUTO_IP_NET_BIT_DEFAULT; | ||||
sss->federation = (struct sn_community *)calloc(1, sizeof(struct sn_communit | ||||
y)); | ||||
/* Initialize the federation */ | ||||
if(sss->federation) { | ||||
if(getenv("N2N_FEDERATION")) | ||||
snprintf(sss->federation->community, N2N_COMMUNITY_SIZE - 1 ,"*%s", | ||||
getenv("N2N_FEDERATION")); | ||||
else | ||||
strncpy(sss->federation->community, (char*)FEDERATION_NAME, N2N_COMM | ||||
UNITY_SIZE); | ||||
sss->federation->community[N2N_COMMUNITY_SIZE - 1] = '\0'; | ||||
/* enable the flag for federation */ | ||||
sss->federation->is_federation = IS_FEDERATION; | ||||
sss->federation->purgeable = COMMUNITY_UNPURGEABLE; | ||||
/* header encryption enabled by default */ | ||||
sss->federation->header_encryption = HEADER_ENCRYPTION_ENABLED; | ||||
/*setup the encryption key */ | ||||
packet_header_setup_key(sss->federation->community, | ||||
&(sss->federation->header_encryption_ctx_static) | ||||
, | ||||
&(sss->federation->header_encryption_ctx_dynamic | ||||
), | ||||
&(sss->federation->header_iv_ctx_static), | ||||
&(sss->federation->header_iv_ctx_dynamic)); | ||||
sss->federation->edges = NULL; | ||||
} | ||||
n2n_srand(n2n_seed()); | ||||
/* Random auth token */ | ||||
sss->auth.scheme = n2n_auth_simple_id; | ||||
memrnd(sss->auth.token, N2N_AUTH_ID_TOKEN_SIZE); | ||||
sss->auth.token_size = N2N_AUTH_ID_TOKEN_SIZE; | ||||
/* Random MAC address */ | ||||
memrnd(sss->mac_addr, N2N_MAC_SIZE); | ||||
sss->mac_addr[0] &= ~0x01; /* Clear multicast bit */ | ||||
sss->mac_addr[0] |= 0x02; /* Set locally-assigned bit */ | ||||
tmp_string = calloc(1, strlen(N2N_MGMT_PASSWORD) + 1); | ||||
if(tmp_string) { | ||||
strncpy((char*)tmp_string, N2N_MGMT_PASSWORD, strlen(N2N_MGMT_PASSWORD) | ||||
+ 1); | ||||
sss->mgmt_password_hash = pearson_hash_64((uint8_t*)tmp_string, strlen(N | ||||
2N_MGMT_PASSWORD)); | ||||
free(tmp_string); | ||||
} | ||||
return 0; /* OK */ | return 0; /* OK */ | |||
} | } | |||
/** Initialise the supernode */ | ||||
void sn_init (n2n_sn_t *sss) { | ||||
if(resolve_create_thread(&(sss->resolve_parameter), sss->federation->edges) | ||||
== 0) { | ||||
traceEvent(TRACE_NORMAL, "successfully created resolver thread"); | ||||
} | ||||
} | ||||
/** Deinitialise the supernode structure and deallocate any memory owned by | /** Deinitialise the supernode structure and deallocate any memory owned by | |||
* it. */ | * it. */ | |||
void sn_term(n2n_sn_t *sss) | void sn_term (n2n_sn_t *sss) { | |||
{ | ||||
struct sn_community *community, *tmp; | struct sn_community *community, *tmp; | |||
struct sn_community_regular_expression *re, *tmp_re; | ||||
n2n_tcp_connection_t *conn, *tmp_conn; | ||||
node_supernode_association_t *assoc, *tmp_assoc; | ||||
if (sss->sock >= 0) | resolve_cancel_thread(sss->resolve_parameter); | |||
{ | ||||
if(sss->sock >= 0) { | ||||
closesocket(sss->sock); | closesocket(sss->sock); | |||
} | } | |||
sss->sock = -1; | sss->sock = -1; | |||
if (sss->mgmt_sock >= 0) | HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) { | |||
{ | shutdown(conn->socket_fd, SHUT_RDWR); | |||
closesocket(conn->socket_fd); | ||||
HASH_DEL(sss->tcp_connections, conn); | ||||
free(conn); | ||||
} | ||||
if(sss->tcp_sock >= 0) { | ||||
shutdown(sss->tcp_sock, SHUT_RDWR); | ||||
closesocket(sss->tcp_sock); | ||||
} | ||||
sss->tcp_sock = -1; | ||||
if(sss->mgmt_sock >= 0) { | ||||
closesocket(sss->mgmt_sock); | closesocket(sss->mgmt_sock); | |||
} | } | |||
sss->mgmt_sock = -1; | sss->mgmt_sock = -1; | |||
HASH_ITER(hh, sss->communities, community, tmp) | HASH_ITER(hh, sss->communities, community, tmp) { | |||
{ | ||||
clear_peer_list(&community->edges); | clear_peer_list(&community->edges); | |||
if (NULL != community->header_encryption_ctx) | if(NULL != community->header_encryption_ctx_static) { | |||
free (community->header_encryption_ctx); | free(community->header_encryption_ctx_static); | |||
free(community->header_encryption_ctx_dynamic); | ||||
} | ||||
// remove all associations | ||||
HASH_ITER(hh, community->assoc, assoc, tmp_assoc) { | ||||
HASH_DEL(community->assoc, assoc); | ||||
free(assoc); | ||||
} | ||||
HASH_DEL(sss->communities, community); | HASH_DEL(sss->communities, community); | |||
free(community); | free(community); | |||
} | } | |||
HASH_ITER(hh, sss->rules, re, tmp_re) { | ||||
HASH_DEL(sss->rules, re); | ||||
if (NULL != re->rule) { | ||||
free(re->rule); | ||||
} | ||||
free(re); | ||||
} | ||||
if(sss->community_file) | ||||
free(sss->community_file); | ||||
#ifdef WIN32 | ||||
destroyWin32(); | ||||
#endif | ||||
} | ||||
void update_node_supernode_association (struct sn_community *comm, | ||||
n2n_mac_t *edgeMac, const struct sockadd | ||||
r_in *sender_sock, | ||||
time_t now) { | ||||
node_supernode_association_t *assoc; | ||||
HASH_FIND(hh, comm->assoc, edgeMac, sizeof(n2n_mac_t), assoc); | ||||
if(!assoc) { | ||||
// create a new association | ||||
assoc = (node_supernode_association_t*)malloc(sizeof(node_supernode_asso | ||||
ciation_t)); | ||||
if(assoc) { | ||||
memcpy(&(assoc->mac), edgeMac, sizeof(n2n_mac_t)); | ||||
memcpy((struct sockaddr_in*)&(assoc->sock), sender_sock, sizeof(stru | ||||
ct sockaddr_in)); | ||||
assoc->last_seen = now; | ||||
HASH_ADD(hh, comm->assoc, mac, sizeof(n2n_mac_t), assoc); | ||||
} else { | ||||
// already there, update socket and time only | ||||
memcpy((struct sockaddr_in*)&(assoc->sock), sender_sock, sizeof(stru | ||||
ct sockaddr_in)); | ||||
assoc->last_seen = now; | ||||
} | ||||
} | ||||
} | } | |||
/** Determine the appropriate lifetime for new registrations. | /** Determine the appropriate lifetime for new registrations. | |||
* | * | |||
* If the supernode has been put into a pre-shutdown phase then this lifetime | * If the supernode has been put into a pre-shutdown phase then this lifetime | |||
* should not allow registrations to continue beyond the shutdown point. | * should not allow registrations to continue beyond the shutdown point. | |||
*/ | */ | |||
static uint16_t reg_lifetime(n2n_sn_t *sss) | static uint16_t reg_lifetime (n2n_sn_t *sss) { | |||
{ | ||||
/* NOTE: UDP firewalls usually have a 30 seconds timeout */ | /* NOTE: UDP firewalls usually have a 30 seconds timeout */ | |||
return 15; | return 15; | |||
} | } | |||
/** Verifies authentication tokens from known edges. | ||||
* | ||||
* It is called by update_edge and during UNREGISTER_SUPER handling | ||||
* to verify the stored auth token. | ||||
*/ | ||||
static int auth_edge (const n2n_auth_t *present, const n2n_auth_t *presented, n2 | ||||
n_auth_t *answer, struct sn_community *community) { | ||||
sn_user_t *user = NULL; | ||||
if(present->scheme == n2n_auth_none) { | ||||
// n2n_auth_none scheme (set at supernode if cli option '-M') | ||||
// if required, zero_token answer (not for NAK) | ||||
if(answer) | ||||
memset(answer, 0, sizeof(n2n_auth_t)); | ||||
// 0 == (always) successful | ||||
return 0; | ||||
} | ||||
if((present->scheme == n2n_auth_simple_id) && (presented->scheme == n2n_auth | ||||
_simple_id)) { | ||||
// n2n_auth_simple_id scheme: if required, zero_token answer (not for NA | ||||
K) | ||||
if(answer) | ||||
memset(answer, 0, sizeof(n2n_auth_t)); | ||||
// 0 = success (tokens are equal) | ||||
return (memcmp(present, presented, sizeof(n2n_auth_t))); | ||||
} | ||||
if((present->scheme == n2n_auth_user_password) && (presented->scheme == n2n_ | ||||
auth_user_password)) { | ||||
// check if submitted public key is in list of allowed users | ||||
HASH_FIND(hh, community->allowed_users, &presented->token, sizeof(n2n_pr | ||||
ivate_public_key_t), user); | ||||
if(user) { | ||||
if(answer) { | ||||
memcpy(answer, presented, sizeof(n2n_auth_t)); | ||||
// return a double-encrypted challenge (just encrypt again) in t | ||||
he (first half of) public key field so edge can verify | ||||
memcpy(answer->token, answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZ | ||||
E, N2N_AUTH_CHALLENGE_SIZE); | ||||
speck_128_encrypt(answer->token, (speck_context_t*)user->shared_ | ||||
secret_ctx); | ||||
// decrypt the challenge using user's shared secret | ||||
speck_128_decrypt(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, ( | ||||
speck_context_t*)user->shared_secret_ctx); | ||||
// xor-in the community dynamic key | ||||
memxor(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, community->d | ||||
ynamic_key, N2N_AUTH_CHALLENGE_SIZE); | ||||
// xor-in the user's shared secret | ||||
memxor(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, user->shared | ||||
_secret, N2N_AUTH_CHALLENGE_SIZE); | ||||
// encrypt it using user's shared secret | ||||
speck_128_encrypt(answer->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, ( | ||||
speck_context_t*)user->shared_secret_ctx); | ||||
// user in list? success! (we will see if edge can handle the ke | ||||
y for further com) | ||||
} | ||||
return 0; | ||||
} | ||||
} | ||||
// if not successful earlier: failure | ||||
return -1; | ||||
} | ||||
// provides the current / a new local auth token | ||||
// REVISIT: behavior should depend on some local auth scheme setting (to be impl | ||||
emented) | ||||
static int get_local_auth (n2n_sn_t *sss, n2n_auth_t *auth) { | ||||
// n2n_auth_simple_id scheme | ||||
memcpy(auth, &(sss->auth), sizeof(n2n_auth_t)); | ||||
return 0; | ||||
} | ||||
// handles an incoming (remote) auth token from a so far unknown edge, | ||||
// takes action as required by auth scheme, and | ||||
// could provide an answer auth token for use in REGISTER_SUPER_ACK | ||||
static int handle_remote_auth (n2n_sn_t *sss, const n2n_auth_t *remote_auth, | ||||
n2n_auth_t *answer_auth, | ||||
struct sn_community *community) { | ||||
sn_user_t *user = NULL; | ||||
if((NULL == community->allowed_users) != (remote_auth->scheme != n2n_auth_us | ||||
er_password)) { | ||||
// received token's scheme does not match expected scheme | ||||
return -1; | ||||
} | ||||
switch(remote_auth->scheme) { | ||||
// we do not handle n2n_auth_none because the edge always edge always us | ||||
es either id or user/password | ||||
// auth_none is sn-internal only (skipping MAC/IP address spoofing prote | ||||
ction) | ||||
case n2n_auth_none: | ||||
case n2n_auth_simple_id: | ||||
// zero_token answer | ||||
memset(answer_auth, 0, sizeof(n2n_auth_t)); | ||||
return 0; | ||||
case n2n_auth_user_password: | ||||
// check if submitted public key is in list of allowed users | ||||
HASH_FIND(hh, community->allowed_users, &remote_auth->token, sizeof( | ||||
n2n_private_public_key_t), user); | ||||
if(user) { | ||||
memcpy(answer_auth, remote_auth, sizeof(n2n_auth_t)); | ||||
// return a double-encrypted challenge (just encrypt again) in t | ||||
he (first half of) public key field so edge can verify | ||||
memcpy(answer_auth->token, answer_auth->token + N2N_PRIVATE_PUBL | ||||
IC_KEY_SIZE, N2N_AUTH_CHALLENGE_SIZE); | ||||
speck_128_encrypt(answer_auth->token, (speck_context_t*)user->sh | ||||
ared_secret_ctx); | ||||
// wrap dynamic key for transmission | ||||
// decrypt the challenge using user's shared secret | ||||
speck_128_decrypt(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SI | ||||
ZE, (speck_context_t*)user->shared_secret_ctx); | ||||
// xor-in the community dynamic key | ||||
memxor(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, communi | ||||
ty->dynamic_key, N2N_AUTH_CHALLENGE_SIZE); | ||||
// xor-in the user's shared secret | ||||
memxor(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SIZE, user->s | ||||
hared_secret, N2N_AUTH_CHALLENGE_SIZE); | ||||
// encrypt it using user's shared secret | ||||
speck_128_encrypt(answer_auth->token + N2N_PRIVATE_PUBLIC_KEY_SI | ||||
ZE, (speck_context_t*)user->shared_secret_ctx); | ||||
return 0; | ||||
} | ||||
break; | ||||
default: | ||||
break; | ||||
} | ||||
// if not successful earlier: failure | ||||
return -1; | ||||
} | ||||
/** Update the edge table with the details of the edge which contacted the | /** Update the edge table with the details of the edge which contacted the | |||
* supernode. */ | * supernode. */ | |||
static int update_edge(n2n_sn_t *sss, | static int update_edge (n2n_sn_t *sss, | |||
const n2n_mac_t edgeMac, | const n2n_common_t* cmn, | |||
struct sn_community *comm, | const n2n_REGISTER_SUPER_t* reg, | |||
const n2n_sock_t *sender_sock, | struct sn_community *comm, | |||
time_t now) | const n2n_sock_t *sender_sock, | |||
{ | const SOCKET socket_fd, | |||
n2n_auth_t *answer_auth, | ||||
int skip_add, | ||||
time_t now) { | ||||
macstr_t mac_buf; | macstr_t mac_buf; | |||
n2n_sock_str_t sockbuf; | n2n_sock_str_t sockbuf; | |||
struct peer_info *scan; | struct peer_info *scan, *iter, *tmp; | |||
int ret; | ||||
traceEvent(TRACE_DEBUG, "update_edge for %s [%s]", | traceEvent(TRACE_DEBUG, "update_edge for %s [%s]", | |||
macaddr_str(mac_buf, edgeMac), | macaddr_str(mac_buf, reg->edgeMac), | |||
sock_to_cstr(sockbuf, sender_sock)); | sock_to_cstr(sockbuf, sender_sock)); | |||
HASH_FIND_PEER(comm->edges, edgeMac, scan); | HASH_FIND_PEER(comm->edges, reg->edgeMac, scan); | |||
if (NULL == scan) | // if unknown, make sure it is also not known by IP address | |||
{ | if(NULL == scan) { | |||
/* Not known */ | HASH_ITER(hh,comm->edges,iter,tmp) { | |||
if(iter->dev_addr.net_addr == reg->dev_addr.net_addr) { | ||||
scan = iter; | ||||
HASH_DEL(comm->edges, scan); | ||||
memcpy(scan->mac_addr, reg->edgeMac, sizeof(n2n_mac_t)); | ||||
HASH_ADD_PEER(comm->edges, scan); | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
scan = (struct peer_info *)calloc(1, | if(NULL == scan) { | |||
sizeof(struct peer_info)); /* dealloca | /* Not known */ | |||
ted in purge_expired_registrations */ | if(handle_remote_auth(sss, &(reg->auth), answer_auth, comm) == 0) { | |||
if(skip_add == SN_ADD) { | ||||
scan = (struct peer_info *) calloc(1, sizeof(struct peer_info)); | ||||
/* deallocated in purge_expired_nodes */ | ||||
memcpy(&(scan->mac_addr), reg->edgeMac, sizeof(n2n_mac_t)); | ||||
scan->dev_addr.net_addr = reg->dev_addr.net_addr; | ||||
scan->dev_addr.net_bitlen = reg->dev_addr.net_bitlen; | ||||
memcpy((char*)scan->dev_desc, reg->dev_desc, N2N_DESC_SIZE); | ||||
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); | ||||
scan->socket_fd = socket_fd; | ||||
scan->last_cookie = reg->cookie; | ||||
scan->last_valid_time_stamp = initial_time_stamp(); | ||||
// eventually, store edge's preferred local socket from REGISTER | ||||
_SUPER | ||||
if(cmn->flags & N2N_FLAGS_SOCKET) | ||||
memcpy(&scan->preferred_sock, ®->sock, sizeof(n2n_sock_t) | ||||
); | ||||
else | ||||
scan->preferred_sock.family = AF_INVALID; | ||||
// store the submitted auth token | ||||
memcpy(&(scan->auth), &(reg->auth), sizeof(n2n_auth_t)); | ||||
// manually set to type 'auth_none' if cli option disables MAC/I | ||||
P address spoofing protection | ||||
// for id based auth communities. This will be obsolete when han | ||||
dling public keys only (v4.0?) | ||||
if((reg->auth.scheme == n2n_auth_simple_id) && (sss->override_sp | ||||
oofing_protection)) | ||||
scan->auth.scheme = n2n_auth_none; | ||||
HASH_ADD_PEER(comm->edges, scan); | ||||
traceEvent(TRACE_INFO, "created edge %s ==> %s", | ||||
macaddr_str(mac_buf, reg->edgeMac), | ||||
sock_to_cstr(sockbuf, sender_sock)); | ||||
} | ||||
ret = update_edge_new_sn; | ||||
} else { | ||||
traceEvent(TRACE_INFO, "authentication failed"); | ||||
ret = update_edge_auth_fail; | ||||
} | ||||
} else { | ||||
/* Known */ | ||||
if(auth_edge(&(scan->auth), &(reg->auth), answer_auth, comm) == 0) { | ||||
if(!sock_equal(sender_sock, &(scan->sock))) { | ||||
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); | ||||
scan->socket_fd = socket_fd; | ||||
scan->last_cookie = reg->cookie; | ||||
// eventually, update edge's preferred local socket from REGISTE | ||||
R_SUPER | ||||
if(cmn->flags & N2N_FLAGS_SOCKET) | ||||
memcpy(&scan->preferred_sock, ®->sock, sizeof(n2n_sock_t) | ||||
); | ||||
else | ||||
scan->preferred_sock.family = AF_INVALID; | ||||
traceEvent(TRACE_INFO, "updated edge %s ==> %s", | ||||
macaddr_str(mac_buf, reg->edgeMac), | ||||
sock_to_cstr(sockbuf, sender_sock)); | ||||
ret = update_edge_sock_change; | ||||
} else { | ||||
scan->last_cookie = reg->cookie; | ||||
traceEvent(TRACE_DEBUG, "edge unchanged %s ==> %s", | ||||
macaddr_str(mac_buf, reg->edgeMac), | ||||
sock_to_cstr(sockbuf, sender_sock)); | ||||
memcpy(&(scan->mac_addr), edgeMac, sizeof(n2n_mac_t)); | ret = update_edge_no_change; | |||
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); | } | |||
scan->last_valid_time_stamp = initial_time_stamp (); | } else { | |||
traceEvent(TRACE_INFO, "authentication failed"); | ||||
ret = update_edge_auth_fail; | ||||
} | ||||
} | ||||
if((scan != NULL) && (ret != update_edge_auth_fail)) { | ||||
scan->last_seen = now; | ||||
} | ||||
return ret; | ||||
} | ||||
/** checks if a certain ip address is still available, i.e. not used by any othe | ||||
r edge of a given community */ | ||||
static int ip_addr_available (struct sn_community *comm, n2n_ip_subnet_t *ip_add | ||||
r) { | ||||
HASH_ADD_PEER(comm->edges, scan); | int success = 1; | |||
struct peer_info *peer, *tmp_peer; | ||||
traceEvent(TRACE_INFO, "update_edge created %s ==> %s", | // prerequisite: list of peers is sorted according to peer's tap ip address | |||
macaddr_str(mac_buf, edgeMac), | HASH_ITER(hh, comm->edges, peer, tmp_peer) { | |||
sock_to_cstr(sockbuf, sender_sock)); | if(peer->dev_addr.net_addr > ip_addr->net_addr) { | |||
break; | ||||
} | ||||
if(peer->dev_addr.net_addr == ip_addr->net_addr) { | ||||
success = 0; | ||||
break; | ||||
} | ||||
} | } | |||
else | ||||
{ | return success; | |||
/* Known */ | } | |||
if (!sock_equal(sender_sock, &(scan->sock))) | ||||
{ | static signed int peer_tap_ip_sort (struct peer_info *a, struct peer_info *b) { | |||
memcpy(&(scan->sock), sender_sock, sizeof(n2n_sock_t)); | ||||
uint32_t a_host_id = a->dev_addr.net_addr & (~bitlen2mask(a->dev_addr.net_bi | ||||
traceEvent(TRACE_INFO, "update_edge updated %s ==> %s", | tlen)); | |||
macaddr_str(mac_buf, edgeMac), | uint32_t b_host_id = b->dev_addr.net_addr & (~bitlen2mask(b->dev_addr.net_bi | |||
sock_to_cstr(sockbuf, sender_sock)); | tlen)); | |||
return ((signed int)a_host_id - (signed int)b_host_id); | ||||
} | ||||
/** The IP address assigned to the edge by the auto ip address function of sn. * | ||||
/ | ||||
static int assign_one_ip_addr (struct sn_community *comm, n2n_desc_t dev_desc, n | ||||
2n_ip_subnet_t *ip_addr) { | ||||
uint32_t tmp, success, net_id, mask, max_host, host_id = 1; | ||||
dec_ip_bit_str_t ip_bit_str = {'\0'}; | ||||
mask = bitlen2mask(comm->auto_ip_net.net_bitlen); | ||||
net_id = comm->auto_ip_net.net_addr & mask; | ||||
max_host = ~mask; | ||||
// sorting is a prerequisite for more efficient availabilitiy check | ||||
HASH_SORT(comm->edges, peer_tap_ip_sort); | ||||
// first proposal derived from hash of mac address | ||||
tmp = pearson_hash_32(dev_desc, sizeof(n2n_desc_t)) & max_host; | ||||
if(tmp == 0) tmp++; /* avoid 0 host */ | ||||
if(tmp == max_host) tmp--; /* avoid broadcast address */ | ||||
tmp |= net_id; | ||||
// candidate | ||||
ip_addr->net_bitlen = comm->auto_ip_net.net_bitlen; | ||||
// check for availability starting from proposal, then downwards, ... | ||||
for(host_id = tmp; host_id > net_id; host_id--) { | ||||
ip_addr->net_addr = host_id; | ||||
success = ip_addr_available(comm, ip_addr); | ||||
if(success) { | ||||
break; | ||||
} | } | |||
else | } | |||
{ | // ... then upwards | |||
traceEvent(TRACE_DEBUG, "update_edge unchanged %s ==> %s", | if(!success) { | |||
macaddr_str(mac_buf, edgeMac), | for(host_id = tmp + 1; host_id < (net_id + max_host); host_id++) { | |||
sock_to_cstr(sockbuf, sender_sock)); | ip_addr->net_addr = host_id; | |||
success = ip_addr_available(comm, ip_addr); | ||||
if(success) { | ||||
break; | ||||
} | ||||
} | } | |||
} | } | |||
scan->last_seen = now; | if(success) { | |||
return 0; | traceEvent(TRACE_INFO, "assign IP %s to tap adapter of edge", ip_subnet_ | |||
to_str(ip_bit_str, ip_addr)); | ||||
return 0; | ||||
} else { | ||||
traceEvent(TRACE_WARNING, "no assignable IP to edge tap adapter"); | ||||
return -1; | ||||
} | ||||
} | ||||
/** checks if a certain sub-network is still available, i.e. does not cut any ot | ||||
her community's sub-network */ | ||||
int subnet_available (n2n_sn_t *sss, | ||||
struct sn_community *comm, | ||||
uint32_t net_id, | ||||
uint32_t mask) { | ||||
struct sn_community *cmn, *tmpCmn; | ||||
int success = 1; | ||||
HASH_ITER(hh, sss->communities, cmn, tmpCmn) { | ||||
if(cmn == comm) { | ||||
continue; | ||||
} | ||||
if(cmn->is_federation == IS_FEDERATION) { | ||||
continue; | ||||
} | ||||
if((net_id <= (cmn->auto_ip_net.net_addr + ~bitlen2mask(cmn->auto_ip_net | ||||
.net_bitlen))) | ||||
&&(net_id + ~mask >= cmn->auto_ip_net.net_addr)) { | ||||
success = 0; | ||||
break; | ||||
} | ||||
} | ||||
return success; | ||||
} | ||||
/** The IP address range (subnet) assigned to the community by the auto ip addre | ||||
ss function of sn. */ | ||||
int assign_one_ip_subnet (n2n_sn_t *sss, | ||||
struct sn_community *comm) { | ||||
uint32_t net_id, net_id_i, mask, net_increment; | ||||
uint32_t no_subnets; | ||||
uint8_t success; | ||||
in_addr_t net; | ||||
mask = bitlen2mask(sss->min_auto_ip_net.net_bitlen); | ||||
// number of possible sub-networks | ||||
no_subnets = (sss->max_auto_ip_net.net_addr - sss->min_auto_ip_net.net_add | ||||
r); | ||||
no_subnets >>= (32 - sss->min_auto_ip_net.net_bitlen); | ||||
no_subnets += 1; | ||||
// proposal for sub-network to choose | ||||
net_id = pearson_hash_32((const uint8_t *)comm->community, N2N_COMMUNITY_ | ||||
SIZE) % no_subnets; | ||||
net_id = sss->min_auto_ip_net.net_addr + (net_id << (32 - sss->min_auto_i | ||||
p_net.net_bitlen)); | ||||
// check for availability starting from net_id, then downwards, ... | ||||
net_increment = (~mask+1); | ||||
for(net_id_i = net_id; net_id_i >= sss->min_auto_ip_net.net_addr; net_id_i - | ||||
= net_increment) { | ||||
success = subnet_available(sss, comm, net_id_i, mask); | ||||
if(success) { | ||||
break; | ||||
} | ||||
} | ||||
// ... then upwards | ||||
if(!success) { | ||||
for(net_id_i = net_id + net_increment; net_id_i <= sss->max_auto_ip_net. | ||||
net_addr; net_id_i += net_increment) { | ||||
success = subnet_available(sss, comm, net_id_i, mask); | ||||
if(success) { | ||||
break; | ||||
} | ||||
} | ||||
} | ||||
if(success) { | ||||
comm->auto_ip_net.net_addr = net_id_i; | ||||
comm->auto_ip_net.net_bitlen = sss->min_auto_ip_net.net_bitlen; | ||||
net = htonl(comm->auto_ip_net.net_addr); | ||||
traceEvent(TRACE_INFO, "assigned sub-network %s/%u to community '%s'", | ||||
inet_ntoa(*(struct in_addr *) &net), | ||||
comm->auto_ip_net.net_bitlen, | ||||
comm->community); | ||||
return 0; | ||||
} else { | ||||
comm->auto_ip_net.net_addr = 0; | ||||
comm->auto_ip_net.net_bitlen = 0; | ||||
traceEvent(TRACE_WARNING, "no assignable sub-network left for community | ||||
'%s'", | ||||
comm->community); | ||||
return -1; | ||||
} | ||||
} | } | |||
/*** | /*** | |||
* | * | |||
* 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_edge_time_stamp_and_verify (struct peer_info * edges, | static int find_edge_time_stamp_and_verify (struct peer_info * edges, | |||
int from_supernode, n2n_mac_t mac, | peer_info_t *sn, n2n_mac_t mac, | |||
uint64_t stamp) { | uint64_t stamp, int allow_jitter) { | |||
uint64_t *previous_stamp = NULL; | ||||
uint64_t * previous_stamp = NULL; | if(sn) { | |||
previous_stamp = &(sn->last_valid_time_stamp); | ||||
} else { | ||||
struct peer_info *edge; | ||||
HASH_FIND_PEER(edges, mac, edge); | ||||
if(!from_supernode) { | if(edge) { | |||
struct peer_info *edge; | // time_stamp_verify_and_update allows the pointer a previous stamp | |||
HASH_FIND_PEER(edges, mac, edge); | to be NULL | |||
if(edge) { | // if it is a (so far) unknown edge | |||
// time_stamp_verify_and_update allows the pointer a previous stamp to be | previous_stamp = &(edge->last_valid_time_stamp); | |||
NULL | } | |||
// if it is a (so far) unknown edge | ||||
previous_stamp = &(edge->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); | |||
} | } | |||
static int purge_expired_communities(n2n_sn_t *sss, | static int re_register_and_purge_supernodes (n2n_sn_t *sss, struct sn_community | |||
time_t* p_last_purge, | *comm, time_t *p_last_re_reg_and_purge, time_t now, uint8_t forced) { | |||
time_t now) | ||||
{ | time_t time; | |||
struct sn_community *comm, *tmp; | struct peer_info *peer, *tmp; | |||
size_t num_reg = 0; | ||||
if(!forced) { | ||||
if((now - (*p_last_re_reg_and_purge)) < RE_REG_AND_PURGE_FREQUENCY) { | ||||
return 0; | ||||
} | ||||
// purge long-time-not-seen supernodes | ||||
purge_expired_nodes(&(comm->edges), sss->sock, &sss->tcp_connections, p_ | ||||
last_re_reg_and_purge, | ||||
RE_REG_AND_PURGE_FREQUENCY, LAST_SEEN_SN_INACTIVE); | ||||
} | ||||
if(comm != NULL) { | ||||
HASH_ITER(hh,comm->edges,peer,tmp) { | ||||
time = now - peer->last_seen; | ||||
if(!forced) { | ||||
if(time <= LAST_SEEN_SN_ACTIVE) { | ||||
continue; | ||||
} | ||||
} | ||||
/* re-register (send REGISTER_SUPER) */ | ||||
uint8_t pktbuf[N2N_PKT_BUF_SIZE] = {0}; | ||||
size_t idx; | ||||
/* ssize_t sent; */ | ||||
n2n_common_t cmn; | ||||
n2n_REGISTER_SUPER_t reg; | ||||
n2n_sock_str_t sockbuf; | ||||
if ((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) return 0; | memset(&cmn, 0, sizeof(cmn)); | |||
memset(®, 0, sizeof(reg)); | ||||
traceEvent(TRACE_DEBUG, "Purging old communities and edges"); | cmn.ttl = N2N_DEFAULT_TTL; | |||
cmn.pc = n2n_register_super; | ||||
cmn.flags = N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn.community, comm->community, N2N_COMMUNITY_SIZE); | ||||
HASH_ITER(hh, sss->communities, comm, tmp) { | reg.cookie = n2n_rand(); | |||
num_reg += purge_peer_list(&comm->edges, now - REGISTRATION_TIMEOUT); | peer->last_cookie = reg.cookie; | |||
if ((comm->edges == NULL) && (!sss->lock_communities)) { | ||||
traceEvent(TRACE_INFO, "Purging idle community %s", comm->community); | reg.dev_addr.net_addr = ntohl(peer->dev_addr.net_addr); | |||
if (NULL != comm->header_encryption_ctx) | reg.dev_addr.net_bitlen = mask2bitlen(ntohl(peer->dev_addr.net_bitle | |||
/* this should not happen as no 'locked' and thus only communities w/o e | n)); | |||
ncrypted header here */ | get_local_auth(sss, &(reg.auth)); | |||
free(comm->header_encryption_ctx); | ||||
HASH_DEL(sss->communities, comm); | reg.key_time = sss->dynamic_key_time; | |||
free(comm); | ||||
idx = 0; | ||||
encode_mac(reg.edgeMac, &idx, sss->mac_addr); | ||||
idx = 0; | ||||
encode_REGISTER_SUPER(pktbuf, &idx, &cmn, ®); | ||||
traceEvent(TRACE_DEBUG, "send REGISTER_SUPER to %s", | ||||
sock_to_cstr(sockbuf, &(peer->sock))); | ||||
packet_header_encrypt(pktbuf, idx, idx, | ||||
comm->header_encryption_ctx_static, comm->head | ||||
er_iv_ctx_static, | ||||
time_stamp()); | ||||
/* sent = */ sendto_peer(sss, peer, pktbuf, idx); | ||||
} | ||||
} | } | |||
} | ||||
(*p_last_purge) = now; | ||||
traceEvent(TRACE_DEBUG, "Remove %ld edges", num_reg); | return 0; /* OK */ | |||
} | ||||
static int purge_expired_communities (n2n_sn_t *sss, | ||||
time_t* p_last_purge, | ||||
time_t now) { | ||||
struct sn_community *comm, *tmp_comm; | ||||
node_supernode_association_t *assoc, *tmp_assoc; | ||||
size_t num_reg = 0; | ||||
size_t num_assoc = 0; | ||||
if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) { | ||||
return 0; | ||||
} | ||||
traceEvent(TRACE_DEBUG, "purging old communities and edges"); | ||||
HASH_ITER(hh, sss->communities, comm, tmp_comm) { | ||||
// federation is taken care of in re_register_and_purge_supernodes() | ||||
if(comm->is_federation == IS_FEDERATION) | ||||
continue; | ||||
// purge the community's local peers | ||||
num_reg += purge_peer_list(&comm->edges, sss->sock, &sss->tcp_connection | ||||
s, now - REGISTRATION_TIMEOUT); | ||||
// purge the community's associated peers (connected to other supernodes | ||||
) | ||||
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) { | ||||
if(comm->assoc->last_seen < (now - 3 * REGISTRATION_TIMEOUT)) { | ||||
HASH_DEL(comm->assoc, assoc); | ||||
free(assoc); | ||||
num_assoc++; | ||||
} | ||||
} | ||||
if((comm->edges == NULL) && (comm->purgeable == COMMUNITY_PURGEABLE)) { | ||||
traceEvent(TRACE_INFO, "purging idle community %s", comm->community) | ||||
; | ||||
if(NULL != comm->header_encryption_ctx_static) { | ||||
/* this should not happen as 'purgeable' and thus only communiti | ||||
es w/o encrypted header here */ | ||||
free(comm->header_encryption_ctx_static); | ||||
free(comm->header_iv_ctx_static); | ||||
free(comm->header_encryption_ctx_dynamic); | ||||
free(comm->header_iv_ctx_dynamic); | ||||
} | ||||
// remove all associations | ||||
HASH_ITER(hh, comm->assoc, assoc, tmp_assoc) { | ||||
HASH_DEL(comm->assoc, assoc); | ||||
free(assoc); | ||||
} | ||||
HASH_DEL(sss->communities, comm); | ||||
free(comm); | ||||
} | ||||
} | ||||
(*p_last_purge) = now; | ||||
return 0; | traceEvent(TRACE_DEBUG, "purge_expired_communities removed %ld locally regis | |||
tered edges and %ld remotely associated edges", | ||||
num_reg, num_assoc); | ||||
return 0; | ||||
} | } | |||
static int number_enc_packets_sort (struct sn_community *a, struct sn_community *b) { | static int number_enc_packets_sort (struct sn_community *a, struct sn_community *b) { | |||
// comparison function for sorting communities in descending order of their | ||||
// number_enc_packets-fields | // comparison function for sorting communities in descending order of their | |||
return (b->number_enc_packets - a->number_enc_packets); | // number_enc_packets-fields | |||
return (b->number_enc_packets - a->number_enc_packets); | ||||
} | } | |||
static int sort_communities (n2n_sn_t *sss, | static int sort_communities (n2n_sn_t *sss, | |||
time_t* p_last_sort, | time_t* p_last_sort, | |||
time_t now) | time_t now) { | |||
{ | ||||
struct sn_community *comm, *tmp; | struct sn_community *comm, *tmp; | |||
if ((now - (*p_last_sort)) < SORT_COMMUNITIES_INTERVAL) return 0; | if((now - (*p_last_sort)) < SORT_COMMUNITIES_INTERVAL) { | |||
return 0; | ||||
} | ||||
// this routine gets periodically called as defined in SORT_COMMUNITIES_INTERV | // this routine gets periodically called as defined in SORT_COMMUNITIES_INTE | |||
AL | RVAL | |||
// it sorts the communities in descending order of their number_enc_packets-fi | // it sorts the communities in descending order of their number_enc_packets- | |||
elds... | fields... | |||
HASH_SORT(sss->communities, number_enc_packets_sort); | HASH_SORT(sss->communities, number_enc_packets_sort); | |||
// ... and afterward resets the number_enc__packets-fields to zero | // ... and afterward resets the number_enc__packets-fields to zero | |||
// (other models could reset it to half of their value to respect history) | // (other models could reset it to half of their value to respect history) | |||
HASH_ITER(hh, sss->communities, comm, tmp) { | HASH_ITER(hh, sss->communities, comm, tmp) { | |||
comm->number_enc_packets = 0; | comm->number_enc_packets = 0; | |||
} | } | |||
(*p_last_sort) = now; | (*p_last_sort) = now; | |||
return 0; | return 0; | |||
} | } | |||
static int process_mgmt(n2n_sn_t *sss, | static int process_mgmt (n2n_sn_t *sss, | |||
const struct sockaddr_in *sender_sock, | const struct sockaddr_in *sender_sock, | |||
const uint8_t *mgmt_buf, | char *mgmt_buf, | |||
size_t mgmt_size, | size_t mgmt_size, | |||
time_t now) | time_t now) { | |||
{ | ||||
char resbuf[N2N_SN_PKTBUF_SIZE]; | char resbuf[N2N_SN_PKTBUF_SIZE]; | |||
size_t ressize = 0; | size_t ressize = 0; | |||
uint32_t num_edges = 0; | uint32_t num_edges = 0; | |||
uint32_t num_comm = 0; | ||||
uint32_t num = 0; | uint32_t num = 0; | |||
struct sn_community *community, *tmp; | struct sn_community *community, *tmp; | |||
struct peer_info * peer, *tmpPeer; | struct peer_info *peer, *tmpPeer; | |||
macstr_t mac_buf; | macstr_t mac_buf; | |||
n2n_sock_str_t sockbuf; | n2n_sock_str_t sockbuf; | |||
char time_buf[10]; /* 9 digits + 1 terminating zero */ | ||||
dec_ip_bit_str_t ip_bit_str = {'\0'}; | ||||
traceEvent(TRACE_DEBUG, "process_mgmt"); | traceEvent(TRACE_DEBUG, "process_mgmt"); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | /* avoid parsing any uninitialized junk from the stack */ | |||
"----------------\n"); | mgmt_buf[mgmt_size] = 0; | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | // process input, if any | |||
"uptime %lu\n", (now - sss->start_time)); | if((0 == memcmp(mgmt_buf, "help", 4)) || (0 == memcmp(mgmt_buf, "?", 1)) | |||
) { | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"Help for supernode management console:\n" | ||||
"\thelp | This help message\n" | ||||
"\treload_communities | Reloads communities an | ||||
d user's public keys\n" | ||||
"\t<enter> | Display status and sta | ||||
tistics\n"); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); | ||||
return 0; /* no status output afterwards */ | ||||
} | ||||
HASH_ITER(hh, sss->communities, community, tmp) | if(0 == memcmp(mgmt_buf, "reload_communities", 18)) { | |||
{ | if(!sss->community_file) { | |||
num_edges += HASH_COUNT(community->edges); | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressi | |||
ze, | ||||
"No community file provided (-c command line | ||||
option)\n"); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize) | ||||
; | ||||
return 0; /* no status output afterwards */ | ||||
} | ||||
traceEvent(TRACE_NORMAL, "'reload_communities' command"); | ||||
if(load_allowed_sn_community(sss)) { | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressi | ||||
ze, | ||||
"Error while re-loading community file (not | ||||
found or no valid content)\n"); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize) | ||||
; | ||||
return 0; /* no status output afterwards */ | ||||
} | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"OK.\n"); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); | ||||
return 0; /* no status output afterwards */ | ||||
} | ||||
if((mgmt_buf[0] == 'r' || mgmt_buf[0] == 'w') && (mgmt_buf[1] == ' ')) { | ||||
/* this is a JSON request */ | ||||
handleMgmtJson_sn(sss, mgmt_buf, *sender_sock); | ||||
return 0; | ||||
} | } | |||
// output current status | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"edges %u\n", | " ### | TAP | MAC | EDGE | |||
num_edges); | | HINT | LAST SEEN\n"); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"======================================================= | ||||
=================================================\n"); | ||||
HASH_ITER(hh, sss->communities, community, tmp) { | ||||
if(num_comm) | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"----------------------------------------------- | ||||
---------------------------------------------------------\n"); | ||||
num_comm++; | ||||
num_edges += HASH_COUNT(community->edges); | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"%s '%s'\n", | ||||
(community->is_federation) ? "FEDERATION" : | ||||
((communit | ||||
y->purgeable == COMMUNITY_UNPURGEABLE) ? "FIXED NAME COMMUNITY" : "COMMUNITY"), | ||||
(community->is_federation) ? "-/-" : community->comm | ||||
unity); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); | ||||
ressize = 0; | ||||
num = 0; | ||||
HASH_ITER(hh, community->edges, peer, tmpPeer) { | ||||
sprintf (time_buf, "%9u", (unsigned int)(now - peer->last_seen)); | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"%4u | %-19s | %-17s | %-21s %-3s | %-15s | %9s\ | ||||
n", | ||||
++num, | ||||
(peer->dev_addr.net_addr == 0) ? ((peer->purgeab | ||||
le == SN_UNPURGEABLE) ? "-l" : "") : | ||||
ip_subnet_to_ | ||||
str(ip_bit_str, &peer->dev_addr), | ||||
(is_null_mac(peer->mac_addr)) ? "" : macaddr_str | ||||
(mac_buf, peer->mac_addr), | ||||
sock_to_cstr(sockbuf, &(peer->sock)), | ||||
((peer->socket_fd >= 0) && (peer->socket_fd != s | ||||
ss->sock)) ? "TCP" : "", | ||||
peer->dev_desc, | ||||
(peer->last_seen) ? time_buf : ""); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); | ||||
ressize = 0; | ||||
} | ||||
} | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"======================================================= | ||||
=================================================\n"); | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"errors %u\n", | "uptime %lu | ", (now - sss->start_time)); | |||
(unsigned int)sss->stats.errors); | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"reg_sup %u\n", | "edges %u | ", | |||
(unsigned int)sss->stats.reg_super); | num_edges); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"reg_nak %u\n", | "reg_sup %u | ", | |||
(unsigned int)sss->stats.reg_super_nak); | (unsigned int) sss->stats.reg_super); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"fwd %u\n", | "reg_nak %u | ", | |||
(unsigned int)sss->stats.fwd); | (unsigned int) sss->stats.reg_super_nak); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"broadcast %u\n", | "errors %u \n", | |||
(unsigned int)sss->stats.broadcast); | (unsigned int) sss->stats.errors); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"last fwd %lu sec ago\n", | "fwd %u | ", | |||
(long unsigned int)(now - sss->stats.last_fwd)); | (unsigned int) sss->stats.fwd); | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"last reg %lu sec ago\n", | "broadcast %u | ", | |||
(long unsigned int)(now - sss->stats.last_reg_super)); | (unsigned int) sss->stats.broadcast); | |||
ressize += snprintf(resbuf+ressize, N2N_SN_PKTBUF_SIZE-ressize, | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
"cur_cmnts %u\n", HASH_COUNT(sss->communities)); | "cur_cmnts %u\n", HASH_COUNT(sss->communities)); | |||
HASH_ITER(hh, sss->communities, community, tmp) { | ||||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | ||||
"community: %s\n", community->community); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *)resbuf, ressize); | ||||
ressize = 0; | ||||
num = 0; | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
HASH_ITER(hh, community->edges, peer, tmpPeer) { | "last_fwd %lu sec ago | ", | |||
ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | (long unsigned int) (now - sss->stats.last_fwd)); | |||
"\t[id: %u][MAC: %s][edge: %s][last seen: %lu sec ag | ||||
o]\n", | ||||
++num, macaddr_str(mac_buf, peer->mac_addr), | ||||
sock_to_cstr(sockbuf, &(peer->sock)), now-peer->last | ||||
_seen); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *)resbuf, ressize); | ressize += snprintf(resbuf + ressize, N2N_SN_PKTBUF_SIZE - ressize, | |||
ressize = 0; | "last reg %lu sec ago\n\n", | |||
} | (long unsigned int) (now - sss->stats.last_reg_super)); | |||
} | ||||
ressize += snprintf(resbuf+ressize, N2N_SN_PKTBUF_SIZE-ressize, | sendto_mgmt(sss, sender_sock, (const uint8_t *) resbuf, ressize); | |||
"\n"); | ||||
sendto_mgmt(sss, sender_sock, (const uint8_t *)resbuf, ressize); | ||||
return 0; | return 0; | |||
} | } | |||
static int sendto_mgmt(n2n_sn_t *sss, | static int sendto_mgmt (n2n_sn_t *sss, | |||
const struct sockaddr_in *sender_sock, | const struct sockaddr_in *sender_sock, | |||
const uint8_t *mgmt_buf, | const uint8_t *mgmt_buf, | |||
size_t mgmt_size) | size_t mgmt_size) { | |||
{ | ||||
ssize_t r = sendto(sss->mgmt_sock, mgmt_buf, mgmt_size, 0 /*flags*/, | ssize_t r = sendto(sss->mgmt_sock, (void *)mgmt_buf, mgmt_size, 0 /*flags*/, | |||
(struct sockaddr *)sender_sock, sizeof (struct sockaddr_in) | (struct sockaddr *)sender_sock, sizeof (struct sockaddr_i | |||
); | n)); | |||
if (r <= 0) { | if(r <= 0) { | |||
++(sss->stats.errors); | ++(sss->stats.errors); | |||
traceEvent (TRACE_ERROR, "sendto_mgmt : sendto failed. %s", strerror (errno) | traceEvent (TRACE_ERROR, "sendto_mgmt : sendto failed. %s", strerror (er | |||
); | rno)); | |||
return -1; | return -1; | |||
} | } | |||
return 0; | ||||
return 0; | ||||
} | } | |||
/** Examine a datagram and determine what to do with it. | /** Examine a datagram and determine what to do with it. | |||
* | * | |||
*/ | */ | |||
static int process_udp(n2n_sn_t * sss, | static int process_udp (n2n_sn_t * sss, | |||
const struct sockaddr_in * sender_sock, | const struct sockaddr_in *sender_sock, | |||
uint8_t * udp_buf, | const SOCKET socket_fd, | |||
size_t udp_size, | uint8_t * udp_buf, | |||
time_t now) | size_t udp_size, | |||
{ | time_t now) { | |||
n2n_common_t cmn; /* common fields in the packet header */ | ||||
size_t rem; | n2n_common_t cmn; /* common fields in the packet header */ | |||
size_t idx; | size_t rem; | |||
size_t msg_type; | size_t idx; | |||
uint8_t from_supernode; | size_t msg_type; | |||
macstr_t mac_buf; | uint8_t from_supernode; | |||
macstr_t mac_buf2; | peer_info_t *sn = NULL; | |||
n2n_sock_str_t sockbuf; | n2n_sock_t sender; | |||
char buf[32]; | macstr_t mac_buf; | |||
struct sn_community *comm, *tmp; | macstr_t mac_buf2; | |||
uint64_t stamp; | n2n_sock_str_t sockbuf; | |||
char buf[32]; | ||||
traceEvent(TRACE_DEBUG, "Processing incoming UDP packet [len: %lu][sender: %s: | uint8_t hash_buf[16] = {0}; /* always size of 16 (max) despite t | |||
%u]", | he actual value of N2N_REG_SUP_HASH_CHECK_LEN (<= 16) */ | |||
udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof(buf | ||||
)), | struct sn_community *comm, *tmp; | |||
ntohs(sender_sock->sin_port)); | uint32_t header_enc = 0; /* 1 == encrypted by static key, 2 == en | |||
crypted by dynamic key */ | ||||
/* check if header is unenrypted. the following check is around 99.99962 perce | uint64_t stamp; | |||
nt reliable. | int skip_add; | |||
* it heavily relies on the structure of packet's common part | time_t any_time = 0; | |||
* changes to wire.c:encode/decode_common need to go together with this code * | ||||
/ | traceEvent(TRACE_DEBUG, "processing incoming UDP packet [len: %lu][sender: % | |||
if (udp_size < 20) { | s:%u]", | |||
traceEvent(TRACE_DEBUG, "process_udp dropped a packet too short to be valid. | udp_size, intoa(ntohl(sender_sock->sin_addr.s_addr), buf, sizeof( | |||
"); | buf)), | |||
return -1; | ntohs(sender_sock->sin_port)); | |||
} | ||||
if ( (udp_buf[19] == (uint8_t)0x00) // null terminated community name | /* check if header is unencrypted. the following check is around 99.99962 pe | |||
rcent reliable. | ||||
* it heavily relies on the structure of packet's common part | ||||
* changes to wire.c:encode/decode_common need to go together with this code | ||||
*/ | ||||
if(udp_size < 24) { | ||||
traceEvent(TRACE_DEBUG, "dropped a packet too short to be valid"); | ||||
return -1; | ||||
} | ||||
if((udp_buf[23] == (uint8_t)0x00) // null terminated community name | ||||
&& (udp_buf[00] == N2N_PKT_VERSION) // correct packet version | && (udp_buf[00] == N2N_PKT_VERSION) // correct packet version | |||
&& ((be16toh (*(uint16_t*)&(udp_buf[02])) & N2N_FLAGS_TYPE_MASK ) <= MSG_ | && ((be16toh(*(uint16_t*)&(udp_buf[02])) & N2N_FLAGS_TYPE_MASK) <= MSG_TY | |||
TYPE_MAX_TYPE ) // message type | PE_MAX_TYPE) // message type | |||
&& ( be16toh (*(uint16_t*)&(udp_buf[02])) < N2N_FLAGS_OPTIONS) // flags | && ( be16toh(*(uint16_t*)&(udp_buf[02])) < N2N_FLAGS_OPTIONS) // flags | |||
) { | ) { | |||
/* most probably unencrypted */ | /* most probably unencrypted */ | |||
/* make sure, no downgrading happens here and no unencrypted packets can be | /* make sure, no downgrading happens here and no unencrypted packets can | |||
* injected in a community which definitely deals with encrypted headers */ | be | |||
HASH_FIND_COMMUNITY(sss->communities, (char *)&udp_buf[04], comm); | * injected in a community which definitely deals with encrypted headers | |||
if (comm) { | */ | |||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | HASH_FIND_COMMUNITY(sss->communities, (char *)&udp_buf[04], comm); | |||
traceEvent(TRACE_DEBUG, "process_udp dropped a packet with unencrypted h | if(comm) { | |||
eader " | if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | |||
"addressed to community '%s' which uses encrypte | traceEvent(TRACE_DEBUG, "dropped a packet with unencrypted heade | |||
d headers.", | r " | |||
comm->community); | "addressed to community '%s' which uses encrypted hea | |||
return -1; | ders", | |||
} | comm->community); | |||
if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { | return -1; | |||
traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " | } | |||
"unencrypted headers.", comm->community); | if(comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { | |||
/* set 'no encryption' in case it is not set yet */ | traceEvent(TRACE_INFO, "locked community '%s' to " | |||
comm->header_encryption = HEADER_ENCRYPTION_NONE; | "unencrypted headers", comm->community); | |||
comm->header_encryption_ctx = NULL; | /* set 'no encryption' in case it is not set yet */ | |||
} | comm->header_encryption = HEADER_ENCRYPTION_NONE; | |||
} | comm->header_encryption_ctx_static = NULL; | |||
} else { | comm->header_encryption_ctx_dynamic = NULL; | |||
/* most probably encrypted */ | } | |||
/* cycle through the known communities (as keys) to eventually decrypt */ | } | |||
uint32_t ret = 0; | } else { | |||
HASH_ITER (hh, sss->communities, comm, tmp) { | /* most probably encrypted */ | |||
/* skip the definitely unencrypted communities */ | /* cycle through the known communities (as keys) to eventually decrypt * | |||
if (comm->header_encryption == HEADER_ENCRYPTION_NONE) | / | |||
continue; | HASH_ITER(hh, sss->communities, comm, tmp) { | |||
uint16_t checksum = 0; | /* skip the definitely unencrypted communities */ | |||
if ( (ret = packet_header_decrypt (udp_buf, udp_size, comm->community, com | if(comm->header_encryption == HEADER_ENCRYPTION_NONE) { | |||
m->header_encryption_ctx, | continue; | |||
comm->header_iv_ctx, | } | |||
&stamp, &checksum)) ) { | ||||
// time stamp verification follows in the packet specific section as it | // match with static (1) or dynamic (2) ctx? | |||
requires to determine the | // check dynamic first as it is identical to static in normal header | |||
// sender from the hash list by its MAC, this all depends on packet type | encryption mode | |||
and packet structure | if(packet_header_decrypt(udp_buf, udp_size, | |||
// (MAC is not always in the same place) | comm->community, | |||
if (checksum != pearson_hash_16 (udp_buf, udp_size)) { | comm->header_encryption_ctx_dynamic, comm-> | |||
traceEvent(TRACE_DEBUG, "process_udp dropped packet due to checksum err | header_iv_ctx_dynamic, | |||
or."); | &stamp)) { | |||
return -1; | header_enc = 2; | |||
} | } | |||
if (comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { | if(!header_enc) { | |||
traceEvent (TRACE_INFO, "process_udp locked community '%s' to using " | pearson_hash_128(hash_buf, udp_buf, max(0, (int)udp_size - (int) | |||
"encrypted headers.", comm->community); | N2N_REG_SUP_HASH_CHECK_LEN)); | |||
/* set 'encrypted' in case it is not set yet */ | header_enc = packet_header_decrypt(udp_buf, max(0, (int)udp_size | |||
comm->header_encryption = HEADER_ENCRYPTION_ENABLED; | - (int)N2N_REG_SUP_HASH_CHECK_LEN), comm->community, | |||
} | comm->header_encryption_ctx_s | |||
// count the number of encrypted packets for sorting the communities fro | tatic, comm->header_iv_ctx_static, &stamp); | |||
m time to time | } | |||
// for the HASH_ITER a few lines above gets faster for the more busy comm | ||||
unities | if(header_enc) { | |||
(comm->number_enc_packets)++; | // time stamp verification follows in the packet specific sectio | |||
// no need to test further communities | n as it requires to determine the | |||
break; | // sender from the hash list by its MAC, this all depends on pac | |||
} | ket type and packet structure | |||
} | // (MAC is not always in the same place) | |||
if (!ret) { | ||||
// no matching key/community | if(comm->header_encryption == HEADER_ENCRYPTION_UNKNOWN) { | |||
traceEvent(TRACE_DEBUG, "process_udp dropped a packet with seemingly encry | traceEvent(TRACE_INFO, "locked community '%s' to " | |||
pted header " | "encrypted headers", comm->community); | |||
"for which no matching community which uses encrypt | /* set 'encrypted' in case it is not set yet */ | |||
ed headers was found."); | comm->header_encryption = HEADER_ENCRYPTION_ENABLED; | |||
return -1; | } | |||
} | // count the number of encrypted packets for sorting the communi | |||
} | ties from time to time | |||
// for the HASH_ITER a few lines above gets faster for the more | ||||
/* Use decode_common() to determine the kind of packet then process it: | busy communities | |||
* | (comm->number_enc_packets)++; | |||
* REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK | // no need to test further communities | |||
* | break; | |||
* REGISTER, REGISTER_ACK and PACKET messages are forwarded to their | } | |||
* destination edge. If the destination is not known then PACKETs are | } | |||
* broadcast. | if(!header_enc) { | |||
*/ | // no matching key/community | |||
traceEvent(TRACE_DEBUG, "dropped a packet with seemingly encrypted h | ||||
rem = udp_size; /* Counts down bytes of packet to protect against buffer overr | eader " | |||
uns. */ | "for which no matching community which uses encrypted hea | |||
idx = 0; /* marches through packet header as parts are decoded. */ | ders was found"); | |||
if(decode_common(&cmn, udp_buf, &rem, &idx) < 0) { | return -1; | |||
traceEvent(TRACE_ERROR, "Failed to decode common section"); | } | |||
return -1; /* failed to decode packet */ | ||||
} | ||||
msg_type = cmn.pc; /* packet code */ | ||||
from_supernode= cmn.flags & N2N_FLAGS_FROM_SUPERNODE; | ||||
if(cmn.ttl < 1) { | ||||
traceEvent(TRACE_WARNING, "Expired TTL"); | ||||
return 0; /* Don't process further */ | ||||
} | ||||
--(cmn.ttl); /* The value copied into all forwarded packets. */ | ||||
switch(msg_type) { | ||||
case MSG_TYPE_PACKET: | ||||
{ | ||||
/* PACKET from one edge to another edge via supernode. */ | ||||
/* pkt will be modified in place and recoded to an output of potentially | ||||
* different size due to addition of the socket.*/ | ||||
n2n_PACKET_t pkt; | ||||
n2n_common_t cmn2; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx=0; | ||||
int unicast; /* non-zero if unicast */ | ||||
uint8_t * rec_buf; /* either udp_buf or encbuf */ | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "process_udp PACKET with unknown community %s", cm | ||||
n.community); | ||||
return -1; | ||||
} | ||||
sss->stats.last_fwd=now; | ||||
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); | ||||
// already checked for valid comm | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, pkt.srcM | ||||
ac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "process_udp dropped PACKET due to time stamp er | ||||
ror."); | ||||
return -1; | ||||
} | ||||
} | } | |||
unicast = (0 == is_multi_broadcast(pkt.dstMac)); | /* Use decode_common() to determine the kind of packet then process it: | |||
* | ||||
* REGISTER_SUPER adds an edge and generate a return REGISTER_SUPER_ACK | ||||
* | ||||
* REGISTER, REGISTER_ACK and PACKET messages are forwarded to their | ||||
* destination edge. If the destination is not known then PACKETs are | ||||
* broadcast. | ||||
*/ | ||||
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) { | ||||
traceEvent(TRACE_ERROR, "failed to decode common section"); | ||||
return -1; /* failed to decode packet */ | ||||
} | ||||
traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", | msg_type = cmn.pc; /* packet code */ | |||
(unicast?"unicast":"multicast"), | ||||
macaddr_str(mac_buf, pkt.srcMac), | ||||
macaddr_str(mac_buf2, pkt.dstMac), | ||||
(from_supernode?"from sn":"local")); | ||||
if(!from_supernode) { | // special case for user/pw auth | |||
memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); | // community's auth scheme and message type need to match the used key (dyna | |||
mic) | ||||
if(comm) { | ||||
if((comm->allowed_users) | ||||
&& (msg_type != MSG_TYPE_REGISTER_SUPER) | ||||
&& (msg_type != MSG_TYPE_REGISTER_SUPER_ACK) | ||||
&& (msg_type != MSG_TYPE_REGISTER_SUPER_NAK)) { | ||||
if(header_enc != 2) { | ||||
traceEvent(TRACE_WARNING, "dropped packet encrypted with static | ||||
key where expecting dynamic key"); | ||||
return -1; | ||||
} | ||||
} | ||||
} | ||||
/* We are going to add socket even if it was not there before */ | /* REVISIT: when UDP/IPv6 is supported we will need a flag to indicate which | |||
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | * IP transport version the packet arrived on. May need to UDP sockets. */ | |||
memset(&sender, 0, sizeof(n2n_sock_t)); | ||||
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); | ||||
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 (&(sss->federation->edges), &sender, | ||||
null_mac, &skip_add); | ||||
// only REGISTER_SUPER allowed from unknown supernodes | ||||
if((!sn) && (msg_type != MSG_TYPE_REGISTER_SUPER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped incoming data from unknown supernod | ||||
e"); | ||||
return -1; | ||||
} | ||||
} | ||||
pkt.sock.family = AF_INET; | if(cmn.ttl < 1) { | |||
pkt.sock.port = ntohs(sender_sock->sin_port); | traceEvent(TRACE_WARNING, "expired TTL"); | |||
memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); | return 0; /* Don't process further */ | |||
} | ||||
--(cmn.ttl); /* The value copied into all forwarded packets. */ | ||||
switch(msg_type) { | ||||
case MSG_TYPE_PACKET: { | ||||
/* PACKET from one edge to another edge via supernode. */ | ||||
/* pkt will be modified in place and recoded to an output of potenti | ||||
ally | ||||
* different size due to addition of the socket.*/ | ||||
n2n_PACKET_t pkt; | ||||
n2n_common_t cmn2; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
int unicast; /* non-zero if unicast */ | ||||
uint8_t * rec_buf; /* either udp_buf or encbuf */ | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "PACKET with unknown community %s", cmn. | ||||
community); | ||||
return -1; | ||||
} | ||||
rec_buf = encbuf; | sss->stats.last_fwd = now; | |||
decode_PACKET(&pkt, &cmn, udp_buf, &rem, &idx); | ||||
/* Re-encode the header. */ | // already checked for valid comm | |||
encode_PACKET(encbuf, &encx, &cmn2, &pkt); | if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | |||
uint16_t oldEncx = encx; | if(!find_edge_time_stamp_and_verify(comm->edges, sn, pkt.srcMac, | |||
stamp, TIME_STAMP_ALLOW_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped PACKET due to time stamp er | ||||
ror"); | ||||
return -1; | ||||
} | ||||
} | ||||
/* Copy the original payload unchanged */ | unicast = (0 == is_multi_broadcast(pkt.dstMac)); | |||
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); | ||||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | traceEvent(TRACE_DEBUG, "RX PACKET (%s) %s -> %s %s", | |||
packet_header_encrypt (rec_buf, oldEncx, comm->header_encryption_ctx, | (unicast ? "unicast" : "multicast"), | |||
comm->header_iv_ctx, | macaddr_str(mac_buf, pkt.srcMac), | |||
time_stamp (), pearson_hash_16 | macaddr_str(mac_buf2, pkt.dstMac), | |||
(rec_buf, encx)); | (from_supernode ? "from sn" : "local")); | |||
if(!from_supernode) { | ||||
memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); | ||||
/* We are going to add socket even if it was not there before */ | ||||
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | ||||
pkt.sock.family = AF_INET; | ||||
pkt.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(pkt.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_S | ||||
IZE); | ||||
rec_buf = encbuf; | ||||
/* Re-encode the header. */ | ||||
encode_PACKET(encbuf, &encx, &cmn2, &pkt); | ||||
uint16_t oldEncx = encx; | ||||
/* Copy the original payload unchanged */ | ||||
encode_buf(encbuf, &encx, (udp_buf + idx), (udp_size - idx)); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
// in case of user-password auth, also encrypt the iv of pay | ||||
load assuming ChaCha20 and SPECK having the same iv size | ||||
packet_header_encrypt(rec_buf, oldEncx + (NULL != comm->allo | ||||
wed_users) * min(encx - oldEncx, N2N_SPECK_IVEC_SIZE), encx, | ||||
comm->header_encryption_ctx_dynamic, c | ||||
omm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
} else { | ||||
/* Already from a supernode. Nothing to modify, just pass to | ||||
* destination. */ | ||||
traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified"); | ||||
rec_buf = udp_buf; | ||||
encx = udp_size; | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
// in case of user-password auth, also encrypt the iv of pay | ||||
load assuming ChaCha20 and SPECK having the same iv size | ||||
packet_header_encrypt(rec_buf, idx + (NULL != comm->allowed_ | ||||
users) * min(encx - idx, N2N_SPECK_IVEC_SIZE), encx, | ||||
comm->header_encryption_ctx_dynamic, c | ||||
omm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
} | ||||
} else { | /* Common section to forward the final product. */ | |||
/* Already from a supernode. Nothing to modify, just pass to | if(unicast) { | |||
* destination. */ | try_forward(sss, comm, &cmn, pkt.dstMac, from_supernode, rec_buf | |||
, encx); | ||||
} else { | ||||
try_broadcast(sss, comm, &cmn, pkt.srcMac, from_supernode, rec_b | ||||
uf, encx); | ||||
} | ||||
break; | ||||
} | ||||
traceEvent(TRACE_DEBUG, "Rx PACKET fwd unmodified"); | case MSG_TYPE_REGISTER: { | |||
/* Forwarding a REGISTER from one edge to the next */ | ||||
rec_buf = udp_buf; | n2n_REGISTER_t reg; | |||
encx = udp_size; | n2n_common_t cmn2; | |||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
int unicast; /* non-zero if unicast */ | ||||
uint8_t * rec_buf; /* either udp_buf or encbuf */ | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "REGISTER from unknown community %s", cm | ||||
n.community); | ||||
return -1; | ||||
} | ||||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | sss->stats.last_fwd = now; | |||
packet_header_encrypt (rec_buf, idx, comm->header_encryption_ctx, | decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); | |||
comm->header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (rec | ||||
_buf, udp_size)); | ||||
} | ||||
/* Common section to forward the final product. */ | ||||
if(unicast) | ||||
try_forward(sss, comm, &cmn, pkt.dstMac, rec_buf, encx); | ||||
else | ||||
try_broadcast(sss, comm, &cmn, pkt.srcMac, rec_buf, encx); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER: | ||||
{ | ||||
/* Forwarding a REGISTER from one edge to the next */ | ||||
n2n_REGISTER_t reg; | ||||
n2n_common_t cmn2; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx=0; | ||||
int unicast; /* non-zero if unicast */ | ||||
uint8_t * rec_buf; /* either udp_buf or encbuf */ | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "process_udp REGISTER from unknown community %s", | ||||
cmn.community); | ||||
return -1; | ||||
} | ||||
sss->stats.last_fwd=now; | ||||
decode_REGISTER(®, &cmn, udp_buf, &rem, &idx); | ||||
// already checked for valid comm | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.srcM | ||||
ac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER due to time stamp | ||||
error."); | ||||
return -1; | ||||
} | ||||
} | ||||
unicast = (0 == is_multi_broadcast(reg.dstMac)); | // already checked for valid comm | |||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, reg.srcMac, | ||||
stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER due to time stamp | ||||
error"); | ||||
return -1; | ||||
} | ||||
} | ||||
if(unicast) { | unicast = (0 == is_multi_broadcast(reg.dstMac)); | |||
traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s", | ||||
macaddr_str(mac_buf, reg.srcMac), | ||||
macaddr_str(mac_buf2, reg.dstMac), | ||||
((cmn.flags & N2N_FLAGS_FROM_SUPERNODE)?"from sn":"local")); | ||||
if(0 == (cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) { | ||||
memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); | ||||
/* We are going to add socket even if it was not there before */ | ||||
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | ||||
reg.sock.family = AF_INET; | ||||
reg.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); | ||||
rec_buf = encbuf; | ||||
/* Re-encode the header. */ | ||||
encode_REGISTER(encbuf, &encx, &cmn2, ®); | ||||
} else { | ||||
/* Already from a supernode. Nothing to modify, just pass to | ||||
* destination. */ | ||||
rec_buf = udp_buf; | ||||
encx = udp_size; | ||||
} | ||||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (rec_buf, encx, comm->header_encryption_ctx, | ||||
comm->header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (rec | ||||
_buf, encx)); | ||||
try_forward(sss, comm, &cmn, reg.dstMac, rec_buf, encx); /* unicast only * | ||||
/ | ||||
} else | ||||
traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination"); | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_ACK: | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (NOT IMPLEMENTED) Should not be via | ||||
supernode"); | ||||
break; | ||||
case MSG_TYPE_REGISTER_SUPER: | ||||
{ | ||||
n2n_REGISTER_SUPER_t reg; | ||||
n2n_REGISTER_SUPER_ACK_t ack; | ||||
n2n_common_t cmn2; | ||||
uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx=0; | ||||
/* Edge requesting registration with us. */ | ||||
sss->stats.last_reg_super=now; | ||||
++(sss->stats.reg_super); | ||||
decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); | ||||
if (comm) { | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, reg.ed | ||||
geMac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER due to tim | ||||
e stamp error."); | ||||
return -1; | ||||
} | ||||
} | ||||
} | ||||
/* | ||||
Before we move any further, we need to check if the requested | ||||
community is allowed by the supernode. In case it is not we do | ||||
not report any message back to the edge to hide the supernode | ||||
existance (better from the security standpoint) | ||||
*/ | ||||
if(!comm && !sss->lock_communities) { | ||||
comm = calloc(1, sizeof(struct sn_community)); | ||||
if(comm) { | ||||
strncpy(comm->community, (char*)cmn.community, N2N_COMMUNITY_SIZE-1); | ||||
comm->community[N2N_COMMUNITY_SIZE-1] = '\0'; | ||||
/* new communities introduced by REGISTERs could not have had encrypted | ||||
header */ | ||||
comm->header_encryption = HEADER_ENCRYPTION_NONE; | ||||
comm->header_encryption_ctx = NULL; | ||||
comm->number_enc_packets = 0; | ||||
HASH_ADD_STR(sss->communities, community, comm); | ||||
traceEvent(TRACE_INFO, "New community: %s", comm->community); | if(unicast) { | |||
} | traceEvent(TRACE_DEBUG, "Rx REGISTER %s -> %s %s", | |||
} | macaddr_str(mac_buf, reg.srcMac), | |||
macaddr_str(mac_buf2, reg.dstMac), | ||||
((cmn.flags & N2N_FLAGS_FROM_SUPERNODE) ? "from sn" : | ||||
"local")); | ||||
if(0 == (cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) { | ||||
memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); | ||||
/* We are going to add socket even if it was not there befor | ||||
e */ | ||||
cmn2.flags |= N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | ||||
reg.sock.family = AF_INET; | ||||
reg.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IP | ||||
V4_SIZE); | ||||
/* Re-encode the header. */ | ||||
encode_REGISTER(encbuf, &encx, &cmn2, ®); | ||||
rec_buf = encbuf; | ||||
} else { | ||||
/* Already from a supernode. Nothing to modify, just pass to | ||||
* destination. */ | ||||
if(comm) { | rec_buf = udp_buf; | |||
cmn2.ttl = N2N_DEFAULT_TTL; | encx = udp_size; | |||
cmn2.pc = n2n_register_super_ack; | } | |||
cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); | ||||
memcpy(&(ack.cookie), &(reg.cookie), sizeof(n2n_cookie_t)); | ||||
memcpy(ack.edgeMac, reg.edgeMac, sizeof(n2n_mac_t)); | ||||
ack.lifetime = reg_lifetime(sss); | ||||
ack.sock.family = AF_INET; | ||||
ack.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE); | ||||
ack.num_sn=0; /* No backup */ | ||||
memset(&(ack.sn_bak), 0, sizeof(n2n_sock_t)); | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]", | ||||
macaddr_str(mac_buf, reg.edgeMac), | ||||
sock_to_cstr(sockbuf, &(ack.sock))); | ||||
update_edge(sss, reg.edgeMac, comm, &(ack.sock), now); | ||||
encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack); | ||||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
packet_header_encrypt (ackbuf, encx, comm->header_encryption_ctx, | ||||
comm->header_iv_ctx, | ||||
time_stamp (), pearson_hash_16 (ack | ||||
buf, encx)); | ||||
sendto(sss->sock, ackbuf, encx, 0, | ||||
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in)); | ||||
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]", | ||||
macaddr_str(mac_buf, reg.edgeMac), | ||||
sock_to_cstr(sockbuf, &(ack.sock))); | ||||
} else | ||||
traceEvent(TRACE_INFO, "Discarded registration: unallowed community '%s'", | ||||
(char*)cmn.community); | ||||
break; | ||||
} | ||||
case MSG_TYPE_QUERY_PEER: { | ||||
n2n_QUERY_PEER_t query; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx=0; | ||||
n2n_common_t cmn2; | ||||
n2n_PEER_INFO_t pi; | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "process_udp QUERY_PEER from unknown community %s" | ||||
, cmn.community); | ||||
return -1; | ||||
} | ||||
decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); | ||||
// already checked for valid comm | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify (comm->edges, from_supernode, query.sr | ||||
cMac, stamp)) { | ||||
traceEvent(TRACE_DEBUG, "process_udp dropped QUERY_PEER due to time stam | ||||
p error."); | ||||
return -1; | ||||
} | ||||
} | ||||
traceEvent( TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", | if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | |||
macaddr_str( mac_buf, query.srcMac ), | packet_header_encrypt(rec_buf, encx, encx, | |||
macaddr_str( mac_buf2, query.targetMac ) ); | comm->header_encryption_ctx_dynamic, c | |||
omm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
try_forward(sss, comm, &cmn, reg.dstMac, from_supernode, rec_buf | ||||
, encx); /* unicast only */ | ||||
} else { | ||||
traceEvent(TRACE_ERROR, "Rx REGISTER with multicast destination" | ||||
); | ||||
} | ||||
break; | ||||
} | ||||
struct peer_info *scan; | case MSG_TYPE_REGISTER_ACK: { | |||
HASH_FIND_PEER(comm->edges, query.targetMac, scan); | traceEvent(TRACE_DEBUG, "Rx REGISTER_ACK (not implemented) should no | |||
t be via supernode"); | ||||
break; | ||||
} | ||||
if (scan) { | case MSG_TYPE_REGISTER_SUPER: { | |||
cmn2.ttl = N2N_DEFAULT_TTL; | n2n_REGISTER_SUPER_t reg; | |||
cmn2.pc = n2n_peer_info; | n2n_REGISTER_SUPER_ACK_t ack; | |||
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; | n2n_REGISTER_SUPER_NAK_t nak; | |||
memcpy( cmn2.community, cmn.community, sizeof(n2n_community_t) ); | n2n_common_t cmn2; | |||
uint8_t ackbuf[N2N_SN_PKTBUF_SIZE]; | ||||
uint8_t payload_buf[REG_SUPER_ACK_PAY | ||||
LOAD_SPACE]; | ||||
n2n_REGISTER_SUPER_ACK_payload_t *payload; | ||||
size_t encx = 0; | ||||
struct sn_community_regular_expression *re, *tmp_re; | ||||
struct peer_info *peer, *tmp_peer, *p; | ||||
int8_t allowed_match = -1; | ||||
uint8_t match = 0; | ||||
int match_length = 0; | ||||
n2n_ip_subnet_t ipaddr; | ||||
int num = 0; | ||||
int skip; | ||||
int ret_value; | ||||
sn_user_t *user = NULL; | ||||
memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); | ||||
memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t)); | ||||
/* Edge/supernode requesting registration with us. */ | ||||
sss->stats.last_reg_super=now; | ||||
++(sss->stats.reg_super); | ||||
decode_REGISTER_SUPER(®, &cmn, udp_buf, &rem, &idx); | ||||
if(comm) { | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, reg.edg | ||||
eMac, stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER due to t | ||||
ime stamp error"); | ||||
return -1; | ||||
} | ||||
} | ||||
} | ||||
pi.aflags = 0; | /* | |||
memcpy( pi.mac, query.targetMac, sizeof(n2n_mac_t) ); | Before we move any further, we need to check if the requested | |||
pi.sock = scan->sock; | community is allowed by the supernode. In case it is not we do | |||
not report any message back to the edge to hide the supernode | ||||
existance (better from the security standpoint) | ||||
*/ | ||||
if(!comm && sss->lock_communities) { | ||||
HASH_ITER(hh, sss->rules, re, tmp_re) { | ||||
allowed_match = re_matchp(re->rule, (const char *)cmn.commun | ||||
ity, &match_length); | ||||
if((allowed_match != -1) | ||||
&& (match_length == strlen((const char *)cmn.community)) | ||||
// --- only full matches allowed (remove, if also partial matches wanted) | ||||
&& (allowed_match == 0)) { // --- only full matches allow | ||||
ed (remove, if also partial matches wanted) | ||||
match = 1; | ||||
break; | ||||
} | ||||
} | ||||
if(match != 1) { | ||||
traceEvent(TRACE_INFO, "discarded registration with unallowe | ||||
d community '%s'", | ||||
(char*)cmn.community); | ||||
return -1; | ||||
} | ||||
} | ||||
encode_PEER_INFO( encbuf, &encx, &cmn2, &pi ); | if(!comm && (!sss->lock_communities || (match == 1))) { | |||
comm = (struct sn_community*)calloc(1, sizeof(struct sn_communit | ||||
y)); | ||||
if (comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | if(comm) { | |||
packet_header_encrypt (encbuf, encx, comm->header_encryption_ctx, | comm_init(comm, (char *)cmn.community); | |||
comm->header_iv_ctx, | /* new communities introduced by REGISTERs could not have ha | |||
time_stamp (), pearson_hash_16 (enc | d encrypted header... */ | |||
buf, encx)); | comm->header_encryption = HEADER_ENCRYPTION_NONE; | |||
comm->header_encryption_ctx_static = NULL; | ||||
comm->header_encryption_ctx_dynamic = NULL; | ||||
/* ... and also are purgeable during periodic purge */ | ||||
comm->purgeable = COMMUNITY_PURGEABLE; | ||||
comm->number_enc_packets = 0; | ||||
HASH_ADD_STR(sss->communities, community, comm); | ||||
sendto( sss->sock, encbuf, encx, 0, | traceEvent(TRACE_INFO, "new community: %s", comm->community) | |||
(struct sockaddr *)sender_sock, sizeof(struct sockaddr_in) ); | ; | |||
assign_one_ip_subnet(sss, comm); | ||||
} | ||||
} | ||||
traceEvent( TRACE_DEBUG, "Tx PEER_INFO to %s", | if(!comm) { | |||
macaddr_str( mac_buf, query.srcMac ) ); | traceEvent(TRACE_INFO, "discarded registration with unallowed co | |||
} else { | mmunity '%s'", | |||
traceEvent( TRACE_DEBUG, "Ignoring QUERY_PEER for unknown edge %s", | (char*)cmn.community); | |||
macaddr_str( mac_buf, query.targetMac ) ); | return -1; | |||
} | } | |||
// hash check (user/pw auth only) | ||||
if(comm->allowed_users) { | ||||
// check if submitted public key is in list of allowed users | ||||
HASH_FIND(hh, comm->allowed_users, ®.auth.token, sizeof(n2n_p | ||||
rivate_public_key_t), user); | ||||
if(user) { | ||||
speck_128_encrypt(hash_buf, (speck_context_t*)user->shared_s | ||||
ecret_ctx); | ||||
if(memcmp(hash_buf, udp_buf + udp_size - N2N_REG_SUP_HASH_CH | ||||
ECK_LEN /* length has already been checked */, N2N_REG_SUP_HASH_CHECK_LEN)) { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER with wrong has | ||||
h"); | ||||
return -1; | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER from unknown user" | ||||
); | ||||
// continue and let auth check do the rest (otherwise, no NA | ||||
K is sent) | ||||
} | ||||
} | ||||
cmn2.ttl = N2N_DEFAULT_TTL; | ||||
cmn2.pc = n2n_register_super_ack; | ||||
cmn2.flags = N2N_FLAGS_SOCKET | N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); | ||||
ack.cookie = reg.cookie; | ||||
memcpy(ack.srcMac, sss->mac_addr, sizeof(n2n_mac_t)); | ||||
if(comm->is_federation != IS_FEDERATION) { /* alternatively, do not | ||||
send zero tap ip address in federation REGISTER_SUPER */ | ||||
if((reg.dev_addr.net_addr == 0) || (reg.dev_addr.net_addr == 0xF | ||||
FFFFFFF) || (reg.dev_addr.net_bitlen == 0) || | ||||
((reg.dev_addr.net_addr & 0xFFFF0000) == 0xA9FE0000 /* 169.25 | ||||
4.0.0 */)) { | ||||
memset(&ipaddr, 0, sizeof(n2n_ip_subnet_t)); | ||||
assign_one_ip_addr(comm, reg.dev_desc, &ipaddr); | ||||
ack.dev_addr.net_addr = ipaddr.net_addr; | ||||
ack.dev_addr.net_bitlen = ipaddr.net_bitlen; | ||||
} | ||||
} | ||||
ack.lifetime = reg_lifetime(sss); | ||||
ack.sock.family = AF_INET; | ||||
ack.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(ack.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SIZE) | ||||
; | ||||
/* Add sender's data to federation (or update it) */ | ||||
if(comm->is_federation == IS_FEDERATION) { | ||||
skip_add = SN_ADD; | ||||
p = add_sn_to_list_by_mac_or_sock(&(sss->federation->edges), &(a | ||||
ck.sock), reg.edgeMac, &skip_add); | ||||
p->last_seen = now; | ||||
// communication with other supernodes happens via standard udp | ||||
port | ||||
p->socket_fd = sss->sock; | ||||
} | ||||
/* Skip random numbers of supernodes before payload assembling, calc | ||||
ulating an appropriate random_number. | ||||
* That way, all supernodes have a chance to be propagated with REGI | ||||
STER_SUPER_ACK. */ | ||||
skip = HASH_COUNT(sss->federation->edges) - (int)(REG_SUPER_ACK_PAYL | ||||
OAD_ENTRY_SIZE / REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE); | ||||
skip = (skip < 0) ? 0 : n2n_rand_sqr(skip); | ||||
/* Assembling supernode list for REGISTER_SUPER_ACK payload */ | ||||
payload = (n2n_REGISTER_SUPER_ACK_payload_t*)payload_buf; | ||||
HASH_ITER(hh, sss->federation->edges, peer, tmp_peer) { | ||||
if(skip) { | ||||
skip--; | ||||
continue; | ||||
} | ||||
if(peer->sock.family == (uint8_t)AF_INVALID) | ||||
continue; /* do not add unresolved supernodes to payload */ | ||||
if(memcmp(&(peer->sock), &(ack.sock), sizeof(n2n_sock_t)) == 0) | ||||
continue; /* a supernode doesn't add itself to the payload */ | ||||
if((now - peer->last_seen) >= LAST_SEEN_SN_NEW) continue; /* sk | ||||
ip long-time-not-seen supernodes. | ||||
* We | ||||
need to allow for a little extra time because supernodes sometimes exceed | ||||
* th | ||||
eir SN_ACTIVE time before they get re-registred to. */ | ||||
if(((++num)*REG_SUPER_ACK_PAYLOAD_ENTRY_SIZE) > REG_SUPER_ACK_PA | ||||
YLOAD_SPACE) break; /* no more space available in REGISTER_SUPER_ACK payload */ | ||||
memcpy(&(payload->sock), &(peer->sock), sizeof(n2n_sock_t)); | ||||
memcpy(payload->mac, peer->mac_addr, sizeof(n2n_mac_t)); | ||||
// shift to next payload entry | ||||
payload++; | ||||
} | ||||
ack.num_sn = num; | ||||
traceEvent(TRACE_DEBUG, "Rx REGISTER_SUPER for %s [%s]", | ||||
macaddr_str(mac_buf, reg.edgeMac), | ||||
sock_to_cstr(sockbuf, &(ack.sock))); | ||||
// check authentication | ||||
ret_value = update_edge_no_change; | ||||
if(comm->is_federation != IS_FEDERATION) { /* REVISIT: auth among su | ||||
pernodes is not implemented yet */ | ||||
if(cmn.flags & N2N_FLAGS_FROM_SUPERNODE) { | ||||
ret_value = update_edge(sss, &cmn, ®, comm, &(ack.sock), | ||||
socket_fd, &(ack.auth), SN_ADD_SKIP, now); | ||||
} else { | ||||
// do not add in case of null mac (edge asking for ip addres | ||||
s) | ||||
ret_value = update_edge(sss, &cmn, ®, comm, &(ack.sock), | ||||
socket_fd, &(ack.auth), is_null_mac(reg.edgeMac) ? SN_ADD_SKIP : SN_ADD, now); | ||||
} | ||||
} | ||||
if(ret_value == update_edge_auth_fail) { | ||||
// send REGISTER_SUPER_NAK | ||||
cmn2.pc = n2n_register_super_nak; | ||||
nak.cookie = reg.cookie; | ||||
memcpy(nak.srcMac, reg.edgeMac, sizeof(n2n_mac_t)); | ||||
encode_REGISTER_SUPER_NAK(ackbuf, &encx, &cmn2, &nak); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(ackbuf, encx, encx, | ||||
comm->header_encryption_ctx_static, co | ||||
mm->header_iv_ctx_static, | ||||
time_stamp()); | ||||
// if user-password-auth | ||||
if(comm->allowed_users) { | ||||
encode_buf(ackbuf, &encx, hash_buf /* no matter what con | ||||
tent */, N2N_REG_SUP_HASH_CHECK_LEN); | ||||
} | ||||
} | ||||
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, ackb | ||||
uf, encx); | ||||
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_NAK for %s", | ||||
macaddr_str(mac_buf, reg.edgeMac)); | ||||
} else { | ||||
// if this is not already from a supernode ... | ||||
// and not from federation, ... | ||||
if((!(cmn.flags & N2N_FLAGS_FROM_SUPERNODE)) || (!(cmn.flags & N | ||||
2N_FLAGS_SOCKET))) { | ||||
// ... forward to all other supernodes (note try_broadcast() | ||||
's behavior with | ||||
// NULL comm and from_supernode parameter) | ||||
// exception: do not forward auto ip draw | ||||
if(!is_null_mac(reg.edgeMac)) { | ||||
reg.sock.family = AF_INET; | ||||
reg.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(reg.sock.addr.v4, &(sender_sock->sin_addr.s_addr) | ||||
, IPV4_SIZE); | ||||
cmn2.pc = n2n_register_super; | ||||
encode_REGISTER_SUPER(ackbuf, &encx, &cmn2, ®); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
{ | ||||
packet_header_encrypt(ackbuf, encx, encx, | ||||
comm->header_encryption_ctx_st | ||||
atic, comm->header_iv_ctx_static, | ||||
time_stamp()); | ||||
// if user-password-auth | ||||
if(comm->allowed_users) { | ||||
// append an encrypted packet hash | ||||
pearson_hash_128(hash_buf, ackbuf, encx); | ||||
// same 'user' as above | ||||
speck_128_encrypt(hash_buf, (speck_context_t*)use | ||||
r->shared_secret_ctx); | ||||
encode_buf(ackbuf, &encx, hash_buf, N2N_REG_SUP_H | ||||
ASH_CHECK_LEN); | ||||
} | ||||
} | ||||
try_broadcast(sss, NULL, &cmn, reg.edgeMac, from_superno | ||||
de, ackbuf, encx); | ||||
} | ||||
// dynamic key time handling if appropriate | ||||
ack.key_time = 0; | ||||
if(comm->is_federation == IS_FEDERATION) { | ||||
if(reg.key_time > sss->dynamic_key_time) { | ||||
traceEvent(TRACE_DEBUG, "setting new key time"); | ||||
// have all edges re_register (using old dynamic key | ||||
) | ||||
send_re_register_super(sss); | ||||
// set new key time | ||||
sss->dynamic_key_time = reg.key_time; | ||||
// calculate new dynamic keys for all communities | ||||
calculate_dynamic_keys(sss); | ||||
// force re-register with all supernodes | ||||
re_register_and_purge_supernodes(sss, sss->federatio | ||||
n, &any_time, now, 1 /* forced */); | ||||
} | ||||
ack.key_time = sss->dynamic_key_time; | ||||
} | ||||
// send REGISTER_SUPER_ACK | ||||
encx = 0; | ||||
cmn2.pc = n2n_register_super_ack; | ||||
encode_REGISTER_SUPER_ACK(ackbuf, &encx, &cmn2, &ack, payloa | ||||
d_buf); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(ackbuf, encx, encx, | ||||
comm->header_encryption_ctx_static | ||||
, comm->header_iv_ctx_static, | ||||
time_stamp()); | ||||
// if user-password-auth | ||||
if(comm->allowed_users) { | ||||
// append an encrypted packet hash | ||||
pearson_hash_128(hash_buf, ackbuf, encx); | ||||
// same 'user' as above | ||||
speck_128_encrypt(hash_buf, (speck_context_t*)user->s | ||||
hared_secret_ctx); | ||||
encode_buf(ackbuf, &encx, hash_buf, N2N_REG_SUP_HASH_ | ||||
CHECK_LEN); | ||||
} | ||||
} | ||||
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, | ||||
ackbuf, encx); | ||||
traceEvent(TRACE_DEBUG, "Tx REGISTER_SUPER_ACK for %s [%s]", | ||||
macaddr_str(mac_buf, reg.edgeMac), | ||||
sock_to_cstr(sockbuf, &(ack.sock))); | ||||
} else { | ||||
// this is an edge with valid authentication registering wit | ||||
h another supernode, so ... | ||||
// 1- ... associate it with that other supernode | ||||
update_node_supernode_association(comm, &(reg.edgeMac), send | ||||
er_sock, now); | ||||
// 2- ... we can delete it from regular list if present (can | ||||
happen) | ||||
HASH_FIND_PEER(comm->edges, reg.edgeMac, peer); | ||||
if(peer != NULL) { | ||||
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= | ||||
0)) { | ||||
n2n_tcp_connection_t *conn; | ||||
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_f | ||||
d), conn); | ||||
close_tcp_connection(sss, conn); /* also deletes the | ||||
peer */ | ||||
} else { | ||||
HASH_DEL(comm->edges, peer); | ||||
free(peer); | ||||
} | ||||
} | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_UNREGISTER_SUPER: { | ||||
n2n_UNREGISTER_SUPER_t unreg; | ||||
struct peer_info *peer; | ||||
int auth; | ||||
memset(&unreg, 0, sizeof(n2n_UNREGISTER_SUPER_t)); | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "dropped UNREGISTER_SUPER with unknown c | ||||
ommunity %s", cmn.community); | ||||
return -1; | ||||
} | ||||
if((from_supernode == 1) || (comm->is_federation == IS_FEDERATION)) | ||||
{ | ||||
traceEvent(TRACE_DEBUG, "dropped UNREGISTER_SUPER: should not co | ||||
me from a supernode or federation."); | ||||
return -1; | ||||
} | ||||
decode_UNREGISTER_SUPER(&unreg, &cmn, udp_buf, &rem, &idx); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, unreg.srcMa | ||||
c, stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped UNREGISTER_SUPER due to tim | ||||
e stamp error"); | ||||
return -1; | ||||
} | ||||
} | ||||
traceEvent(TRACE_DEBUG, "Rx UNREGISTER_SUPER from %s", | ||||
macaddr_str(mac_buf, unreg.srcMac)); | ||||
HASH_FIND_PEER(comm->edges, unreg.srcMac, peer); | ||||
if(peer != NULL) { | ||||
if((auth = auth_edge(&(peer->auth), &unreg.auth, NULL, comm)) == | ||||
0) { | ||||
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) | ||||
{ | ||||
n2n_tcp_connection_t *conn; | ||||
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), | ||||
conn); | ||||
close_tcp_connection(sss, conn); /* also deletes the pee | ||||
r */ | ||||
} else { | ||||
HASH_DEL(comm->edges, peer); | ||||
free(peer); | ||||
} | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_SUPER_ACK: { | ||||
n2n_REGISTER_SUPER_ACK_t ack; | ||||
struct peer_info *scan, *tmp; | ||||
n2n_sock_str_t sockbuf1; | ||||
n2n_sock_str_t sockbuf2; | ||||
macstr_t mac_buf1; | ||||
n2n_sock_t sender; | ||||
n2n_sock_t *orig_sender; | ||||
int i; | ||||
uint8_t dec_tmpbuf[REG_SUPER_ACK_PAYLOAD_SP | ||||
ACE]; | ||||
n2n_REGISTER_SUPER_ACK_payload_t *payload; | ||||
memset(&sender, 0, sizeof(n2n_sock_t)); | ||||
sender.family = AF_INET; | ||||
sender.port = ntohs(sender_sock->sin_port); | ||||
memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE | ||||
); | ||||
orig_sender = &sender; | ||||
memset(&ack, 0, sizeof(n2n_REGISTER_SUPER_ACK_t)); | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "REGISTER_SUPER_ACK with unknown communi | ||||
ty %s", cmn.community); | ||||
return -1; | ||||
} | ||||
if((from_supernode == 0) || (comm->is_federation == IS_NO_FEDERATION | ||||
)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER_ACK, should not | ||||
come from an edge or regular community"); | ||||
return -1; | ||||
} | ||||
decode_REGISTER_SUPER_ACK(&ack, &cmn, udp_buf, &rem, &idx, dec_tmpbu | ||||
f); | ||||
orig_sender = &(ack.sock); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, ack.srcMac, | ||||
stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER_ACK due to t | ||||
ime stamp error"); | ||||
return -1; | ||||
} | ||||
} | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK from MAC %s [%s] (exte | ||||
rnal %s)", | ||||
macaddr_str(mac_buf1, ack.srcMac), | ||||
sock_to_cstr(sockbuf1, &sender), | ||||
sock_to_cstr(sockbuf2, orig_sender)); | ||||
skip_add = SN_ADD_SKIP; | ||||
scan = add_sn_to_list_by_mac_or_sock(&(sss->federation->edges), &sen | ||||
der, ack.srcMac, &skip_add); | ||||
if(scan != NULL) { | ||||
scan->last_seen = now; | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "dropped REGISTER_SUPER_ACK due to an un | ||||
known supernode"); | ||||
break; | ||||
} | ||||
break; | if(ack.cookie == scan->last_cookie) { | |||
} | ||||
default: | ||||
/* Not a known message type */ | ||||
traceEvent(TRACE_WARNING, "Unable to handle packet type %d: ignored", (signe | ||||
d int)msg_type); | ||||
} /* switch(msg_type) */ | ||||
return 0; | payload = (n2n_REGISTER_SUPER_ACK_payload_t *)dec_tmpbuf; | |||
for(i = 0; i < ack.num_sn; i++) { | ||||
skip_add = SN_ADD; | ||||
tmp = add_sn_to_list_by_mac_or_sock(&(sss->federation->edges | ||||
), &(payload->sock), payload->mac, &skip_add); | ||||
// other supernodes communicate via standard udp socket | ||||
tmp->socket_fd = sss->sock; | ||||
if(skip_add == SN_ADD_ADDED) { | ||||
tmp->last_seen = now - LAST_SEEN_SN_NEW; | ||||
} | ||||
// shift to next payload entry | ||||
payload++; | ||||
} | ||||
if(ack.key_time > sss->dynamic_key_time) { | ||||
traceEvent(TRACE_DEBUG, "setting new key time"); | ||||
// have all edges re_register (using old dynamic key) | ||||
send_re_register_super(sss); | ||||
// set new key time | ||||
sss->dynamic_key_time = ack.key_time; | ||||
// calculate new dynamic keys for all communities | ||||
calculate_dynamic_keys(sss); | ||||
// force re-register with all supernodes | ||||
re_register_and_purge_supernodes(sss, sss->federation, &any_ | ||||
time, now, 1 /* forced */); | ||||
} | ||||
} else { | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_ACK with wrong or old | ||||
cookie"); | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_REGISTER_SUPER_NAK: { | ||||
n2n_REGISTER_SUPER_NAK_t nak; | ||||
uint8_t nakbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
struct peer_info *peer; | ||||
n2n_sock_str_t sockbuf; | ||||
macstr_t mac_buf; | ||||
n2n_sock_t sender; | ||||
memset(&sender, 0, sizeof(n2n_sock_t)); | ||||
sender.family = AF_INET; | ||||
sender.port = ntohs(sender_sock->sin_port); | ||||
memcpy(&(sender.addr.v4), &(sender_sock->sin_addr.s_addr), IPV4_SIZE | ||||
); | ||||
memset(&nak, 0, sizeof(n2n_REGISTER_SUPER_NAK_t)); | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "REGISTER_SUPER_NAK with unknown communi | ||||
ty %s", cmn.community); | ||||
return -1; | ||||
} | ||||
decode_REGISTER_SUPER_NAK(&nak, &cmn, udp_buf, &rem, &idx); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, nak.srcMac, | ||||
stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "process_udp dropped REGISTER_SUPER_ | ||||
NAK due to time stamp error"); | ||||
return -1; | ||||
} | ||||
} | ||||
traceEvent(TRACE_INFO, "Rx REGISTER_SUPER_NAK from %s [%s]", | ||||
macaddr_str(mac_buf, nak.srcMac), | ||||
sock_to_cstr(sockbuf, &sender)); | ||||
HASH_FIND_PEER(comm->edges, nak.srcMac, peer); | ||||
if(comm->is_federation == IS_NO_FEDERATION) { | ||||
if(peer != NULL) { | ||||
// this is a NAK for one of the edges conencted to this supe | ||||
rnode, forward, | ||||
// i.e. re-assemble (memcpy from udpbuf to nakbuf could be s | ||||
ufficient as well) | ||||
// use incoming cmn (with already decreased TTL) | ||||
// NAK (cookie, srcMac, auth) remains unchanged | ||||
encode_REGISTER_SUPER_NAK(nakbuf, &encx, &cmn, &nak); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(nakbuf, encx, encx, | ||||
comm->header_encryption_ctx_static | ||||
, comm->header_iv_ctx_static, | ||||
time_stamp()); | ||||
// if user-password-auth | ||||
if(comm->allowed_users) { | ||||
encode_buf(nakbuf, &encx, hash_buf /* no matter what | ||||
content */, N2N_REG_SUP_HASH_CHECK_LEN); | ||||
} | ||||
} | ||||
sendto_peer(sss, peer, nakbuf, encx); | ||||
if((peer->socket_fd != sss->sock) && (peer->socket_fd >= 0)) | ||||
{ | ||||
n2n_tcp_connection_t *conn; | ||||
HASH_FIND_INT(sss->tcp_connections, &(peer->socket_fd), | ||||
conn); | ||||
close_tcp_connection(sss, conn); /* also deletes the pee | ||||
r */ | ||||
} else { | ||||
HASH_DEL(comm->edges, peer); | ||||
free(peer); | ||||
} | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_QUERY_PEER: { | ||||
n2n_QUERY_PEER_t query; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
n2n_common_t cmn2; | ||||
n2n_PEER_INFO_t pi; | ||||
struct sn_community_regular_expression *re, *tmp_re; | ||||
int8_t allowed_match = -1; | ||||
uint8_t match = 0; | ||||
int match_length = 0; | ||||
if(!comm && sss->lock_communities) { | ||||
HASH_ITER(hh, sss->rules, re, tmp_re) { | ||||
allowed_match = re_matchp(re->rule, (const char *)cmn.commun | ||||
ity, &match_length); | ||||
if((allowed_match != -1) | ||||
&& (match_length == strlen((const char *)cmn.community)) | ||||
// --- only full matches allowed (remove, if also partial matches wanted) | ||||
&& (allowed_match == 0)) { | ||||
// --- only full matches allowed (remove, if also partial matches wanted) | ||||
match = 1; | ||||
break; | ||||
} | ||||
} | ||||
if(match != 1) { | ||||
traceEvent(TRACE_DEBUG, "QUERY_PEER from unknown community % | ||||
s", cmn.community); | ||||
return -1; | ||||
} | ||||
} | ||||
if(!comm && sss->lock_communities && (match == 0)) { | ||||
traceEvent(TRACE_DEBUG, "QUERY_PEER from not allowed community % | ||||
s", cmn.community); | ||||
return -1; | ||||
} | ||||
decode_QUERY_PEER( &query, &cmn, udp_buf, &rem, &idx ); | ||||
// to answer a PING, it is sufficient if the provided communtiy woul | ||||
d be a valid one, there does not | ||||
// neccessarily need to be a comm entry present, e.g. because there | ||||
locally are no edges of the | ||||
// community connected (several supernodes in a federation setup) | ||||
if(comm) { | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, query.s | ||||
rcMac, stamp, TIME_STAMP_ALLOW_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped QUERY_PEER due to time | ||||
stamp error"); | ||||
return -1; | ||||
} | ||||
} | ||||
} | ||||
if(is_null_mac(query.targetMac)) { | ||||
traceEvent(TRACE_DEBUG, "Rx PING from %s", | ||||
macaddr_str(mac_buf, query.srcMac)); | ||||
cmn2.ttl = N2N_DEFAULT_TTL; | ||||
cmn2.pc = n2n_peer_info; | ||||
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t)); | ||||
pi.aflags = 0; | ||||
memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t)); | ||||
memcpy(pi.srcMac, sss->mac_addr, sizeof(n2n_mac_t)); | ||||
pi.sock.family = AF_INET; | ||||
pi.sock.port = ntohs(sender_sock->sin_port); | ||||
memcpy(pi.sock.addr.v4, &(sender_sock->sin_addr.s_addr), IPV4_SI | ||||
ZE); | ||||
pi.load = sn_selection_criterion_gather_data(sss); | ||||
snprintf(pi.version, sizeof(pi.version), "%s", sss->version); | ||||
pi.uptime = now - sss->start_time; | ||||
encode_PEER_INFO(encbuf, &encx, &cmn2, &pi); | ||||
if(comm) { | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(encbuf, encx, encx, comm->header_e | ||||
ncryption_ctx_dynamic, | ||||
comm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
} | ||||
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, encb | ||||
uf, encx); | ||||
traceEvent(TRACE_DEBUG, "Tx PONG to %s", | ||||
macaddr_str(mac_buf, query.srcMac)); | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "Rx QUERY_PEER from %s for %s", | ||||
macaddr_str(mac_buf, query.srcMac), | ||||
macaddr_str(mac_buf2, query.targetMac)); | ||||
struct peer_info *scan; | ||||
// as opposed to the special case 'PING', proper QUERY_PEER proc | ||||
essing requires a locally actually present community entry | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "QUERY_PEER with unknown community % | ||||
s", cmn.community); | ||||
return -1; | ||||
} | ||||
HASH_FIND_PEER(comm->edges, query.targetMac, scan); | ||||
if(scan) { | ||||
cmn2.ttl = N2N_DEFAULT_TTL; | ||||
cmn2.pc = n2n_peer_info; | ||||
cmn2.flags = N2N_FLAGS_FROM_SUPERNODE; | ||||
memcpy(cmn2.community, cmn.community, sizeof(n2n_community_t | ||||
)); | ||||
pi.aflags = 0; | ||||
memcpy(pi.srcMac, query.srcMac, sizeof(n2n_mac_t)); | ||||
memcpy(pi.mac, query.targetMac, sizeof(n2n_mac_t)); | ||||
pi.sock = scan->sock; | ||||
if(scan->preferred_sock.family != (uint8_t)AF_INVALID) { | ||||
cmn2.flags |= N2N_FLAGS_SOCKET; | ||||
pi.preferred_sock = scan->preferred_sock; | ||||
} | ||||
encode_PEER_INFO(encbuf, &encx, &cmn2, &pi); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(encbuf, encx, encx, comm->header_e | ||||
ncryption_ctx_dynamic, | ||||
comm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
// back to sender, be it edge or supernode (which will forwa | ||||
rd to edge) | ||||
sendto_sock(sss, socket_fd, (struct sockaddr *)sender_sock, | ||||
encbuf, encx); | ||||
traceEvent(TRACE_DEBUG, "Tx PEER_INFO to %s", | ||||
macaddr_str(mac_buf, query.srcMac)); | ||||
} else { | ||||
if(from_supernode) { | ||||
traceEvent(TRACE_DEBUG, "QUERY_PEER on unknown edge from | ||||
supernode %s, dropping the packet", | ||||
macaddr_str(mac_buf, query.srcMac)); | ||||
} else { | ||||
traceEvent(TRACE_DEBUG, "QUERY_PEER from unknown edge %s | ||||
, forwarding to all other supernodes", | ||||
macaddr_str(mac_buf, query.srcMac)); | ||||
memcpy(&cmn2, &cmn, sizeof(n2n_common_t)); | ||||
cmn2.flags |= N2N_FLAGS_FROM_SUPERNODE; | ||||
encode_QUERY_PEER(encbuf, &encx, &cmn2, &query); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) | ||||
{ | ||||
packet_header_encrypt(encbuf, encx, encx, comm->head | ||||
er_encryption_ctx_dynamic, | ||||
comm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
try_broadcast(sss, NULL, &cmn, query.srcMac, from_supern | ||||
ode, encbuf, encx); | ||||
} | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
case MSG_TYPE_PEER_INFO: { | ||||
n2n_PEER_INFO_t pi; | ||||
uint8_t encbuf[N2N_SN_PKTBUF_SIZE]; | ||||
size_t encx = 0; | ||||
struct peer_info *peer; | ||||
if(!comm) { | ||||
traceEvent(TRACE_DEBUG, "PEER_INFO with unknown community %s", c | ||||
mn.community); | ||||
return -1; | ||||
} | ||||
decode_PEER_INFO(&pi, &cmn, udp_buf, &rem, &idx); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
if(!find_edge_time_stamp_and_verify(comm->edges, sn, pi.srcMac, | ||||
stamp, TIME_STAMP_NO_JITTER)) { | ||||
traceEvent(TRACE_DEBUG, "dropped PEER_INFO due to time stamp | ||||
error"); | ||||
return -1; | ||||
} | ||||
} | ||||
traceEvent(TRACE_INFO, "Rx PEER_INFO from %s [%s]", | ||||
macaddr_str(mac_buf, pi.srcMac), | ||||
sock_to_cstr(sockbuf, &sender)); | ||||
HASH_FIND_PEER(comm->edges, pi.srcMac, peer); | ||||
if(peer != NULL) { | ||||
if((comm->is_federation == IS_NO_FEDERATION) && (!is_null_mac(pi | ||||
.srcMac))) { | ||||
// snoop on the information to use for supernode forwarding | ||||
(do not wait until first remote REGISTER_SUPER) | ||||
update_node_supernode_association(comm, &(pi.mac), sender_so | ||||
ck, now); | ||||
// this is a PEER_INFO for one of the edges conencted to thi | ||||
s supernode, forward, | ||||
// i.e. re-assemble (memcpy of udpbuf to encbuf could be suf | ||||
ficient as well) | ||||
// use incoming cmn (with already decreased TTL) | ||||
// PEER_INFO remains unchanged | ||||
encode_PEER_INFO(encbuf, &encx, &cmn, &pi); | ||||
if(comm->header_encryption == HEADER_ENCRYPTION_ENABLED) { | ||||
packet_header_encrypt(encbuf, encx, encx, | ||||
comm->header_encryption_ctx_dynami | ||||
c, comm->header_iv_ctx_dynamic, | ||||
time_stamp()); | ||||
} | ||||
sendto_peer(sss, peer, encbuf, encx); | ||||
} | ||||
} | ||||
break; | ||||
} | ||||
default: | ||||
/* Not a known message type */ | ||||
traceEvent(TRACE_WARNING, "unable to handle packet type %d: ignored" | ||||
, (signed int)msg_type); | ||||
} /* switch(msg_type) */ | ||||
return 0; | ||||
} | } | |||
/** Long lived processing entry point. Split out from main to simply | /** Long lived processing entry point. Split out from main to simply | |||
* daemonisation on some platforms. */ | * daemonisation on some platforms. */ | |||
int run_sn_loop(n2n_sn_t *sss, int *keep_running) | int run_sn_loop (n2n_sn_t *sss) { | |||
{ | ||||
uint8_t pktbuf[N2N_SN_PKTBUF_SIZE]; | uint8_t pktbuf[N2N_SN_PKTBUF_SIZE]; | |||
time_t last_purge_edges = 0; | time_t last_purge_edges = 0; | |||
time_t last_sort_communities = 0; | time_t last_sort_communities = 0; | |||
time_t last_re_reg_and_purge = 0; | ||||
sss->start_time = time(NULL); | sss->start_time = time(NULL); | |||
while (*keep_running) | while(*sss->keep_running) { | |||
{ | ||||
int rc; | int rc; | |||
ssize_t bread; | ssize_t bread; | |||
int max_sock; | int max_sock; | |||
fd_set socket_mask; | fd_set socket_mask; | |||
n2n_tcp_connection_t *conn, *tmp_conn; | ||||
#ifdef N2N_HAVE_TCP | ||||
SOCKET tmp_sock; | ||||
n2n_sock_str_t sockbuf; | ||||
#endif | ||||
struct timeval wait_time; | struct timeval wait_time; | |||
time_t now = 0; | time_t before, now = 0; | |||
FD_ZERO(&socket_mask); | FD_ZERO(&socket_mask); | |||
max_sock = MAX(sss->sock, sss->mgmt_sock); | ||||
FD_SET(sss->sock, &socket_mask); | FD_SET(sss->sock, &socket_mask); | |||
#ifdef N2N_HAVE_TCP | ||||
FD_SET(sss->tcp_sock, &socket_mask); | ||||
#endif | ||||
FD_SET(sss->mgmt_sock, &socket_mask); | FD_SET(sss->mgmt_sock, &socket_mask); | |||
max_sock = MAX(MAX(sss->sock, sss->mgmt_sock), sss->tcp_sock); | ||||
#ifdef N2N_HAVE_TCP | ||||
// add the tcp connections' sockets | ||||
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) { | ||||
//socket descriptor | ||||
FD_SET(conn->socket_fd, &socket_mask); | ||||
if(conn->socket_fd > max_sock) | ||||
max_sock = conn->socket_fd; | ||||
} | ||||
#endif | ||||
wait_time.tv_sec = 10; | wait_time.tv_sec = 10; | |||
wait_time.tv_usec = 0; | wait_time.tv_usec = 0; | |||
before = time(NULL); | ||||
rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time); | rc = select(max_sock + 1, &socket_mask, NULL, NULL, &wait_time); | |||
now = time(NULL); | now = time(NULL); | |||
if (rc > 0) | if(rc > 0) { | |||
{ | ||||
if (FD_ISSET(sss->sock, &socket_mask)) | // external udp | |||
{ | if(FD_ISSET(sss->sock, &socket_mask)) { | |||
struct sockaddr_in sender_sock; | struct sockaddr_in sender_sock; | |||
socklen_t i; | socklen_t i; | |||
i = sizeof(sender_sock); | i = sizeof(sender_sock); | |||
bread = recvfrom(sss->sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0 /*flag s*/, | bread = recvfrom(sss->sock, (void *)pktbuf, N2N_SN_PKTBUF_SIZE, 0 /*flags*/, | |||
(struct sockaddr *)&sender_sock, (socklen_t *)& i); | (struct sockaddr *)&sender_sock, (socklen_t *)& i); | |||
if ((bread < 0) | if((bread < 0) | |||
#ifdef WIN32 | #ifdef WIN32 | |||
&& (WSAGetLastError() != WSAECONNRESET) | && (WSAGetLastError() != WSAECONNRESET) | |||
#endif | #endif | |||
) | ) { | |||
{ | ||||
/* For UDP bread of zero just means no data (unlike TCP). */ | /* For UDP bread of zero just means no data (unlike TCP). */ | |||
/* The fd is no good now. Maybe we lost our interface. */ | /* The fd is no good now. Maybe we lost our interface. */ | |||
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)" , bread, errno, strerror(errno)); | traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)" , bread, errno, strerror(errno)); | |||
#ifdef WIN32 | #ifdef WIN32 | |||
traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastE rror()); | traceEvent(TRACE_ERROR, "WSAGetLastError(): %u", WSAGetLastE rror()); | |||
#endif | #endif | |||
*keep_running = 0; | *sss->keep_running = 0; | |||
break; | break; | |||
} | } | |||
/* We have a datagram to process */ | // we have a datagram to process... | |||
if (bread > 0) | if(bread > 0) { | |||
{ | // ...and the datagram has data (not just a header) | |||
/* And the datagram has data (not just a header) */ | process_udp(sss, &sender_sock, sss->sock, pktbuf, bread, now | |||
process_udp(sss, &sender_sock, pktbuf, bread, now); | ); | |||
} | ||||
} | ||||
#ifdef N2N_HAVE_TCP | ||||
// the so far known tcp connections | ||||
// beware: current conn and other items of the connection list may b | ||||
e found | ||||
// due for deletion while processing packets. Even OTHER connections | ||||
, e.g. if | ||||
// forwarding to another edge node fails. connections due for deleti | ||||
on will | ||||
// not immediately be deleted but marked 'inactive' for later deleti | ||||
on | ||||
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) { | ||||
// do not process entries that have been marked inactive, those | ||||
will be deleted | ||||
// immediately after this loop | ||||
if(conn->inactive) | ||||
continue; | ||||
if(FD_ISSET(conn->socket_fd, &socket_mask)) { | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
i = sizeof(sender_sock); | ||||
bread = recvfrom(conn->socket_fd, | ||||
conn->buffer + conn->position, conn->expect | ||||
ed - conn->position, 0 /*flags*/, | ||||
(struct sockaddr *)&sender_sock, (socklen_t | ||||
*)&i); | ||||
if(bread <= 0) { | ||||
traceEvent(TRACE_INFO, "closing tcp connection to [%s]", | ||||
sock_to_cstr(sockbuf, (n2n_sock_t*)&sender_sock)); | ||||
traceEvent(TRACE_DEBUG, "recvfrom() returns %d and sees | ||||
errno %d (%s)", bread, errno, strerror(errno)); | ||||
#ifdef WIN32 | ||||
traceEvent(TRACE_DEBUG, "WSAGetLastError(): %u", WSAGetL | ||||
astError()); | ||||
#endif | ||||
close_tcp_connection(sss, conn); | ||||
continue; | ||||
} | ||||
conn->position += bread; | ||||
if(conn->position == conn->expected) { | ||||
if(conn->position == sizeof(uint16_t)) { | ||||
// the prepended length has been read, preparing for | ||||
the packet | ||||
conn->expected += be16toh(*(uint16_t*)(conn->buffer) | ||||
); | ||||
if(conn->expected > N2N_SN_PKTBUF_SIZE) { | ||||
traceEvent(TRACE_INFO, "closing tcp connection t | ||||
o [%s]", sock_to_cstr(sockbuf, (n2n_sock_t*)&sender_sock)); | ||||
traceEvent(TRACE_DEBUG, "too many bytes in tcp p | ||||
acket expected"); | ||||
close_tcp_connection(sss, conn); | ||||
continue; | ||||
} | ||||
} else { | ||||
// full packet read, handle it | ||||
process_udp(sss, (struct sockaddr_in*)&(conn->sock), | ||||
conn->socket_fd, | ||||
conn->buffer + sizeof(uint16_t), co | ||||
nn->position - sizeof(uint16_t), now); | ||||
// reset, await new prepended length | ||||
conn->expected = sizeof(uint16_t); | ||||
conn->position = 0; | ||||
} | ||||
} | ||||
} | ||||
} | ||||
// remove inactive / already closed tcp connections from list | ||||
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) { | ||||
if(conn->inactive) { | ||||
HASH_DEL(sss->tcp_connections, conn); | ||||
free(conn); | ||||
} | ||||
} | ||||
// accept new incoming tcp connection | ||||
if(FD_ISSET(sss->tcp_sock, &socket_mask)) { | ||||
struct sockaddr_in sender_sock; | ||||
socklen_t i; | ||||
i = sizeof(sender_sock); | ||||
if((HASH_COUNT(sss->tcp_connections) + 4) < FD_SETSIZE) { | ||||
tmp_sock = accept(sss->tcp_sock, (struct sockaddr *)&sender_ | ||||
sock, (socklen_t *)&i); | ||||
if(tmp_sock >= 0) { | ||||
conn = (n2n_tcp_connection_t*)malloc(sizeof(n2n_tcp_conn | ||||
ection_t)); | ||||
if(conn) { | ||||
conn->socket_fd = tmp_sock; | ||||
memcpy(&(conn->sock), &sender_sock, sizeof(struct so | ||||
ckaddr_in)); | ||||
conn->inactive = 0; | ||||
conn->expected = sizeof(uint16_t); | ||||
conn->position = 0; | ||||
HASH_ADD_INT(sss->tcp_connections, socket_fd, conn); | ||||
traceEvent(TRACE_INFO, "accepted incoming TCP connec | ||||
tion from [%s]", | ||||
sock_to_cstr(sockbuf, (n2n_so | ||||
ck_t*)&sender_sock)); | ||||
} | ||||
} | ||||
} else { | ||||
// no space to store the socket for a new connection, cl | ||||
ose immediately | ||||
traceEvent(TRACE_DEBUG, "denied incoming TCP connection | ||||
from [%s] due to max connections limit hit", | ||||
sock_to_cstr(sockbuf, (n2n_sock_ | ||||
t*)&sender_sock)); | ||||
} | } | |||
} | } | |||
#endif /* N2N_HAVE_TCP */ | ||||
if (FD_ISSET(sss->mgmt_sock, &socket_mask)) | // handle management port input | |||
{ | if(FD_ISSET(sss->mgmt_sock, &socket_mask)) { | |||
struct sockaddr_in sender_sock; | struct sockaddr_in sender_sock; | |||
size_t i; | size_t i; | |||
i = sizeof(sender_sock); | i = sizeof(sender_sock); | |||
bread = recvfrom(sss->mgmt_sock, pktbuf, N2N_SN_PKTBUF_SIZE, 0 / *flags*/, | bread = recvfrom(sss->mgmt_sock, (void *)pktbuf, N2N_SN_PKTBUF_S IZE, 0 /*flags*/, | |||
(struct sockaddr *)&sender_sock, (socklen_t *)& i); | (struct sockaddr *)&sender_sock, (socklen_t *)& i); | |||
if (bread <= 0) | if(bread <= 0) { | |||
{ | ||||
traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)" , bread, errno, strerror(errno)); | traceEvent(TRACE_ERROR, "recvfrom() failed %d errno %d (%s)" , bread, errno, strerror(errno)); | |||
*keep_running = 0; | *sss->keep_running = 0; | |||
break; | break; | |||
} | } | |||
/* We have a datagram to process */ | // we have a datagram to process | |||
process_mgmt(sss, &sender_sock, pktbuf, bread, now); | process_mgmt(sss, &sender_sock, (char *)pktbuf, bread, now); | |||
} | } | |||
} | ||||
else | } else { | |||
{ | if(((now - before) < wait_time.tv_sec) && (*sss->keep_running)){ | |||
traceEvent(TRACE_DEBUG, "timeout"); | // this is no real timeout, something went wrong with one of the | |||
tcp connections (probably) | ||||
// close them all, edges will re-open if they detect closure | ||||
traceEvent(TRACE_DEBUG, "falsly claimed timeout, assuming issue | ||||
with tcp connection, closing them all"); | ||||
HASH_ITER(hh, sss->tcp_connections, conn, tmp_conn) | ||||
close_tcp_connection(sss, conn); | ||||
} else | ||||
traceEvent(TRACE_DEBUG, "timeout"); | ||||
} | } | |||
re_register_and_purge_supernodes(sss, sss->federation, &last_re_reg_and_ purge, now, 0 /* not forced */); | ||||
purge_expired_communities(sss, &last_purge_edges, now); | purge_expired_communities(sss, &last_purge_edges, now); | |||
sort_communities (sss, &last_sort_communities, now); | sort_communities(sss, &last_sort_communities, now); | |||
resolve_check(sss->resolve_parameter, 0 /* presumably, no special resolu | ||||
tion requirement */, now); | ||||
} /* while */ | } /* while */ | |||
sn_term(sss); | sn_term(sss); | |||
return 0; | return 0; | |||
} | } | |||
End of changes. 146 change blocks. | ||||
788 lines changed or deleted | 2952 lines changed or added |