"Fossies" - the Fresh Open Source Software Archive  

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

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

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, &reg->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, &reg->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(&reg, 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, &reg);
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(&reg, &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(&reg, &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, &reg);
} 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(&reg, &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, &reg);
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(&reg, &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, &reg.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, &reg, 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, &reg, 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, &reg);
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

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