"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/n2n.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.

n2n.c  (n2n-2.8):n2n.c  (n2n-3.0)
/** /**
* (C) 2007-20 - ntop.org and contributors * (C) 2007-21 - ntop.org and contributors
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or * the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not see see <http://www.gnu.org/licenses/> * along with this program; if not see see <http://www.gnu.org/licenses/>
* *
*/ */
#include "n2n.h" #include "n2n.h"
#include "sn_selection.h"
#include "minilzo.h" #include "minilzo.h"
#include <assert.h> #include <assert.h>
static const uint8_t broadcast_addr[6] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
static const uint8_t multicast_addr[6] = { 0x01, 0x00, 0x5E, 0x00, 0x00, 0x00 };
/* First 3 bytes are meaningful */
static const uint8_t ipv6_multicast_addr[6] = { 0x33, 0x33, 0x00, 0x00, 0x00, 0x
00 }; /* First 2 bytes are meaningful */
/* ************************************** */ /* ************************************** */
SOCKET open_socket(int local_port, int bind_any) { SOCKET open_socket (int local_port, in_addr_t address, int type /* 0 = UDP, TCP
SOCKET sock_fd; otherwise */) {
struct sockaddr_in local_address;
int sockopt; SOCKET sock_fd;
struct sockaddr_in local_address;
if((sock_fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { int sockopt;
traceEvent(TRACE_ERROR, "Unable to create socket [%s][%d]\n",
strerror(errno), sock_fd); if((int)(sock_fd = socket(PF_INET, ((type == 0) ? SOCK_DGRAM : SOCK_STREAM)
return(-1); , 0)) < 0) {
} traceEvent(TRACE_ERROR, "Unable to create socket [%s][%d]\n",
strerror(errno), sock_fd);
return(-1);
}
#ifndef WIN32 #ifndef WIN32
/* fcntl(sock_fd, F_SETFL, O_NONBLOCK); */ /* fcntl(sock_fd, F_SETFL, O_NONBLOCK); */
#endif #endif
sockopt = 1; sockopt = 1;
setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(sockopt setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&sockopt, sizeof(socko
)); pt));
memset(&local_address, 0, sizeof(local_address)); memset(&local_address, 0, sizeof(local_address));
local_address.sin_family = AF_INET; local_address.sin_family = AF_INET;
local_address.sin_port = htons(local_port); local_address.sin_port = htons(local_port);
local_address.sin_addr.s_addr = htonl(bind_any ? INADDR_ANY : INADDR_LOOPBACK) local_address.sin_addr.s_addr = htonl(address);
;
if(bind(sock_fd,(struct sockaddr*) &local_address, sizeof(local_address)) ==
if(bind(sock_fd,(struct sockaddr*) &local_address, sizeof(local_address)) == - -1) {
1) { traceEvent(TRACE_ERROR, "Bind error on local port %u [%s]\n", local_port
traceEvent(TRACE_ERROR, "Bind error on local port %u [%s]\n", local_port, st , strerror(errno));
rerror(errno)); return(-1);
return(-1); }
}
return(sock_fd); return(sock_fd);
} }
static int traceLevel = 2 /* NORMAL */; static int traceLevel = 2 /* NORMAL */;
static int useSyslog = 0, syslog_opened = 0; static int useSyslog = 0, syslog_opened = 0;
static FILE *traceFile = NULL; static FILE *traceFile = NULL;
int getTraceLevel() { int getTraceLevel () {
return(traceLevel);
return(traceLevel);
} }
void setTraceLevel(int level) { void setTraceLevel (int level) {
traceLevel = level;
traceLevel = level;
} }
void setUseSyslog(int use_syslog) { void setUseSyslog (int use_syslog) {
useSyslog= use_syslog;
useSyslog = use_syslog;
} }
void setTraceFile(FILE *f) { void setTraceFile (FILE *f) {
traceFile = f;
traceFile = f;
} }
void closeTraceFile() { void closeTraceFile () {
if (traceFile != NULL && traceFile != stdout) {
fclose(traceFile); if((traceFile != NULL) && (traceFile != stdout)) {
} fclose(traceFile);
}
#ifndef WIN32 #ifndef WIN32
if (useSyslog && syslog_opened) { if(useSyslog && syslog_opened) {
closelog(); closelog();
syslog_opened = 0; syslog_opened = 0;
} }
#endif #endif
} }
#define N2N_TRACE_DATESIZE 32 #define N2N_TRACE_DATESIZE 32
void traceEvent(int eventTraceLevel, char* file, int line, char * format, ...) { void traceEvent (int eventTraceLevel, char* file, int line, char * format, ...)
va_list va_ap; {
if(traceFile == NULL) va_list va_ap;
traceFile = stdout;
if(eventTraceLevel <= traceLevel) { if(traceFile == NULL) {
char buf[1024]; traceFile = stdout;
char out_buf[1280]; }
char theDate[N2N_TRACE_DATESIZE];
char *extra_msg = "";
time_t theTime = time(NULL);
int i;
/* We have two paths - one if we're logging, one if we aren't
* Note that the no-log case is those systems which don't support it(WIN32
),
* those without the headers !defined(USE_SYS
LOG)
* those where it's parametrically off...
*/
memset(buf, 0, sizeof(buf));
strftime(theDate, N2N_TRACE_DATESIZE, "%d/%b/%Y %H:%M:%S", localtime(&theTim
e));
va_start(va_ap, format);
vsnprintf(buf, sizeof(buf)-1, format, va_ap);
va_end(va_ap);
if(eventTraceLevel == 0 /* TRACE_ERROR */)
extra_msg = "ERROR: ";
else if(eventTraceLevel == 1 /* TRACE_WARNING */)
extra_msg = "WARNING: ";
while(buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = '\0'; if(eventTraceLevel <= traceLevel) {
char buf[1024];
char out_buf[1280];
char theDate[N2N_TRACE_DATESIZE];
char *extra_msg = "";
time_t theTime = time(NULL);
int i;
/* We have two paths - one if we're logging, one if we aren't
* Note that the no-log case is those systems which don't support it(WIN
32),
* those without the headers !defined(USE_SYSLOG)
* those where it's parametrically off...
*/
memset(buf, 0, sizeof(buf));
strftime(theDate, N2N_TRACE_DATESIZE, "%d/%b/%Y %H:%M:%S", localtime(&th
eTime));
va_start(va_ap, format);
vsnprintf(buf, sizeof(buf) - 1, format, va_ap);
va_end(va_ap);
if(eventTraceLevel == 0 /* TRACE_ERROR */) {
extra_msg = "ERROR: ";
} else if(eventTraceLevel == 1 /* TRACE_WARNING */) {
extra_msg = "WARNING: ";
}
while(buf[strlen(buf) - 1] == '\n') {
buf[strlen(buf) - 1] = '\0';
}
#ifndef WIN32 #ifndef WIN32
if(useSyslog) { if(useSyslog) {
if(!syslog_opened) { if(!syslog_opened) {
openlog("n2n", LOG_PID, LOG_DAEMON); openlog("n2n", LOG_PID, LOG_DAEMON);
syslog_opened = 1; syslog_opened = 1;
} }
snprintf(out_buf, sizeof(out_buf), "%s%s", extra_msg, buf); snprintf(out_buf, sizeof(out_buf), "%s%s", extra_msg, buf);
syslog(LOG_INFO, "%s", out_buf); syslog(LOG_INFO, "%s", out_buf);
} else { } else {
for(i=strlen(file)-1; i>0; i--) if(file[i] == '/') { i++; break; for(i = strlen(file) - 1; i > 0; i--) {
}; if(file[i] == '/') {
snprintf(out_buf, sizeof(out_buf), "%s [%s:%d] %s%s", theDate, &f i++;
ile[i], line, extra_msg, buf); break;
fprintf(traceFile, "%s\n", out_buf); }
fflush(traceFile); }
} snprintf(out_buf, sizeof(out_buf), "%s [%s:%d] %s%s", theDate, &file
[i], line, extra_msg, buf);
fprintf(traceFile, "%s\n", out_buf);
fflush(traceFile);
}
#else #else
/* this is the WIN32 code */ /* this is the WIN32 code */
for(i=strlen(file)-1; i>0; i--) if(file[i] == '\\') { i++; break; }; for(i = strlen(file) - 1; i > 0; i--) {
snprintf(out_buf, sizeof(out_buf), "%s [%s:%d] %s%s", theDate, &file[i], lin if(file[i] == '\\') {
e, extra_msg, buf); i++;
fprintf(traceFile, "%s\n", out_buf); break;
fflush(traceFile); }
}
snprintf(out_buf, sizeof(out_buf), "%s [%s:%d] %s%s", theDate, &file[i],
line, extra_msg, buf);
fprintf(traceFile, "%s\n", out_buf);
fflush(traceFile);
#endif #endif
} }
} }
/* *********************************************** */ /* *********************************************** */
/* addr should be in network order. Things are so much simpler that way. */ /* addr should be in network order. Things are so much simpler that way. */
char* intoa(uint32_t /* host order */ addr, char* buf, uint16_t buf_len) { char* intoa (uint32_t /* host order */ addr, char* buf, uint16_t buf_len) {
char *cp, *retStr;
uint8_t byteval; char *cp, *retStr;
int n; uint8_t byteval;
int n;
cp = &buf[buf_len];
*--cp = '\0'; cp = &buf[buf_len];
*--cp = '\0';
n = 4;
do { n = 4;
byteval = addr & 0xff; do {
*--cp = byteval % 10 + '0'; byteval = addr & 0xff;
byteval /= 10; *--cp = byteval % 10 + '0';
if(byteval > 0) { byteval /= 10;
*--cp = byteval % 10 + '0'; if(byteval > 0) {
byteval /= 10; *--cp = byteval % 10 + '0';
if(byteval > 0) byteval /= 10;
*--cp = byteval + '0'; if(byteval > 0) {
} *--cp = byteval + '0';
*--cp = '.'; }
addr >>= 8; }
} while(--n > 0); *--cp = '.';
addr >>= 8;
} while(--n > 0);
/* Convert the string to lowercase */
retStr = (char*)(cp + 1);
return(retStr);
}
/** Convert subnet prefix bit length to host order subnet mask. */
uint32_t bitlen2mask (uint8_t bitlen) {
/* Convert the string to lowercase */ uint8_t i;
retStr =(char*)(cp+1); uint32_t mask = 0;
return(retStr); for (i = 1; i <= bitlen; ++i) {
mask |= 1 << (32 - i);
}
return mask;
}
/** Convert host order subnet mask to subnet prefix bit length. */
uint8_t mask2bitlen (uint32_t mask) {
uint8_t i, bitlen = 0;
for (i = 0; i < 32; ++i) {
if((mask << i) & 0x80000000) {
++bitlen;
} else {
break;
}
}
return bitlen;
} }
/* *********************************************** */ /* *********************************************** */
char * macaddr_str(macstr_t buf, char * macaddr_str (macstr_t buf,
const n2n_mac_t mac) const n2n_mac_t mac) {
{
snprintf(buf, N2N_MACSTR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X", snprintf(buf, N2N_MACSTR_SIZE, "%02X:%02X:%02X:%02X:%02X:%02X",
mac[0] & 0xFF, mac[1] & 0xFF, mac[2] & 0xFF, mac[0] & 0xFF, mac[1] & 0xFF, mac[2] & 0xFF,
mac[3] & 0xFF, mac[4] & 0xFF, mac[5] & 0xFF); mac[3] & 0xFF, mac[4] & 0xFF, mac[5] & 0xFF);
return(buf);
return(buf);
} }
/* *********************************************** */ /* *********************************************** */
uint8_t is_multi_broadcast(const uint8_t * dest_mac) { /** Resolve the supernode IP address.
*
*/
int supernode2sock (n2n_sock_t *sn, const n2n_sn_name_t addrIn) {
n2n_sn_name_t addr;
char *supernode_host;
char *supernode_port;
int rv = 0;
int nameerr;
const struct addrinfo aihints = {0, PF_INET, 0, 0, 0, NULL, NULL, NULL};
struct addrinfo * ainfo = NULL;
struct sockaddr_in * saddr;
sn->family = AF_INVALID;
memcpy(addr, addrIn, N2N_EDGE_SN_HOST_SIZE);
supernode_host = strtok(addr, ":");
if(supernode_host) {
supernode_port = strtok(NULL, ":");
if(supernode_port) {
sn->port = atoi(supernode_port);
nameerr = getaddrinfo(supernode_host, NULL, &aihints, &ainfo);
if(0 == nameerr) {
/* ainfo s the head of a linked list if non-NULL. */
if(ainfo && (PF_INET == ainfo->ai_family)) {
/* It is definitely and IPv4 address -> sockaddr_in */
saddr = (struct sockaddr_in *)ainfo->ai_addr;
memcpy(sn->addr.v4, &(saddr->sin_addr.s_addr), IPV4_SIZE);
sn->family = AF_INET;
traceEvent(TRACE_INFO, "supernode2sock successfully resolves
supernode IPv4 address for %s", supernode_host);
rv = 0;
} else {
/* Should only return IPv4 addresses due to aihints. */
traceEvent(TRACE_WARNING, "supernode2sock fails to resolve s
upernode IPv4 address for %s", supernode_host);
rv = -1;
}
freeaddrinfo(ainfo); /* free everything allocated by getaddrinfo
(). */
} else {
traceEvent(TRACE_WARNING, "supernode2sock fails to resolve super
node host %s, %d: %s", supernode_host, nameerr, gai_strerror(nameerr));
rv = -2;
}
} else {
traceEvent(TRACE_WARNING, "supernode2sock sees malformed supernode p
arameter (-l <host:port>) %s", addrIn);
rv = -3;
}
} else {
traceEvent(TRACE_WARNING, "supernode2sock sees malformed supernode param
eter (-l <host:port>) %s",
addrIn);
rv = -4;
}
ainfo = NULL;
return rv;
}
N2N_THREAD_RETURN_DATATYPE resolve_thread(N2N_THREAD_PARAMETER_DATATYPE p) {
#ifdef HAVE_PTHREAD
n2n_resolve_parameter_t *param = (n2n_resolve_parameter_t*)p;
n2n_resolve_ip_sock_t *entry, *tmp_entry;
time_t rep_time = N2N_RESOLVE_INTERVAL / 10;
time_t now;
while(1) {
sleep(N2N_RESOLVE_INTERVAL / 60); /* wake up in-between to check for sig
naled requests */
// what's the time?
now = time(NULL);
// lock access
pthread_mutex_lock(&param->access);
// is it time to resolve yet?
if(((param->request)) || ((now - param->last_resolved) > rep_time)) {
HASH_ITER(hh, param->list, entry, tmp_entry) {
// resolve
entry->error_code = supernode2sock(&entry->sock, entry->org_ip);
// if socket changed and no error
if(!sock_equal(&entry->sock, entry->org_sock)
&& (!entry->error_code)) {
// flag the change
param->changed = 1;
}
}
param->last_resolved = now;
// any request fulfilled
param->request = 0;
// determine next resolver repetition (shorter time if resolver erro
rs occured)
rep_time = N2N_RESOLVE_INTERVAL;
HASH_ITER(hh, param->list, entry, tmp_entry) {
if(entry->error_code) {
rep_time = N2N_RESOLVE_INTERVAL / 10;
break;
}
}
}
int is_broadcast =(memcmp(broadcast_addr, dest_mac, 6) == 0); // unlock access
int is_multicast =(memcmp(multicast_addr, dest_mac, 3) == 0); pthread_mutex_unlock(&param->access);
int is_ipv6_multicast =(memcmp(ipv6_multicast_addr, dest_mac, 2) == 0); }
#endif
}
int resolve_create_thread (n2n_resolve_parameter_t **param, struct peer_info *sn
_list) {
#ifdef HAVE_PTHREAD
struct peer_info *sn, *tmp_sn;
n2n_resolve_ip_sock_t *entry;
int ret;
// create parameter structure
*param = (n2n_resolve_parameter_t*)calloc(1, sizeof(n2n_resolve_parameter_t)
);
if(*param) {
HASH_ITER(hh, sn_list, sn, tmp_sn) {
// create entries for those peers that come with ip_addr string (fro
m command-line)
if(sn->ip_addr) {
entry = (n2n_resolve_ip_sock_t*)calloc(1, sizeof(n2n_resolve_ip_
sock_t));
if(entry) {
entry->org_ip = sn->ip_addr;
entry->org_sock = &(sn->sock);
memcpy(&(entry->sock), &(sn->sock), sizeof(n2n_sock_t));
HASH_ADD(hh, (*param)->list, org_ip, sizeof(char*), entry);
} else
traceEvent(TRACE_WARNING, "resolve_create_thread was unable
to add list entry for supernode '%s'", sn->ip_addr);
}
}
(*param)->check_interval = N2N_RESOLVE_CHECK_INTERVAL;
} else {
traceEvent(TRACE_WARNING, "resolve_create_thread was unable to create li
st of supernodes");
return -1;
}
// create thread
ret = pthread_create(&((*param)->id), NULL, resolve_thread, (void *)*param);
if(ret) {
traceEvent(TRACE_WARNING, "resolve_create_thread failed to create resolv
er thread with error number %d", ret);
return -1;
}
pthread_mutex_init(&((*param)->access), NULL);
return 0;
#endif
}
void resolve_cancel_thread (n2n_resolve_parameter_t *param) {
#ifdef HAVE_PTHREAD
pthread_cancel(param->id);
free(param);
#endif
}
uint8_t resolve_check (n2n_resolve_parameter_t *param, uint8_t requires_resoluti
on, time_t now) {
uint8_t ret = requires_resolution; /* if trylock fails, it still requires re
solution */
#ifdef HAVE_PTHREAD
n2n_resolve_ip_sock_t *entry, *tmp_entry;
n2n_sock_str_t sock_buf;
if(NULL == param)
return ret;
// check_interval and last_check do not need to be guarded by the mutex beca
use
// their values get changed and evaluated only here
if((now - param->last_checked > param->check_interval) || (requires_resoluti
on)) {
// try to lock access
if(pthread_mutex_trylock(&param->access) == 0) {
// any changes?
if(param->changed) {
// reset flag
param->changed = 0;
// unselectively copy all socks (even those with error code, tha
t would be the old one because
// sockets do not get overwritten in case of error in resolve_th
read) from list to supernode list
HASH_ITER(hh, param->list, entry, tmp_entry) {
memcpy(entry->org_sock, &entry->sock, sizeof(n2n_sock_t));
traceEvent(TRACE_INFO, "resolve_check renews ip address of s
upernode '%s' to %s",
entry->org_ip, sock_to_cstr(sock_buf,
&(entry->sock)));
}
}
// let the resolver thread know eventual difficulties in reaching th
e supernode
if(requires_resolution) {
param->request = 1;
ret = 0;
}
param->last_checked = now;
// next appointment
if(param->request)
// earlier if resolver still working on fulfilling a request
param->check_interval = N2N_RESOLVE_CHECK_INTERVAL / 10;
else
param->check_interval = N2N_RESOLVE_CHECK_INTERVAL;
// unlock access
pthread_mutex_unlock(&param->access);
}
}
#endif
return ret;
}
/* ************************************** */
struct peer_info* add_sn_to_list_by_mac_or_sock (struct peer_info **sn_list, n2n
_sock_t *sock, const n2n_mac_t mac, int *skip_add) {
struct peer_info *scan, *tmp, *peer = NULL;
if(!is_null_mac(mac)) { /* not zero MAC */
HASH_FIND_PEER(*sn_list, mac, peer);
}
return is_broadcast || is_multicast || is_ipv6_multicast; if(peer == NULL) { /* zero MAC, search by socket */
HASH_ITER(hh, *sn_list, scan, tmp) {
if(memcmp(&(scan->sock), sock, sizeof(n2n_sock_t)) == 0) {
// update mac if appropriate, needs to be deleted first because
it is key to the hash list
if(!is_null_mac(mac)) {
HASH_DEL(*sn_list, scan);
memcpy(scan->mac_addr, mac, sizeof(n2n_mac_t));
HASH_ADD_PEER(*sn_list, scan);
}
peer = scan;
break;
}
}
if((peer == NULL) && (*skip_add == SN_ADD)) {
peer = (struct peer_info*)calloc(1, sizeof(struct peer_info));
if(peer) {
sn_selection_criterion_default(&(peer->selection_criterion));
peer->last_valid_time_stamp = initial_time_stamp();
memcpy(&(peer->sock), sock, sizeof(n2n_sock_t));
memcpy(peer->mac_addr, mac, sizeof(n2n_mac_t));
HASH_ADD_PEER(*sn_list, peer);
*skip_add = SN_ADD_ADDED;
}
}
}
return peer;
} }
/* ************************************************ */
/* http://www.faqs.org/rfcs/rfc908.html */ /* http://www.faqs.org/rfcs/rfc908.html */
uint8_t is_multi_broadcast (const n2n_mac_t dest_mac) {
int is_broadcast = (memcmp(broadcast_mac, dest_mac, N2N_MAC_SIZE) == 0);
int is_multicast = (memcmp(multicast_mac, dest_mac, 3) == 0) && !(dest_mac[3
] >> 7);
int is_ipv6_multicast = (memcmp(ipv6_multicast_mac, dest_mac, 2) == 0);
return is_broadcast || is_multicast || is_ipv6_multicast;
}
uint8_t is_broadcast (const n2n_mac_t dest_mac) {
int is_broadcast = (memcmp(broadcast_mac, dest_mac, N2N_MAC_SIZE) == 0);
return is_broadcast;
}
uint8_t is_null_mac (const n2n_mac_t dest_mac) {
int is_null_mac = (memcmp(null_mac, dest_mac, N2N_MAC_SIZE) == 0);
return is_null_mac;
}
/* *********************************************** */ /* *********************************************** */
char* msg_type2str(uint16_t msg_type) { char* msg_type2str (uint16_t msg_type) {
switch(msg_type) {
case MSG_TYPE_REGISTER: return("MSG_TYPE_REGISTER"); switch(msg_type) {
case MSG_TYPE_DEREGISTER: return("MSG_TYPE_DEREGISTER"); case MSG_TYPE_REGISTER: return("MSG_TYPE_REGISTER");
case MSG_TYPE_PACKET: return("MSG_TYPE_PACKET"); case MSG_TYPE_DEREGISTER: return("MSG_TYPE_DEREGISTER");
case MSG_TYPE_REGISTER_ACK: return("MSG_TYPE_REGISTER_ACK"); case MSG_TYPE_PACKET: return("MSG_TYPE_PACKET");
case MSG_TYPE_REGISTER_SUPER: return("MSG_TYPE_REGISTER_SUPER"); case MSG_TYPE_REGISTER_ACK: return("MSG_TYPE_REGISTER_ACK");
case MSG_TYPE_REGISTER_SUPER_ACK: return("MSG_TYPE_REGISTER_SUPER_ACK"); case MSG_TYPE_REGISTER_SUPER: return("MSG_TYPE_REGISTER_SUPER");
case MSG_TYPE_REGISTER_SUPER_NAK: return("MSG_TYPE_REGISTER_SUPER_NAK"); case MSG_TYPE_REGISTER_SUPER_ACK: return("MSG_TYPE_REGISTER_SUPER_ACK");
case MSG_TYPE_FEDERATION: return("MSG_TYPE_FEDERATION"); case MSG_TYPE_REGISTER_SUPER_NAK: return("MSG_TYPE_REGISTER_SUPER_NAK");
default: return("???"); case MSG_TYPE_FEDERATION: return("MSG_TYPE_FEDERATION");
} default: return("???");
}
return("???"); return("???");
} }
/* *********************************************** */ /* *********************************************** */
void hexdump(const uint8_t * buf, size_t len) void hexdump (const uint8_t *buf, size_t len) {
{
size_t i;
if(0 == len) { return; } size_t i;
for(i=0; i<len; i++) if(0 == len) {
{ return;
if((i > 0) &&((i % 16) == 0)) { printf("\n"); }
printf("%02X ", buf[i] & 0xFF);
} }
printf("\n"); printf("-----------------------------------------------\n");
for(i = 0; i < len; i++) {
if((i > 0) && ((i % 16) == 0)) {
printf("\n");
}
printf("%02X ", buf[i] & 0xFF);
}
printf("\n");
printf("-----------------------------------------------\n");
} }
/* *********************************************** */ /* *********************************************** */
void print_n2n_version() { void print_n2n_version () {
printf("Welcome to n2n v.%s for %s\n"
"Built on %s\n" printf("Welcome to n2n v.%s for %s\n"
"Copyright 2007-2020 - ntop.org and contributors\n\n", "Built on %s\n"
GIT_RELEASE, PACKAGE_OSNAME, PACKAGE_BUILDDATE); "Copyright 2007-2021 - ntop.org and contributors\n\n",
GIT_RELEASE, PACKAGE_OSNAME, PACKAGE_BUILDDATE);
} }
/* *********************************************** */ /* *********************************************** */
size_t purge_expired_registrations(struct peer_info ** peer_list, time_t* p_last size_t purge_expired_nodes (struct peer_info **peer_list,
_purge) { SOCKET socket_not_to_close,
time_t now = time(NULL); n2n_tcp_connection_t **tcp_connections,
size_t num_reg = 0; time_t *p_last_purge,
int frequency, int timeout) {
if((now - (*p_last_purge)) < PURGE_REGISTRATION_FREQUENCY) return 0; time_t now = time(NULL);
size_t num_reg = 0;
traceEvent(TRACE_DEBUG, "Purging old registrations"); if((now - (*p_last_purge)) < frequency) {
return 0;
}
num_reg = purge_peer_list(peer_list, now-REGISTRATION_TIMEOUT); traceEvent(TRACE_DEBUG, "Purging old registrations");
(*p_last_purge) = now; num_reg = purge_peer_list(peer_list, socket_not_to_close, tcp_connections, n
traceEvent(TRACE_DEBUG, "Remove %ld registrations", num_reg); ow - timeout);
return num_reg; (*p_last_purge) = now;
} traceEvent(TRACE_DEBUG, "Remove %ld registrations", num_reg);
/** Purge old items from the peer_list and return the number of items that were return num_reg;
removed. */ }
size_t purge_peer_list(struct peer_info ** peer_list,
time_t purge_before)
{
struct peer_info *scan, *tmp;
size_t retval=0;
HASH_ITER(hh, *peer_list, scan, tmp) { /** Purge old items from the peer_list, eventually close the related socket, and
if(scan->last_seen < purge_before) { * return the number of items that were removed. */
HASH_DEL(*peer_list, scan); size_t purge_peer_list (struct peer_info **peer_list,
retval++; SOCKET socket_not_to_close,
free(scan); n2n_tcp_connection_t **tcp_connections,
time_t purge_before) {
struct peer_info *scan, *tmp;
n2n_tcp_connection_t *conn;
size_t retval = 0;
HASH_ITER(hh, *peer_list, scan, tmp) {
if((scan->purgeable == SN_PURGEABLE) && (scan->last_seen < purge_before)
) {
if((scan->socket_fd >=0) && (scan->socket_fd != socket_not_to_close)
) {
if(tcp_connections) {
HASH_FIND_INT(*tcp_connections, &scan->socket_fd, conn);
if(conn) {
HASH_DEL(*tcp_connections, conn);
free(conn);
}
shutdown(scan->socket_fd, SHUT_RDWR);
closesocket(scan->socket_fd);
}
}
HASH_DEL(*peer_list, scan);
retval++;
free(scan);
}
} }
}
return retval; return retval;
} }
/** Purge all items from the peer_list and return the number of items that were removed. */ /** Purge all items from the peer_list and return the number of items that were removed. */
size_t clear_peer_list(struct peer_info ** peer_list) size_t clear_peer_list (struct peer_info ** peer_list) {
{
struct peer_info *scan, *tmp; struct peer_info *scan, *tmp;
size_t retval=0; size_t retval = 0;
HASH_ITER(hh, *peer_list, scan, tmp) { HASH_ITER(hh, *peer_list, scan, tmp) {
HASH_DEL(*peer_list, scan); HASH_DEL(*peer_list, scan);
retval++; retval++;
free(scan); free(scan);
} }
return retval; return retval;
} }
static uint8_t hex2byte(const char * s) static uint8_t hex2byte (const char * s) {
{
char tmp[3]; char tmp[3];
tmp[0]=s[0]; tmp[0] = s[0];
tmp[1]=s[1]; tmp[1] = s[1];
tmp[2]=0; /* NULL term */ tmp[2] = 0; /* NULL term */
return((uint8_t)strtol(tmp, NULL, 16)); return((uint8_t)strtol(tmp, NULL, 16));
} }
extern int str2mac(uint8_t * outmac /* 6 bytes */, const char * s) extern int str2mac (uint8_t * outmac /* 6 bytes */, const char * s) {
{
size_t i; size_t i;
/* break it down as one case for the first "HH", the 5 x through loop for /* break it down as one case for the first "HH", the 5 x through loop for
* each ":HH" where HH is a two hex nibbles in ASCII. */ * each ":HH" where HH is a two hex nibbles in ASCII. */
*outmac=hex2byte(s); *outmac = hex2byte(s);
++outmac; ++outmac;
s+=2; /* don't skip colon yet - helps generalise loop. */ s += 2; /* don't skip colon yet - helps generalise loop. */
for(i=1; i<6; ++i) for(i = 1; i < 6; ++i) {
{ s += 1;
s+=1; *outmac = hex2byte(s);
*outmac=hex2byte(s); ++outmac;
++outmac; s += 2;
s+=2; }
}
return 0; /* ok */
return 0; /* ok */ }
}
extern char * sock_to_cstr (n2n_sock_str_t out,
extern char * sock_to_cstr(n2n_sock_str_t out, const n2n_sock_t * sock) {
const n2n_sock_t * sock) {
if(NULL == out) { return NULL; } if(NULL == out) {
memset(out, 0, N2N_SOCKBUF_SIZE); return NULL;
}
if(AF_INET6 == sock->family) { memset(out, 0, N2N_SOCKBUF_SIZE);
/* INET6 not written yet */
snprintf(out, N2N_SOCKBUF_SIZE, "XXXX:%hu", sock->port); if(AF_INET6 == sock->family) {
return out; /* INET6 not written yet */
} else { snprintf(out, N2N_SOCKBUF_SIZE, "XXXX:%hu", sock->port);
const uint8_t * a = sock->addr.v4; return out;
} else {
snprintf(out, N2N_SOCKBUF_SIZE, "%hu.%hu.%hu.%hu:%hu", const uint8_t * a = sock->addr.v4;
(unsigned short)(a[0] & 0xff),
(unsigned short)(a[1] & 0xff), snprintf(out, N2N_SOCKBUF_SIZE, "%hu.%hu.%hu.%hu:%hu",
(unsigned short)(a[2] & 0xff), (unsigned short)(a[0] & 0xff),
(unsigned short)(a[3] & 0xff), (unsigned short)(a[1] & 0xff),
(unsigned short)sock->port); (unsigned short)(a[2] & 0xff),
return out; (unsigned short)(a[3] & 0xff),
} (unsigned short)sock->port);
return out;
}
}
char *ip_subnet_to_str (dec_ip_bit_str_t buf, const n2n_ip_subnet_t *ipaddr) {
snprintf(buf, sizeof(dec_ip_bit_str_t), "%hhu.%hhu.%hhu.%hhu/%hhu",
(uint8_t) ((ipaddr->net_addr >> 24) & 0xFF),
(uint8_t) ((ipaddr->net_addr >> 16) & 0xFF),
(uint8_t) ((ipaddr->net_addr >> 8) & 0xFF),
(uint8_t) (ipaddr->net_addr & 0xFF),
ipaddr->net_bitlen);
return buf;
} }
/* @return 1 if the two sockets are equivalent. */ /* @return 1 if the two sockets are equivalent. */
int sock_equal(const n2n_sock_t * a, int sock_equal (const n2n_sock_t * a,
const n2n_sock_t * b) { const n2n_sock_t * b) {
if(a->port != b->port) { return(0); }
if(a->family != b->family) { return(0); } if(a->port != b->port) {
return(0);
switch(a->family) { }
case AF_INET:
if(memcmp(a->addr.v4, b->addr.v4, IPV4_SIZE))
return(0);
break;
default:
if(memcmp(a->addr.v6, b->addr.v6, IPV6_SIZE))
return(0);
break;
}
/* equal */ if(a->family != b->family) {
return(1); return(0);
}
switch(a->family) {
case AF_INET:
if(memcmp(a->addr.v4, b->addr.v4, IPV4_SIZE)) {
return(0);
}
break;
default:
if(memcmp(a->addr.v6, b->addr.v6, IPV6_SIZE)) {
return(0);
}
break;
}
/* equal */
return(1);
} }
/* *********************************************** */ /* *********************************************** */
#if defined(WIN32) && !defined(__GNUC__) // fills a specified memory area with random numbers
int gettimeofday(struct timeval *tp, void *tzp) { int memrnd (uint8_t *address, size_t len) {
time_t clock;
struct tm tm; for(; len >= 4; len -= 4) {
SYSTEMTIME wtm; *(uint32_t*)address = n2n_rand();
GetLocalTime(&wtm); address += 4;
tm.tm_year = wtm.wYear - 1900; }
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay; for(; len > 0; len--) {
tm.tm_hour = wtm.wHour; *address = n2n_rand();
tm.tm_min = wtm.wMinute; address++;
tm.tm_sec = wtm.wSecond; }
tm.tm_isdst = -1;
clock = mktime(&tm); return 0;
tp->tv_sec = clock; }
tp->tv_usec = wtm.wMilliseconds * 1000;
return (0); // exclusive-ors a specified memory area with another
int memxor (uint8_t *destination, const uint8_t *source, size_t len) {
for(; len >= 4; len -= 4) {
*(uint32_t*)destination ^= *(uint32_t*)source;
source += 4;
destination += 4;
}
for(; len > 0; len--) {
*destination ^= *source;
source++;
destination++;
}
return 0;
}
/* *********************************************** */
#if defined(WIN32)
int gettimeofday (struct timeval *tp, void *tzp) {
time_t clock;
struct tm tm;
SYSTEMTIME wtm;
GetLocalTime(&wtm);
tm.tm_year = wtm.wYear - 1900;
tm.tm_mon = wtm.wMonth - 1;
tm.tm_mday = wtm.wDay;
tm.tm_hour = wtm.wHour;
tm.tm_min = wtm.wMinute;
tm.tm_sec = wtm.wSecond;
tm.tm_isdst = -1;
clock = mktime(&tm);
tp->tv_sec = clock;
tp->tv_usec = wtm.wMilliseconds * 1000;
return 0;
} }
#endif #endif
// returns a time stamp for use with replay protection // stores the previously issued time stamp
static uint64_t previously_issued_time_stamp = 0;
// returns a time stamp for use with replay protection (branchless code)
//
// depending on the self-detected accuracy, it has the following format
//
// MMMMMMMMCCCCCCCF or
//
// MMMMMMMMSSSSSCCF
//
// with M being the 32-bit second time stamp
// S the 20-bit sub-second (microsecond) time stamp part, if applicab
le
// C a counter (8 bit or 24 bit) reset to 0 with every MMMMMMMM(SSSSS
) turn-over
// F a 4-bit flag field with
// ...c being the accuracy indicator (if set, only counter and no sub-se
cond accuracy)
//
uint64_t time_stamp (void) { uint64_t time_stamp (void) {
struct timeval tod; struct timeval tod;
uint64_t micro_seconds; uint64_t micro_seconds;
uint64_t co, mask_lo, mask_hi, hi_unchanged, counter, new_co;
gettimeofday(&tod, NULL);
// (roughly) calculate the microseconds since 1970, leftbound
micro_seconds = ((uint64_t)(tod.tv_sec) << 32) + ((uint64_t)tod.tv_usec << 1
2);
// more exact but more costly due to the multiplication:
// micro_seconds = ((uint64_t)(tod.tv_sec) * 1000000ULL + tod.tv_usec) << 12
;
// extract "counter only" flag (lowest bit)
co = (previously_issued_time_stamp << 63) >> 63;
// set mask accordingly
mask_lo = -co;
mask_lo >>= 32;
// either 0x00000000FFFFFFFF (if co flag set) or 0x0000000000000000 (if co f
lag not set)
mask_lo |= (~mask_lo) >> 52;
// either 0x00000000FFFFFFFF (unchanged) or 0x0000000000000FFF (lowest
12 bit set)
mask_hi = ~mask_lo;
hi_unchanged = ((previously_issued_time_stamp & mask_hi) == (micro_seconds &
mask_hi));
// 0 if upper bits unchanged (compared to previous stamp), 1 otherwise
// read counter and shift right for flags
counter = (previously_issued_time_stamp & mask_lo) >> 4;
gettimeofday (&tod, NULL); counter += hi_unchanged;
/* We will (roughly) calculate the microseconds since 1970 leftbound into the counter &= -hi_unchanged;
return value. // either counter++ if upper part of timestamp unchanged, 0 otherwise
The leading 32 bits are used for tv_sec. The following 20 bits (sufficent a
s microseconds
fraction never exceeds 1,000,000,) encode the value tv_usec. The remaining
lowest 12 bits
are kept random for use in IV */
micro_seconds = n2n_rand();
micro_seconds = ( (((uint64_t)(tod.tv_sec) << 32) + (tod.tv_usec << 12))
| (micro_seconds >> 52) );
// more exact but more costly due to the multiplication:
// micro_seconds = (tod.tv_sec * 1000000 + tod.tv_usec) << 12) | ...
return (micro_seconds); // back to time stamp format
counter <<= 4;
// set new co flag if counter overflows while upper bits unchanged or if it
was set before
new_co = (((counter & mask_lo) == 0) & hi_unchanged) | co;
// in case co flag changed, masks need to be recalculated
mask_lo = -new_co;
mask_lo >>= 32;
mask_lo |= (~mask_lo) >> 52;
mask_hi = ~mask_lo;
// assemble new timestamp
micro_seconds &= mask_hi;
micro_seconds |= counter;
micro_seconds |= new_co;
previously_issued_time_stamp = micro_seconds;
return micro_seconds;
} }
// returns an initial time stamp for use with replay protection // returns an initial time stamp for use with replay protection
uint64_t initial_time_stamp (void) { uint64_t initial_time_stamp (void) {
return ( time_stamp() - TIME_STAMP_FRAME ); return time_stamp() - TIME_STAMP_FRAME;
} }
// checks if a provided time stamp is consistent with current time and previousl y valid time stamps // checks if a provided time stamp is consistent with current time and previousl y valid time stamps
// and, in case of validity, updates the "last valid time stamp" // and, in case of validity, updates the "last valid time stamp"
int time_stamp_verify_and_update (uint64_t stamp, uint64_t * previous_stamp) { int time_stamp_verify_and_update (uint64_t stamp, uint64_t *previous_stamp, int allow_jitter) {
int64_t diff; // do not change to unsigned int64_t diff; /* do not change to unsigned */
uint64_t co; /* counter only mode (for sub-seconds) */
// is it around current time (+/- allowed deviation TIME_STAMP_FRAME)? co = (stamp << 63) >> 63;
diff = stamp - time_stamp();
// abs() // is it around current time (+/- allowed deviation TIME_STAMP_FRAME)?
diff = (diff < 0 ? -diff : diff); diff = stamp - time_stamp();
if(diff >= TIME_STAMP_FRAME) { // abs()
traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp ou diff = (diff < 0 ? -diff : diff);
t of allowed frame."); if(diff >= TIME_STAMP_FRAME) {
return (0); // failure traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp
} out of allowed frame.");
return 0; // failure
// if applicable: is it higher than previous time stamp (including allowed dev }
iation of TIME_STAMP_JITTER)?
if(NULL != previous_stamp) { // if applicable: is it higher than previous time stamp (including allowed d
// if no jitter allowed, reset lowest three (random) nybbles; the codnition eviation of TIME_STAMP_JITTER)?
shoudl already be evaluated by the compiler if(NULL != previous_stamp) {
if(TIME_STAMP_JITTER == 0) { diff = stamp - *previous_stamp;
stamp = (stamp >> 12) << 12; if(allow_jitter) {
*previous_stamp = (*previous_stamp >> 12) << 12; // 8 times higher jitter allowed for counter-only flagged timestamps
} ( ~ 1.25 sec with 160 ms default jitter)
diff = stamp - *previous_stamp + TIME_STAMP_JITTER; diff += TIME_STAMP_JITTER << (co << 3);
if(diff <= 0) { }
traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timestamp to
o old compared to previous."); if(diff <= 0) {
return (0); // failure traceEvent(TRACE_DEBUG, "time_stamp_verify_and_update found a timest
} amp too old compared to previous.");
// for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn the cl return 0; // failure
ock backwards", }
// set the higher of the values // for not allowing to exploit the allowed TIME_STAMP_JITTER to "turn th
*previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp); e clock backwards",
} // set the higher of the values
*previous_stamp = (stamp > *previous_stamp ? stamp : *previous_stamp);
}
return (1); // success return 1; // success
} }
 End of changes. 62 change blocks. 
347 lines changed or deleted 843 lines changed or added

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