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(¶m->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(¶m->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(¶m->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(¶m->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 |