Save bandwidth by moderating onion pinging.

This commit is contained in:
zugz 2017-04-29 19:16:01 +02:00 committed by Robin Lindén
parent 1b290c0d84
commit 5dd255729a
No known key found for this signature in database
GPG Key ID: 601A604B7E605776
2 changed files with 140 additions and 34 deletions

View File

@ -219,11 +219,22 @@ static bool path_timed_out(Onion_Client_Paths *onion_paths, uint32_t pathnum)
{
pathnum = pathnum % NUMBER_ONION_PATHS;
return ((onion_paths->last_path_success[pathnum] + ONION_PATH_TIMEOUT < onion_paths->last_path_used[pathnum]
&& onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES)
bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum];
uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT;
return ((onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES
&& is_timeout(onion_paths->last_path_used[pathnum], timeout))
|| is_timeout(onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME));
}
/* should node be considered to have timed out */
static bool onion_node_timed_out(const Onion_Node *node)
{
return (node->timestamp == 0
|| (node->unsuccessful_pings >= ONION_NODE_MAX_PINGS
&& is_timeout(node->last_pinged, ONION_NODE_TIMEOUT)));
}
/* Create a new path or use an old suitable one (if pathnum is valid)
* or a random one from onion_paths.
*
@ -255,8 +266,8 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
return -1;
}
onion_paths->last_path_success[pathnum] = unix_time() + ONION_PATH_FIRST_TIMEOUT - ONION_PATH_TIMEOUT;
onion_paths->path_creation_time[pathnum] = unix_time();
onion_paths->last_path_success[pathnum] = onion_paths->path_creation_time[pathnum];
onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2;
uint32_t path_num = rand();
@ -270,8 +281,11 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
}
}
if (onion_paths->last_path_used_times[pathnum] < ONION_PATH_MAX_NO_RESPONSE_USES) {
onion_paths->last_path_used[pathnum] = unix_time();
}
++onion_paths->last_path_used_times[pathnum];
onion_paths->last_path_used[pathnum] = unix_time();
memcpy(path, &onion_paths->paths[pathnum], sizeof(Onion_Path));
return 0;
}
@ -485,8 +499,8 @@ static int onion_client_cmp_entry(const void *a, const void *b)
Onion_Node entry2 = cmp2.entry;
const uint8_t *cmp_public_key = cmp1.base_public_key;
int t1 = is_timeout(entry1.timestamp, ONION_NODE_TIMEOUT);
int t2 = is_timeout(entry2.timestamp, ONION_NODE_TIMEOUT);
int t1 = onion_node_timed_out(&entry1);
int t2 = onion_node_timed_out(&entry2);
if (t1 && t2) {
return 0;
@ -532,7 +546,7 @@ static void sort_onion_node_list(Onion_Node *list, unsigned int length, const ui
}
static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, IP_Port ip_port,
uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_num)
uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_used)
{
if (num > onion_c->num_friends) {
return -1;
@ -555,6 +569,10 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
return -1;
}
if (is_stored == 1) {
onion_c->friends_list[num - 1].last_reported_announced = unix_time();
}
list_nodes = onion_c->friends_list[num - 1].clients_list;
reference_id = onion_c->friends_list[num - 1].real_public_key;
list_length = MAX_ONION_CLIENTS;
@ -565,7 +583,7 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
int index = -1, stored = 0;
unsigned int i;
if (is_timeout(list_nodes[0].timestamp, ONION_NODE_TIMEOUT)
if (onion_node_timed_out(&list_nodes[0])
|| id_closest(reference_id, list_nodes[0].public_key, public_key) == 2) {
index = 0;
}
@ -596,12 +614,14 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
list_nodes[index].is_stored = is_stored;
list_nodes[index].timestamp = unix_time();
list_nodes[index].unsuccessful_pings = 0;
if (!stored) {
list_nodes[index].last_pinged = 0;
list_nodes[index].added_time = unix_time();
}
list_nodes[index].path_used = set_path_timeouts(onion_c, num, path_num);
list_nodes[index].path_used = path_used;
return 0;
}
@ -666,9 +686,9 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
}
}
if (is_timeout(list_nodes[0].timestamp, ONION_NODE_TIMEOUT)
if (onion_node_timed_out(&list_nodes[0])
|| id_closest(reference_id, list_nodes[0].public_key, nodes[i].public_key) == 2
|| is_timeout(list_nodes[1].timestamp, ONION_NODE_TIMEOUT)
|| onion_node_timed_out(&list_nodes[1])
|| id_closest(reference_id, list_nodes[1].public_key, nodes[i].public_key) == 2) {
/* check if node is already in list. */
for (j = 0; j < list_length; ++j) {
@ -728,7 +748,9 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t
return 1;
}
if (client_add_to_list(onion_c, num, public_key, ip_port, plain[0], plain + 1, path_num) == -1) {
uint32_t path_used = set_path_timeouts(onion_c, num, path_num);
if (client_add_to_list(onion_c, num, public_key, ip_port, plain[0], plain + 1, path_used) == -1) {
return 1;
}
@ -905,7 +927,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
Onion_Node *list_nodes = onion_c->friends_list[friend_num].clients_list;
for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
if (is_timeout(list_nodes[i].timestamp, ONION_NODE_TIMEOUT)) {
if (onion_node_timed_out(&list_nodes[i])) {
continue;
}
@ -917,7 +939,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
}
}
if (num_good < (num_nodes / 4) + 1) {
if (num_good < (num_nodes - 1) / 4 + 1) {
return -1;
}
@ -1369,10 +1391,12 @@ static void populate_path_nodes_tcp(Onion_Client *onion_c)
#define ANNOUNCE_FRIEND (ONION_NODE_PING_INTERVAL * 6)
#define ANNOUNCE_FRIEND_BEGINNING 3
#define FRIEND_ONION_NODE_TIMEOUT (ONION_NODE_TIMEOUT * 6)
#define RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING 17
#define ONION_FRIEND_BACKOFF_FACTOR 4
#define ONION_FRIEND_MAX_PING_INTERVAL (5*60*MAX_ONION_CLIENTS)
static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
{
if (friendnum >= onion_c->num_friends) {
@ -1387,14 +1411,41 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
if (onion_c->friends_list[friendnum].run_count < RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING) {
interval = ANNOUNCE_FRIEND_BEGINNING;
} else {
if (onion_c->friends_list[friendnum].last_reported_announced == 0) {
onion_c->friends_list[friendnum].last_reported_announced = unix_time();
}
uint64_t backoff_interval = (unix_time() - onion_c->friends_list[friendnum].last_reported_announced)
/ ONION_FRIEND_BACKOFF_FACTOR;
if (backoff_interval > ONION_FRIEND_MAX_PING_INTERVAL) {
backoff_interval = ONION_FRIEND_MAX_PING_INTERVAL;
}
if (interval < backoff_interval) {
interval = backoff_interval;
}
}
unsigned int i, count = 0;
Onion_Node *list_nodes = onion_c->friends_list[friendnum].clients_list;
if (!onion_c->friends_list[friendnum].is_online) {
// ensure we get a response from some node roughly once per
// (interval / MAX_ONION_CLIENTS)
bool ping_random = true;
for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
if (is_timeout(list_nodes[i].timestamp, FRIEND_ONION_NODE_TIMEOUT)) {
if (!(is_timeout(list_nodes[i].timestamp, interval / MAX_ONION_CLIENTS)
&& is_timeout(list_nodes[i].last_pinged, ONION_NODE_PING_INTERVAL))) {
ping_random = false;
break;
}
}
for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
if (onion_node_timed_out(&list_nodes[i])) {
continue;
}
@ -1406,9 +1457,16 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
continue;
}
if (is_timeout(list_nodes[i].last_pinged, interval)) {
if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
continue;
}
if (is_timeout(list_nodes[i].last_pinged, interval)
|| (ping_random && rand() % (MAX_ONION_CLIENTS - i) == 0)) {
if (client_send_announce_request(onion_c, friendnum + 1, list_nodes[i].ip_port, list_nodes[i].public_key, 0, ~0) == 0) {
list_nodes[i].last_pinged = unix_time();
++list_nodes[i].unsuccessful_pings;
ping_random = false;
}
}
}
@ -1422,16 +1480,18 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
n = (MAX_ONION_CLIENTS / 2);
}
if (num_nodes != 0) {
unsigned int j;
if (count <= (uint32_t)rand() % MAX_ONION_CLIENTS) {
if (num_nodes != 0) {
unsigned int j;
for (j = 0; j < n; ++j) {
unsigned int num = rand() % num_nodes;
client_send_announce_request(onion_c, friendnum + 1, onion_c->path_nodes[num].ip_port,
onion_c->path_nodes[num].public_key, 0, ~0);
for (j = 0; j < n; ++j) {
unsigned int num = rand() % num_nodes;
client_send_announce_request(onion_c, friendnum + 1, onion_c->path_nodes[num].ip_port,
onion_c->path_nodes[num].public_key, 0, ~0);
}
++onion_c->friends_list[friendnum].run_count;
}
++onion_c->friends_list[friendnum].run_count;
}
} else {
++onion_c->friends_list[friendnum].run_count;
@ -1463,13 +1523,16 @@ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_ha
#define ANNOUNCE_INTERVAL_NOT_ANNOUNCED 3
#define ANNOUNCE_INTERVAL_ANNOUNCED ONION_NODE_PING_INTERVAL
#define TIME_TO_STABLE (ONION_NODE_PING_INTERVAL * 6)
#define ANNOUNCE_INTERVAL_STABLE (ONION_NODE_PING_INTERVAL * 8)
static void do_announce(Onion_Client *onion_c)
{
unsigned int i, count = 0;
Onion_Node *list_nodes = onion_c->clients_announce_list;
for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
if (is_timeout(list_nodes[i].timestamp, ONION_NODE_TIMEOUT)) {
if (onion_node_timed_out(&list_nodes[i])) {
continue;
}
@ -1481,16 +1544,48 @@ static void do_announce(Onion_Client *onion_c)
continue;
}
if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
continue;
}
unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED;
if (list_nodes[i].is_stored && path_exists(&onion_c->onion_paths_self, list_nodes[i].path_used)) {
interval = ANNOUNCE_INTERVAL_ANNOUNCED;
uint32_t pathnum = list_nodes[i].path_used % NUMBER_ONION_PATHS;
/* A node/path is considered 'stable', and can be pinged less
* aggressively, if it has survived for at least TIME_TO_STABLE
* and the latest packets sent to it are not timing out.
*/
if (is_timeout(list_nodes[i].added_time, TIME_TO_STABLE)
&& !(list_nodes[i].unsuccessful_pings > 0
&& is_timeout(list_nodes[i].last_pinged, ONION_NODE_TIMEOUT))
&& is_timeout(onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE)
&& !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0
&& is_timeout(onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) {
interval = ANNOUNCE_INTERVAL_STABLE;
}
}
if (is_timeout(list_nodes[i].last_pinged, interval)) {
if (is_timeout(list_nodes[i].last_pinged, interval)
|| (is_timeout(onion_c->last_announce, ONION_NODE_PING_INTERVAL)
&& rand() % (MAX_ONION_CLIENTS_ANNOUNCE - i) == 0)) {
uint32_t path_to_use = list_nodes[i].path_used;
if (list_nodes[i].unsuccessful_pings == ONION_NODE_MAX_PINGS - 1
&& is_timeout(list_nodes[i].added_time, TIME_TO_STABLE)) {
/* Last chance for a long-lived node - try a random path */
path_to_use = ~0;
}
if (client_send_announce_request(onion_c, 0, list_nodes[i].ip_port, list_nodes[i].public_key,
list_nodes[i].ping_id, list_nodes[i].path_used) == 0) {
list_nodes[i].ping_id, path_to_use) == 0) {
list_nodes[i].last_pinged = unix_time();
++list_nodes[i].unsuccessful_pings;
onion_c->last_announce = unix_time();
}
}
}
@ -1507,7 +1602,7 @@ static void do_announce(Onion_Client *onion_c)
path_nodes = onion_c->path_nodes;
}
if (count < (uint32_t)rand() % MAX_ONION_CLIENTS_ANNOUNCE) {
if (count <= (uint32_t)rand() % MAX_ONION_CLIENTS_ANNOUNCE) {
if (num_nodes != 0) {
for (i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) {
unsigned int num = rand() % num_nodes;
@ -1534,7 +1629,7 @@ static int onion_isconnected(const Onion_Client *onion_c)
}
for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
if (!is_timeout(onion_c->clients_announce_list[i].timestamp, ONION_NODE_TIMEOUT)) {
if (!onion_node_timed_out(&onion_c->clients_announce_list[i])) {
++num;
if (onion_c->clients_announce_list[i].is_stored) {

View File

@ -32,7 +32,7 @@
#define MAX_ONION_CLIENTS 8
#define MAX_ONION_CLIENTS_ANNOUNCE 12 /* Number of nodes to announce ourselves to. */
#define ONION_NODE_PING_INTERVAL 15
#define ONION_NODE_TIMEOUT (ONION_NODE_PING_INTERVAL * 3)
#define ONION_NODE_TIMEOUT ONION_NODE_PING_INTERVAL
/* The interval in seconds at which to tell our friends where we are */
#define ONION_DHTPK_SEND_INTERVAL 30
@ -50,12 +50,16 @@
#define MAX_STORED_PINGED_NODES 9
#define MIN_NODE_PING_TIME 10
#define ONION_NODE_MAX_PINGS 3
#define MAX_PATH_NODES 32
/* If no packets are received within that interval tox will
* be considered offline.
/* If no announce response packets are received within this interval tox will
* be considered offline. We give time for a node to be pinged often enough
* that it times out, which leads to the network being thoroughly tested as it
* is replaced.
*/
#define ONION_OFFLINE_TIMEOUT (ONION_NODE_PING_INTERVAL * 1.25)
#define ONION_OFFLINE_TIMEOUT (ONION_NODE_PING_INTERVAL * (ONION_NODE_MAX_PINGS+2))
/* Onion data packet ids. */
#define ONION_DATA_FRIEND_REQ CRYPTO_PACKET_FRIEND_REQ
@ -68,10 +72,14 @@ typedef struct {
uint8_t data_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t is_stored;
uint64_t added_time;
uint64_t timestamp;
uint64_t last_pinged;
uint8_t unsuccessful_pings;
uint32_t path_used;
} Onion_Node;
@ -101,6 +109,8 @@ typedef struct {
uint8_t temp_public_key[CRYPTO_PUBLIC_KEY_SIZE];
uint8_t temp_secret_key[CRYPTO_SECRET_KEY_SIZE];
uint64_t last_reported_announced;
uint64_t last_dht_pk_onion_sent;
uint64_t last_dht_pk_dht_sent;
@ -133,6 +143,7 @@ typedef struct {
uint16_t num_friends;
Onion_Node clients_announce_list[MAX_ONION_CLIENTS_ANNOUNCE];
uint64_t last_announce;
Onion_Client_Paths onion_paths_self;
Onion_Client_Paths onion_paths_friends;