summaryrefslogtreecommitdiff
path: root/toxcore/onion_client.c
diff options
context:
space:
mode:
authorzugz <mbays+tox@sdf.org>2017-04-29 19:16:01 +0200
committerRobin Lindén <dev@robinlinden.eu>2017-08-05 15:06:44 +0200
commit5dd255729a1e09fa44334da89f2e46f272aea583 (patch)
tree206a7b2fe3b419877824ab9da36115d77d7dc90c /toxcore/onion_client.c
parent1b290c0d84d92fd28fc1f64f33bf4455d73e2e2e (diff)
Save bandwidth by moderating onion pinging.
Diffstat (limited to 'toxcore/onion_client.c')
-rw-r--r--toxcore/onion_client.c155
1 files changed, 125 insertions, 30 deletions
diff --git a/toxcore/onion_client.c b/toxcore/onion_client.c
index 3c14a101..91b5b067 100644
--- a/toxcore/onion_client.c
+++ b/toxcore/onion_client.c
@@ -219,11 +219,22 @@ static bool path_timed_out(Onion_Client_Paths *onion_paths, uint32_t pathnum)
219{ 219{
220 pathnum = pathnum % NUMBER_ONION_PATHS; 220 pathnum = pathnum % NUMBER_ONION_PATHS;
221 221
222 return ((onion_paths->last_path_success[pathnum] + ONION_PATH_TIMEOUT < onion_paths->last_path_used[pathnum] 222 bool is_new = onion_paths->last_path_success[pathnum] == onion_paths->path_creation_time[pathnum];
223 && onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES) 223 uint64_t timeout = is_new ? ONION_PATH_FIRST_TIMEOUT : ONION_PATH_TIMEOUT;
224
225 return ((onion_paths->last_path_used_times[pathnum] >= ONION_PATH_MAX_NO_RESPONSE_USES
226 && is_timeout(onion_paths->last_path_used[pathnum], timeout))
224 || is_timeout(onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME)); 227 || is_timeout(onion_paths->path_creation_time[pathnum], ONION_PATH_MAX_LIFETIME));
225} 228}
226 229
230/* should node be considered to have timed out */
231static bool onion_node_timed_out(const Onion_Node *node)
232{
233 return (node->timestamp == 0
234 || (node->unsuccessful_pings >= ONION_NODE_MAX_PINGS
235 && is_timeout(node->last_pinged, ONION_NODE_TIMEOUT)));
236}
237
227/* Create a new path or use an old suitable one (if pathnum is valid) 238/* Create a new path or use an old suitable one (if pathnum is valid)
228 * or a random one from onion_paths. 239 * or a random one from onion_paths.
229 * 240 *
@@ -255,8 +266,8 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
255 return -1; 266 return -1;
256 } 267 }
257 268
258 onion_paths->last_path_success[pathnum] = unix_time() + ONION_PATH_FIRST_TIMEOUT - ONION_PATH_TIMEOUT;
259 onion_paths->path_creation_time[pathnum] = unix_time(); 269 onion_paths->path_creation_time[pathnum] = unix_time();
270 onion_paths->last_path_success[pathnum] = onion_paths->path_creation_time[pathnum];
260 onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2; 271 onion_paths->last_path_used_times[pathnum] = ONION_PATH_MAX_NO_RESPONSE_USES / 2;
261 272
262 uint32_t path_num = rand(); 273 uint32_t path_num = rand();
@@ -270,8 +281,11 @@ static int random_path(const Onion_Client *onion_c, Onion_Client_Paths *onion_pa
270 } 281 }
271 } 282 }
272 283
284 if (onion_paths->last_path_used_times[pathnum] < ONION_PATH_MAX_NO_RESPONSE_USES) {
285 onion_paths->last_path_used[pathnum] = unix_time();
286 }
287
273 ++onion_paths->last_path_used_times[pathnum]; 288 ++onion_paths->last_path_used_times[pathnum];
274 onion_paths->last_path_used[pathnum] = unix_time();
275 memcpy(path, &onion_paths->paths[pathnum], sizeof(Onion_Path)); 289 memcpy(path, &onion_paths->paths[pathnum], sizeof(Onion_Path));
276 return 0; 290 return 0;
277} 291}
@@ -485,8 +499,8 @@ static int onion_client_cmp_entry(const void *a, const void *b)
485 Onion_Node entry2 = cmp2.entry; 499 Onion_Node entry2 = cmp2.entry;
486 const uint8_t *cmp_public_key = cmp1.base_public_key; 500 const uint8_t *cmp_public_key = cmp1.base_public_key;
487 501
488 int t1 = is_timeout(entry1.timestamp, ONION_NODE_TIMEOUT); 502 int t1 = onion_node_timed_out(&entry1);
489 int t2 = is_timeout(entry2.timestamp, ONION_NODE_TIMEOUT); 503 int t2 = onion_node_timed_out(&entry2);
490 504
491 if (t1 && t2) { 505 if (t1 && t2) {
492 return 0; 506 return 0;
@@ -532,7 +546,7 @@ static void sort_onion_node_list(Onion_Node *list, unsigned int length, const ui
532} 546}
533 547
534static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, IP_Port ip_port, 548static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t *public_key, IP_Port ip_port,
535 uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_num) 549 uint8_t is_stored, const uint8_t *pingid_or_key, uint32_t path_used)
536{ 550{
537 if (num > onion_c->num_friends) { 551 if (num > onion_c->num_friends) {
538 return -1; 552 return -1;
@@ -555,6 +569,10 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
555 return -1; 569 return -1;
556 } 570 }
557 571
572 if (is_stored == 1) {
573 onion_c->friends_list[num - 1].last_reported_announced = unix_time();
574 }
575
558 list_nodes = onion_c->friends_list[num - 1].clients_list; 576 list_nodes = onion_c->friends_list[num - 1].clients_list;
559 reference_id = onion_c->friends_list[num - 1].real_public_key; 577 reference_id = onion_c->friends_list[num - 1].real_public_key;
560 list_length = MAX_ONION_CLIENTS; 578 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
565 int index = -1, stored = 0; 583 int index = -1, stored = 0;
566 unsigned int i; 584 unsigned int i;
567 585
568 if (is_timeout(list_nodes[0].timestamp, ONION_NODE_TIMEOUT) 586 if (onion_node_timed_out(&list_nodes[0])
569 || id_closest(reference_id, list_nodes[0].public_key, public_key) == 2) { 587 || id_closest(reference_id, list_nodes[0].public_key, public_key) == 2) {
570 index = 0; 588 index = 0;
571 } 589 }
@@ -596,12 +614,14 @@ static int client_add_to_list(Onion_Client *onion_c, uint32_t num, const uint8_t
596 614
597 list_nodes[index].is_stored = is_stored; 615 list_nodes[index].is_stored = is_stored;
598 list_nodes[index].timestamp = unix_time(); 616 list_nodes[index].timestamp = unix_time();
617 list_nodes[index].unsuccessful_pings = 0;
599 618
600 if (!stored) { 619 if (!stored) {
601 list_nodes[index].last_pinged = 0; 620 list_nodes[index].last_pinged = 0;
621 list_nodes[index].added_time = unix_time();
602 } 622 }
603 623
604 list_nodes[index].path_used = set_path_timeouts(onion_c, num, path_num); 624 list_nodes[index].path_used = path_used;
605 return 0; 625 return 0;
606} 626}
607 627
@@ -666,9 +686,9 @@ static int client_ping_nodes(Onion_Client *onion_c, uint32_t num, const Node_for
666 } 686 }
667 } 687 }
668 688
669 if (is_timeout(list_nodes[0].timestamp, ONION_NODE_TIMEOUT) 689 if (onion_node_timed_out(&list_nodes[0])
670 || id_closest(reference_id, list_nodes[0].public_key, nodes[i].public_key) == 2 690 || id_closest(reference_id, list_nodes[0].public_key, nodes[i].public_key) == 2
671 || is_timeout(list_nodes[1].timestamp, ONION_NODE_TIMEOUT) 691 || onion_node_timed_out(&list_nodes[1])
672 || id_closest(reference_id, list_nodes[1].public_key, nodes[i].public_key) == 2) { 692 || id_closest(reference_id, list_nodes[1].public_key, nodes[i].public_key) == 2) {
673 /* check if node is already in list. */ 693 /* check if node is already in list. */
674 for (j = 0; j < list_length; ++j) { 694 for (j = 0; j < list_length; ++j) {
@@ -728,7 +748,9 @@ static int handle_announce_response(void *object, IP_Port source, const uint8_t
728 return 1; 748 return 1;
729 } 749 }
730 750
731 if (client_add_to_list(onion_c, num, public_key, ip_port, plain[0], plain + 1, path_num) == -1) { 751 uint32_t path_used = set_path_timeouts(onion_c, num, path_num);
752
753 if (client_add_to_list(onion_c, num, public_key, ip_port, plain[0], plain + 1, path_used) == -1) {
732 return 1; 754 return 1;
733 } 755 }
734 756
@@ -905,7 +927,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
905 Onion_Node *list_nodes = onion_c->friends_list[friend_num].clients_list; 927 Onion_Node *list_nodes = onion_c->friends_list[friend_num].clients_list;
906 928
907 for (i = 0; i < MAX_ONION_CLIENTS; ++i) { 929 for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
908 if (is_timeout(list_nodes[i].timestamp, ONION_NODE_TIMEOUT)) { 930 if (onion_node_timed_out(&list_nodes[i])) {
909 continue; 931 continue;
910 } 932 }
911 933
@@ -917,7 +939,7 @@ int send_onion_data(Onion_Client *onion_c, int friend_num, const uint8_t *data,
917 } 939 }
918 } 940 }
919 941
920 if (num_good < (num_nodes / 4) + 1) { 942 if (num_good < (num_nodes - 1) / 4 + 1) {
921 return -1; 943 return -1;
922 } 944 }
923 945
@@ -1369,10 +1391,12 @@ static void populate_path_nodes_tcp(Onion_Client *onion_c)
1369 1391
1370#define ANNOUNCE_FRIEND (ONION_NODE_PING_INTERVAL * 6) 1392#define ANNOUNCE_FRIEND (ONION_NODE_PING_INTERVAL * 6)
1371#define ANNOUNCE_FRIEND_BEGINNING 3 1393#define ANNOUNCE_FRIEND_BEGINNING 3
1372#define FRIEND_ONION_NODE_TIMEOUT (ONION_NODE_TIMEOUT * 6)
1373 1394
1374#define RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING 17 1395#define RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING 17
1375 1396
1397#define ONION_FRIEND_BACKOFF_FACTOR 4
1398#define ONION_FRIEND_MAX_PING_INTERVAL (5*60*MAX_ONION_CLIENTS)
1399
1376static void do_friend(Onion_Client *onion_c, uint16_t friendnum) 1400static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
1377{ 1401{
1378 if (friendnum >= onion_c->num_friends) { 1402 if (friendnum >= onion_c->num_friends) {
@@ -1387,14 +1411,41 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
1387 1411
1388 if (onion_c->friends_list[friendnum].run_count < RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING) { 1412 if (onion_c->friends_list[friendnum].run_count < RUN_COUNT_FRIEND_ANNOUNCE_BEGINNING) {
1389 interval = ANNOUNCE_FRIEND_BEGINNING; 1413 interval = ANNOUNCE_FRIEND_BEGINNING;
1414 } else {
1415 if (onion_c->friends_list[friendnum].last_reported_announced == 0) {
1416 onion_c->friends_list[friendnum].last_reported_announced = unix_time();
1417 }
1418
1419 uint64_t backoff_interval = (unix_time() - onion_c->friends_list[friendnum].last_reported_announced)
1420 / ONION_FRIEND_BACKOFF_FACTOR;
1421
1422 if (backoff_interval > ONION_FRIEND_MAX_PING_INTERVAL) {
1423 backoff_interval = ONION_FRIEND_MAX_PING_INTERVAL;
1424 }
1425
1426 if (interval < backoff_interval) {
1427 interval = backoff_interval;
1428 }
1390 } 1429 }
1391 1430
1392 unsigned int i, count = 0; 1431 unsigned int i, count = 0;
1393 Onion_Node *list_nodes = onion_c->friends_list[friendnum].clients_list; 1432 Onion_Node *list_nodes = onion_c->friends_list[friendnum].clients_list;
1394 1433
1395 if (!onion_c->friends_list[friendnum].is_online) { 1434 if (!onion_c->friends_list[friendnum].is_online) {
1435 // ensure we get a response from some node roughly once per
1436 // (interval / MAX_ONION_CLIENTS)
1437 bool ping_random = true;
1438
1396 for (i = 0; i < MAX_ONION_CLIENTS; ++i) { 1439 for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
1397 if (is_timeout(list_nodes[i].timestamp, FRIEND_ONION_NODE_TIMEOUT)) { 1440 if (!(is_timeout(list_nodes[i].timestamp, interval / MAX_ONION_CLIENTS)
1441 && is_timeout(list_nodes[i].last_pinged, ONION_NODE_PING_INTERVAL))) {
1442 ping_random = false;
1443 break;
1444 }
1445 }
1446
1447 for (i = 0; i < MAX_ONION_CLIENTS; ++i) {
1448 if (onion_node_timed_out(&list_nodes[i])) {
1398 continue; 1449 continue;
1399 } 1450 }
1400 1451
@@ -1406,9 +1457,16 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
1406 continue; 1457 continue;
1407 } 1458 }
1408 1459
1409 if (is_timeout(list_nodes[i].last_pinged, interval)) { 1460 if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
1461 continue;
1462 }
1463
1464 if (is_timeout(list_nodes[i].last_pinged, interval)
1465 || (ping_random && rand() % (MAX_ONION_CLIENTS - i) == 0)) {
1410 if (client_send_announce_request(onion_c, friendnum + 1, list_nodes[i].ip_port, list_nodes[i].public_key, 0, ~0) == 0) { 1466 if (client_send_announce_request(onion_c, friendnum + 1, list_nodes[i].ip_port, list_nodes[i].public_key, 0, ~0) == 0) {
1411 list_nodes[i].last_pinged = unix_time(); 1467 list_nodes[i].last_pinged = unix_time();
1468 ++list_nodes[i].unsuccessful_pings;
1469 ping_random = false;
1412 } 1470 }
1413 } 1471 }
1414 } 1472 }
@@ -1422,16 +1480,18 @@ static void do_friend(Onion_Client *onion_c, uint16_t friendnum)
1422 n = (MAX_ONION_CLIENTS / 2); 1480 n = (MAX_ONION_CLIENTS / 2);
1423 } 1481 }
1424 1482
1425 if (num_nodes != 0) { 1483 if (count <= (uint32_t)rand() % MAX_ONION_CLIENTS) {
1426 unsigned int j; 1484 if (num_nodes != 0) {
1485 unsigned int j;
1427 1486
1428 for (j = 0; j < n; ++j) { 1487 for (j = 0; j < n; ++j) {
1429 unsigned int num = rand() % num_nodes; 1488 unsigned int num = rand() % num_nodes;
1430 client_send_announce_request(onion_c, friendnum + 1, onion_c->path_nodes[num].ip_port, 1489 client_send_announce_request(onion_c, friendnum + 1, onion_c->path_nodes[num].ip_port,
1431 onion_c->path_nodes[num].public_key, 0, ~0); 1490 onion_c->path_nodes[num].public_key, 0, ~0);
1432 } 1491 }
1433 1492
1434 ++onion_c->friends_list[friendnum].run_count; 1493 ++onion_c->friends_list[friendnum].run_count;
1494 }
1435 } 1495 }
1436 } else { 1496 } else {
1437 ++onion_c->friends_list[friendnum].run_count; 1497 ++onion_c->friends_list[friendnum].run_count;
@@ -1463,13 +1523,16 @@ void oniondata_registerhandler(Onion_Client *onion_c, uint8_t byte, oniondata_ha
1463#define ANNOUNCE_INTERVAL_NOT_ANNOUNCED 3 1523#define ANNOUNCE_INTERVAL_NOT_ANNOUNCED 3
1464#define ANNOUNCE_INTERVAL_ANNOUNCED ONION_NODE_PING_INTERVAL 1524#define ANNOUNCE_INTERVAL_ANNOUNCED ONION_NODE_PING_INTERVAL
1465 1525
1526#define TIME_TO_STABLE (ONION_NODE_PING_INTERVAL * 6)
1527#define ANNOUNCE_INTERVAL_STABLE (ONION_NODE_PING_INTERVAL * 8)
1528
1466static void do_announce(Onion_Client *onion_c) 1529static void do_announce(Onion_Client *onion_c)
1467{ 1530{
1468 unsigned int i, count = 0; 1531 unsigned int i, count = 0;
1469 Onion_Node *list_nodes = onion_c->clients_announce_list; 1532 Onion_Node *list_nodes = onion_c->clients_announce_list;
1470 1533
1471 for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { 1534 for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
1472 if (is_timeout(list_nodes[i].timestamp, ONION_NODE_TIMEOUT)) { 1535 if (onion_node_timed_out(&list_nodes[i])) {
1473 continue; 1536 continue;
1474 } 1537 }
1475 1538
@@ -1481,16 +1544,48 @@ static void do_announce(Onion_Client *onion_c)
1481 continue; 1544 continue;
1482 } 1545 }
1483 1546
1547 if (list_nodes[i].unsuccessful_pings >= ONION_NODE_MAX_PINGS) {
1548 continue;
1549 }
1550
1551
1484 unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED; 1552 unsigned int interval = ANNOUNCE_INTERVAL_NOT_ANNOUNCED;
1485 1553
1486 if (list_nodes[i].is_stored && path_exists(&onion_c->onion_paths_self, list_nodes[i].path_used)) { 1554 if (list_nodes[i].is_stored && path_exists(&onion_c->onion_paths_self, list_nodes[i].path_used)) {
1487 interval = ANNOUNCE_INTERVAL_ANNOUNCED; 1555 interval = ANNOUNCE_INTERVAL_ANNOUNCED;
1556
1557 uint32_t pathnum = list_nodes[i].path_used % NUMBER_ONION_PATHS;
1558
1559 /* A node/path is considered 'stable', and can be pinged less
1560 * aggressively, if it has survived for at least TIME_TO_STABLE
1561 * and the latest packets sent to it are not timing out.
1562 */
1563 if (is_timeout(list_nodes[i].added_time, TIME_TO_STABLE)
1564 && !(list_nodes[i].unsuccessful_pings > 0
1565 && is_timeout(list_nodes[i].last_pinged, ONION_NODE_TIMEOUT))
1566 && is_timeout(onion_c->onion_paths_self.path_creation_time[pathnum], TIME_TO_STABLE)
1567 && !(onion_c->onion_paths_self.last_path_used_times[pathnum] > 0
1568 && is_timeout(onion_c->onion_paths_self.last_path_used[pathnum], ONION_PATH_TIMEOUT))) {
1569 interval = ANNOUNCE_INTERVAL_STABLE;
1570 }
1488 } 1571 }
1489 1572
1490 if (is_timeout(list_nodes[i].last_pinged, interval)) { 1573 if (is_timeout(list_nodes[i].last_pinged, interval)
1574 || (is_timeout(onion_c->last_announce, ONION_NODE_PING_INTERVAL)
1575 && rand() % (MAX_ONION_CLIENTS_ANNOUNCE - i) == 0)) {
1576 uint32_t path_to_use = list_nodes[i].path_used;
1577
1578 if (list_nodes[i].unsuccessful_pings == ONION_NODE_MAX_PINGS - 1
1579 && is_timeout(list_nodes[i].added_time, TIME_TO_STABLE)) {
1580 /* Last chance for a long-lived node - try a random path */
1581 path_to_use = ~0;
1582 }
1583
1491 if (client_send_announce_request(onion_c, 0, list_nodes[i].ip_port, list_nodes[i].public_key, 1584 if (client_send_announce_request(onion_c, 0, list_nodes[i].ip_port, list_nodes[i].public_key,
1492 list_nodes[i].ping_id, list_nodes[i].path_used) == 0) { 1585 list_nodes[i].ping_id, path_to_use) == 0) {
1493 list_nodes[i].last_pinged = unix_time(); 1586 list_nodes[i].last_pinged = unix_time();
1587 ++list_nodes[i].unsuccessful_pings;
1588 onion_c->last_announce = unix_time();
1494 } 1589 }
1495 } 1590 }
1496 } 1591 }
@@ -1507,7 +1602,7 @@ static void do_announce(Onion_Client *onion_c)
1507 path_nodes = onion_c->path_nodes; 1602 path_nodes = onion_c->path_nodes;
1508 } 1603 }
1509 1604
1510 if (count < (uint32_t)rand() % MAX_ONION_CLIENTS_ANNOUNCE) { 1605 if (count <= (uint32_t)rand() % MAX_ONION_CLIENTS_ANNOUNCE) {
1511 if (num_nodes != 0) { 1606 if (num_nodes != 0) {
1512 for (i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) { 1607 for (i = 0; i < (MAX_ONION_CLIENTS_ANNOUNCE / 2); ++i) {
1513 unsigned int num = rand() % num_nodes; 1608 unsigned int num = rand() % num_nodes;
@@ -1534,7 +1629,7 @@ static int onion_isconnected(const Onion_Client *onion_c)
1534 } 1629 }
1535 1630
1536 for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) { 1631 for (i = 0; i < MAX_ONION_CLIENTS_ANNOUNCE; ++i) {
1537 if (!is_timeout(onion_c->clients_announce_list[i].timestamp, ONION_NODE_TIMEOUT)) { 1632 if (!onion_node_timed_out(&onion_c->clients_announce_list[i])) {
1538 ++num; 1633 ++num;
1539 1634
1540 if (onion_c->clients_announce_list[i].is_stored) { 1635 if (onion_c->clients_announce_list[i].is_stored) {