Mercurial > hg > index.fcgi > gift-gnutella > gift-gnutella-0.0.11-1pba
diff src/gt_netorg.c @ 0:d39e1d0d75b6
initial add
author | paulo@hit-nxdomain.opendns.com |
---|---|
date | Sat, 20 Feb 2010 21:18:28 -0800 |
parents | |
children |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/gt_netorg.c Sat Feb 20 21:18:28 2010 -0800 1.3 @@ -0,0 +1,577 @@ 1.4 +/* 1.5 + * $Id: gt_netorg.c,v 1.47 2005/01/04 15:00:51 mkern Exp $ 1.6 + * 1.7 + * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) 1.8 + * 1.9 + * This program is free software; you can redistribute it and/or modify it 1.10 + * under the terms of the GNU General Public License as published by the 1.11 + * Free Software Foundation; either version 2, or (at your option) any 1.12 + * later version. 1.13 + * 1.14 + * This program is distributed in the hope that it will be useful, but 1.15 + * WITHOUT ANY WARRANTY; without even the implied warranty of 1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.17 + * General Public License for more details. 1.18 + */ 1.19 + 1.20 +#include "gt_gnutella.h" 1.21 + 1.22 +#include "gt_node.h" 1.23 +#include "gt_node_list.h" 1.24 +#include "gt_netorg.h" 1.25 + 1.26 +#include "gt_connect.h" 1.27 +#include "gt_accept.h" 1.28 + 1.29 +#include "gt_packet.h" 1.30 + 1.31 +#include "gt_node_cache.h" 1.32 +#include "gt_web_cache.h" 1.33 + 1.34 +/*****************************************************************************/ 1.35 + 1.36 +/* how often we check the network's condition */ 1.37 +#define MAINTAIN_INTERVAL (10 * SECONDS) 1.38 + 1.39 +/* how often to check to disconnect idle nodes */ 1.40 +#define IDLE_DISCONNECT_INTERVAL (2 * MINUTES) 1.41 + 1.42 +/* how often to trim the node list */ 1.43 +#define CLEANUP_INTERVAL (15 * MINUTES) 1.44 + 1.45 +/* how often to clear indications of connecting to nodes */ 1.46 +#define RETRY_ALL_INTERVAL (60 * MINUTES) 1.47 + 1.48 +/* maximum number of unreplied pings before disconnecting from a node */ 1.49 +#define MAX_UNREPLIED_PINGS 10 1.50 + 1.51 +/* how many connections attempts each maintain loop for nodes previously 1.52 + * registered */ 1.53 +#define TRY_CONNECT_NODE_LIST gt_config_get_int("connect/node_list=3") 1.54 + 1.55 +/* how many connection attempts for nodes in the pong cache */ 1.56 +#define TRY_CONNECT_NODE_CACHE gt_config_get_int("connect/node_cache=7") 1.57 + 1.58 +/*****************************************************************************/ 1.59 + 1.60 +/* timer for initiating/closing connections */ 1.61 +static timer_id maintain_timer; 1.62 + 1.63 +/* timer for disconnecting connections */ 1.64 +static timer_id disconnect_timer; 1.65 + 1.66 +/* timer for disconnecting idle nodes */ 1.67 +static timer_id idle_disconnect_timer; 1.68 + 1.69 +/* timer for cleaning up the node list */ 1.70 +static timer_id cleanup_timer; 1.71 + 1.72 +/* timer to clear 'tried' indicators to retry connecting */ 1.73 +static timer_id retry_all_timer; 1.74 + 1.75 +/*****************************************************************************/ 1.76 + 1.77 +static GtNode *node_disconnect_one (TCPC *c, GtNode *node, void *udata) 1.78 +{ 1.79 + GT->DBGFN (GT, "[%s]: disconnecting", net_ip_str (GT_NODE(c)->ip)); 1.80 + gt_node_disconnect (c); 1.81 + return NULL; 1.82 +} 1.83 + 1.84 +static GtNode *node_ping (TCPC *c, GtNode *node, GtPacket *packet) 1.85 +{ 1.86 + gt_packet_send (c, packet); 1.87 + 1.88 + /* ->pings_with_noreply gets set to zero when the node sends a pong */ 1.89 + if (gt_packet_ttl (packet) == 1) 1.90 + node->pings_with_noreply++; 1.91 + 1.92 + return NULL; 1.93 +} 1.94 + 1.95 +static void ping_hosts_ttl (uint8_t ttl) 1.96 +{ 1.97 + GtPacket *packet; 1.98 + 1.99 + if (!(packet = gt_packet_new (GT_MSG_PING, ttl, NULL))) 1.100 + return; 1.101 + 1.102 + gt_conn_foreach (GT_CONN_FOREACH(node_ping), packet, 1.103 + GT_NODE_NONE, GT_NODE_CONNECTED, 0); 1.104 + 1.105 + gt_packet_free (packet); 1.106 +} 1.107 + 1.108 +static void ping_hosts (time_t now) 1.109 +{ 1.110 + static time_t last_ping; 1.111 + static time_t last_keep_alive; 1.112 + BOOL need_connections; 1.113 + uint8_t ttl; 1.114 + 1.115 + need_connections = gt_conn_need_connections (GT_NODE_ULTRA); 1.116 + 1.117 + if (now - last_ping < 30 * SECONDS && !need_connections) 1.118 + return; 1.119 + 1.120 + last_ping = now; 1.121 + 1.122 + /* ping to get more hosts if we need connections */ 1.123 + if (now - last_keep_alive >= 1 * MINUTES) 1.124 + { 1.125 + /* do a keepalive */ 1.126 + ttl = 1; 1.127 + last_keep_alive = now; 1.128 + } 1.129 + else 1.130 + { 1.131 + /* get more hosts */ 1.132 + ttl = 7; 1.133 + } 1.134 + 1.135 + ping_hosts_ttl (ttl); 1.136 +} 1.137 + 1.138 +/*****************************************************************************/ 1.139 + 1.140 +static void disconnect_no_query_route (void) 1.141 +{ 1.142 + int nr_supernodes; 1.143 + 1.144 + /* only disconnect if theres other nodes to fallback on */ 1.145 + nr_supernodes = gt_conn_length (GT_NODE_ULTRA, GT_NODE_CONNECTED); 1.146 + 1.147 + if (nr_supernodes > 0) 1.148 + { 1.149 + gt_conn_foreach (node_disconnect_one, NULL, 1.150 + GT_NODE_LEAF, GT_NODE_CONNECTED, 0); 1.151 + } 1.152 +} 1.153 + 1.154 +static void report_connected_leaf (int connected) 1.155 +{ 1.156 + static int last_connected = 0; 1.157 + 1.158 + if (connected != last_connected) 1.159 + { 1.160 + GT->DBGFN (GT, "connected=%d nodes=%d", connected, 1.161 + gt_conn_length (GT_NODE_NONE, GT_NODE_ANY)); 1.162 + last_connected = connected; 1.163 + } 1.164 +} 1.165 + 1.166 +static int get_need_as_ultra (gt_node_class_t klass) 1.167 +{ 1.168 + switch (klass) 1.169 + { 1.170 + case GT_NODE_ULTRA: return GT_PEER_CONNECTIONS; 1.171 + case GT_NODE_LEAF: return GT_LEAF_CONNECTIONS; 1.172 + default: return 0; 1.173 + } 1.174 +} 1.175 + 1.176 +static int get_need_as_leaf (gt_node_class_t klass) 1.177 +{ 1.178 + switch (klass) 1.179 + { 1.180 + case GT_NODE_ULTRA: return GT_SHIELDED_CONNECTIONS; 1.181 + case GT_NODE_LEAF: return 0; /* no leaf<->leaf connections allowed */ 1.182 + default: return 0; 1.183 + } 1.184 +} 1.185 + 1.186 +int gt_conn_need_connections (gt_node_class_t klass) 1.187 +{ 1.188 + int connected; 1.189 + int desired; 1.190 + 1.191 + connected = gt_conn_length (klass, GT_NODE_CONNECTED); 1.192 + 1.193 + /* don't call this with multiple classes -- the need of one 1.194 + * class could cancel a surplus of the other */ 1.195 + assert (klass == GT_NODE_ULTRA || klass == GT_NODE_LEAF); 1.196 + 1.197 + if (GT_SELF->klass & GT_NODE_ULTRA) 1.198 + desired = get_need_as_ultra (klass); 1.199 + else 1.200 + desired = get_need_as_leaf (klass); 1.201 + 1.202 + return desired - connected; 1.203 +} 1.204 + 1.205 +static void disconnect_hosts (gt_node_class_t klass, int excess) 1.206 +{ 1.207 + int connected; 1.208 + 1.209 + connected = gt_conn_length (klass, GT_NODE_CONNECTED); 1.210 + 1.211 + GT->DBGFN (GT, "too many connections (%d)[%s], disconnecting %d", 1.212 + connected, gt_node_class_str (klass), excess); 1.213 + 1.214 + while (excess-- > 0) 1.215 + { 1.216 + GtNode *node = gt_conn_random (klass, GT_NODE_CONNECTED); 1.217 + 1.218 + /* TODO: send BYE message here */ 1.219 + 1.220 + assert (GT_CONN(node) != NULL); 1.221 + gt_node_disconnect (GT_CONN(node)); 1.222 + } 1.223 +} 1.224 + 1.225 +static BOOL disconnect_excess_timer (void *udata) 1.226 +{ 1.227 + int leaf_excess; 1.228 + int ultra_excess; 1.229 + 1.230 + leaf_excess = gt_conn_need_connections (GT_NODE_LEAF); 1.231 + ultra_excess = gt_conn_need_connections (GT_NODE_ULTRA); 1.232 + 1.233 + if (leaf_excess < 0) 1.234 + disconnect_hosts (GT_NODE_LEAF, -leaf_excess); 1.235 + 1.236 + if (ultra_excess < 0) 1.237 + disconnect_hosts (GT_NODE_ULTRA, -ultra_excess); 1.238 + 1.239 + disconnect_timer = 0; 1.240 + return FALSE; 1.241 +} 1.242 + 1.243 +static GtNode *collect_each_node (TCPC *c, GtNode *node, List **nodes) 1.244 +{ 1.245 + if (node->tried_connect) 1.246 + return NULL; 1.247 + 1.248 + if (!node->gt_port) 1.249 + return NULL; 1.250 + 1.251 + /* mark having tried to to connect to this node already */ 1.252 + node->tried_connect = TRUE; 1.253 + 1.254 + *nodes = list_append (*nodes, node); 1.255 + 1.256 + /* stop iterating if we have enough nodes */ 1.257 + if (list_length (*nodes) >= TRY_CONNECT_NODE_LIST) 1.258 + return node; 1.259 + 1.260 + return NULL; 1.261 +} 1.262 + 1.263 +static GtNode *clear_try_bit (TCPC *c, GtNode *node, void *udata) 1.264 +{ 1.265 + node->tried_connect = FALSE; 1.266 + return NULL; 1.267 +} 1.268 + 1.269 +static BOOL prune_registered (struct cached_node *cached, void *udata) 1.270 +{ 1.271 + if (gt_node_lookup (cached->addr.ip, cached->addr.port)) 1.272 + { 1.273 + GT->DBGFN (GT, "pruning %s (already registered)", 1.274 + net_ip_str (cached->addr.ip), cached->addr.port); 1.275 + free (cached); 1.276 + return TRUE; 1.277 + } 1.278 + 1.279 + return FALSE; 1.280 +} 1.281 + 1.282 +static BOOL register_cached (struct cached_node *cached, void *udata) 1.283 +{ 1.284 + GtNode *node; 1.285 + 1.286 + node = gt_node_lookup (cached->addr.ip, cached->addr.port); 1.287 + 1.288 + if (node) 1.289 + { 1.290 + /* 1.291 + * Argh, gt_node_lookup only matches by IP 1.292 + * This should be assert (0) 1.293 + */ 1.294 + assert (node->gt_port != cached->addr.port); 1.295 + 1.296 + free (cached); 1.297 + return TRUE; 1.298 + } 1.299 + 1.300 + node = gt_node_register (cached->addr.ip, cached->addr.port, 1.301 + cached->klass); 1.302 + 1.303 + /* we've got to free the node, Jim */ 1.304 + free (cached); 1.305 + 1.306 + /* this happens if the address is invalid or a mem failure */ 1.307 + if (!node) 1.308 + return TRUE; 1.309 + 1.310 + gt_connect (node); 1.311 + node->tried_connect = TRUE; 1.312 + 1.313 + return TRUE; 1.314 +} 1.315 + 1.316 +static BOOL connect_each (GtNode *node, void *udata) 1.317 +{ 1.318 + if (gt_connect (node) < 0) 1.319 + { 1.320 + GT->err (GT, "Failed to connect to node %s:%hu: %s", 1.321 + net_ip_str (node->ip), node->gt_port, GIFT_NETERROR()); 1.322 + return TRUE; 1.323 + } 1.324 + 1.325 + return TRUE; 1.326 +} 1.327 + 1.328 +/*****************************************************************************/ 1.329 + 1.330 +/* returns number of nodes we will try to connect to */ 1.331 +static size_t try_some_nodes (time_t now) 1.332 +{ 1.333 + List *nodes = NULL; 1.334 + List *cached = NULL; 1.335 + size_t total = 0; 1.336 + size_t nr; 1.337 + size_t len; 1.338 + size_t count; 1.339 + 1.340 + /* the total amount of nodes we should try */ 1.341 + nr = TRY_CONNECT_NODE_LIST + TRY_CONNECT_NODE_CACHE; 1.342 + 1.343 + /* 1.344 + * Iterate the node (pong) cache and node list until we 1.345 + * have seen 'nr' nodes or there are no more hosts to try. 1.346 + */ 1.347 + 1.348 + while (total < nr) 1.349 + { 1.350 + gt_conn_foreach (GT_CONN_FOREACH(collect_each_node), &nodes, 1.351 + GT_NODE_NONE, GT_NODE_DISCONNECTED, 0); 1.352 + 1.353 + /* grab at most nr - total nodes (still need to fix the preceeding 1.354 + * call to gt_conn_foreach() to respect 'total') */ 1.355 + count = MIN (nr - total, TRY_CONNECT_NODE_CACHE); 1.356 + assert (count >= 0); 1.357 + 1.358 + cached = gt_node_cache_get_remove (count); 1.359 + 1.360 + /* registered nodes can still slip into our node cache, argh */ 1.361 + cached = list_foreach_remove (cached, 1.362 + (ListForeachFunc)prune_registered, 1.363 + NULL); 1.364 + 1.365 + len = list_length (nodes) + list_length (cached); 1.366 + 1.367 + total += len; 1.368 + 1.369 + if (len == 0) 1.370 + break; 1.371 + 1.372 + nodes = list_foreach_remove (nodes, (ListForeachFunc)connect_each, 1.373 + NULL); 1.374 + assert (nodes == NULL); 1.375 + 1.376 + cached = list_foreach_remove (cached, (ListForeachFunc)register_cached, 1.377 + NULL); 1.378 + assert (cached == NULL); 1.379 + } 1.380 + 1.381 + return total; 1.382 +} 1.383 + 1.384 +static void maintain_class (gt_node_class_t klass, time_t now) 1.385 +{ 1.386 + int connected; 1.387 + int need; 1.388 + 1.389 + connected = gt_conn_length (klass, GT_NODE_CONNECTED); 1.390 + need = gt_conn_need_connections (klass); 1.391 + 1.392 + /* 1.393 + * print the number of nodes connected if it has changed 1.394 + * XXX: print leaves from ultrapeers and leaves too. 1.395 + * damn static variables to hell 1.396 + */ 1.397 + if (klass == GT_NODE_ULTRA) 1.398 + report_connected_leaf (connected); 1.399 + 1.400 + /* 0 == perfection */ 1.401 + if (need == 0) 1.402 + return; 1.403 + 1.404 + /* disconnect some nodes */ 1.405 + if (need < 0) 1.406 + { 1.407 + if (disconnect_timer) 1.408 + return; 1.409 + 1.410 + /* 1.411 + * Disconnect the node soon, because it could happen that 1.412 + * someone will disconnect from us first, causing cascading 1.413 + * disconnects. 1.414 + */ 1.415 + GT->DBGFN (GT, "starting disconnect timer..."); 1.416 + disconnect_timer = timer_add (4 * SECONDS, 1.417 + (TimerCallback)disconnect_excess_timer, 1.418 + NULL); 1.419 + return; 1.420 + } 1.421 + 1.422 + /* 1.423 + * If try_some_nodes() returns 0, then there are no nodes in the node 1.424 + * cache nor any on the node list that we haven't tried yet. In that case, 1.425 + * we need to contact the gwebcaches and hope a fresh infusion of nodes 1.426 + * will help. While we wait, we retry all the nodes we already tried by 1.427 + * clearing node->tried_connect for each node, which otherwise prevents 1.428 + * from recontacting the nodes. 1.429 + * 1.430 + * We will "block" on the gwebcaches if the bandwidth is completely 1.431 + * saturated and we can't get a reply from anyone, or if there are no 1.432 + * ultrapeers with connection slots available. The gwebcache subsystem 1.433 + * imposes its own limits on how often it will contact gwebcaches, so if 1.434 + * we do end up in this situation, hopefully we will simply spend most of 1.435 + * the time unconnected rather than hammering the gwebcaches. 1.436 + */ 1.437 + if (try_some_nodes (now) == 0) 1.438 + { 1.439 + size_t len; 1.440 + 1.441 + len = gt_conn_length (GT_NODE_NONE, GT_NODE_ANY); 1.442 + GT->dbg (GT, "try_some_nodes() returned 0. node list len=%u", len); 1.443 + 1.444 + if (connected == 0 || len < 20) 1.445 + { 1.446 + /* try to get more hosts */ 1.447 + GT->dbg (GT, "No hosts to try. Looking in gwebcaches..."); 1.448 + gt_web_cache_update (); 1.449 + } 1.450 + 1.451 + GT->dbg (GT, "Retrying to connect to nodes..."); 1.452 + 1.453 + /* while we are waiting for the gwebcaches, try each node again */ 1.454 + gt_conn_foreach (GT_CONN_FOREACH(clear_try_bit), NULL, 1.455 + GT_NODE_NONE, GT_NODE_ANY, 0); 1.456 + 1.457 + return; 1.458 + } 1.459 +} 1.460 + 1.461 +static GtNode *disconnect_no_ping_replies (TCPC *c, GtNode *node, void *udata) 1.462 +{ 1.463 + if (node->pings_with_noreply < MAX_UNREPLIED_PINGS) 1.464 + return NULL; 1.465 + 1.466 + GT->DBGSOCK (GT, node->c, "%d unreplied pings. disconnecting", 1.467 + node->pings_with_noreply); 1.468 + 1.469 + gt_node_disconnect (c); 1.470 + return NULL; 1.471 +} 1.472 + 1.473 +/*****************************************************************************/ 1.474 + 1.475 +/* 1.476 + * This is the main network maintainence function. All connections to the 1.477 + * network are initiated from here. 1.478 + */ 1.479 +static BOOL maintain (void *udata) 1.480 +{ 1.481 + time_t now; 1.482 + 1.483 + now = time (NULL); 1.484 + 1.485 + /* disconnect nodes without query routing if we are not a supernode */ 1.486 + if (!(GT_SELF->klass & GT_NODE_ULTRA)) 1.487 + disconnect_no_query_route (); 1.488 + 1.489 +#if 0 1.490 + trace_list (connections); 1.491 +#endif 1.492 + 1.493 + /* 1.494 + * Send pings to all connected nodes. We used to do this only every 1.495 + * minute, but because some nodes have short timeouts if they receive 1.496 + * nothing from you, we now do it every MAINTAIN_INTERVAL. 1.497 + */ 1.498 + ping_hosts (now); 1.499 + 1.500 + maintain_class (GT_NODE_ULTRA, now); 1.501 + maintain_class (GT_NODE_LEAF, now); 1.502 + 1.503 + return TRUE; 1.504 +} 1.505 + 1.506 +static BOOL idle_disconnect (void *udata) 1.507 +{ 1.508 + gt_conn_foreach (GT_CONN_FOREACH(disconnect_no_ping_replies), NULL, 1.509 + GT_NODE_NONE, GT_NODE_CONNECTED, 0); 1.510 + return TRUE; 1.511 +} 1.512 + 1.513 +static BOOL cleanup (void *udata) 1.514 +{ 1.515 + /* trim excess nodes */ 1.516 + gt_conn_trim (); 1.517 + 1.518 + /* save to disk important nodes from the node list */ 1.519 + gt_node_list_save (); 1.520 + 1.521 + /* save to disk important nodes from the node cache */ 1.522 + gt_node_cache_save (); 1.523 + 1.524 + return TRUE; 1.525 +} 1.526 + 1.527 +static BOOL retry_all (void *udata) 1.528 +{ 1.529 + /* 1.530 + * Clear the 'tried' bit for all nodes, so if we start looking for nodes 1.531 + * we try reconnecting to the ones we know about instead of contacting the 1.532 + * gwebcaches. 1.533 + * 1.534 + * NOTE: should all the nodes be possibly retried (GT_NODE_ANY) or 1.535 + * only those that are disconnected (GT_NODE_DISCONNECTED)? 1.536 + */ 1.537 + gt_conn_foreach (GT_CONN_FOREACH(clear_try_bit), NULL, 1.538 + GT_NODE_NONE, GT_NODE_ANY, 0); 1.539 + 1.540 + return TRUE; 1.541 +} 1.542 +/*****************************************************************************/ 1.543 + 1.544 +void gt_netorg_init (void) 1.545 +{ 1.546 + if (maintain_timer != 0) 1.547 + return; 1.548 + 1.549 + /* load the node cache */ 1.550 + gt_node_cache_init (); 1.551 + 1.552 + /* setup the links maintain timer */ 1.553 + maintain_timer = timer_add (MAINTAIN_INTERVAL, 1.554 + maintain, NULL); 1.555 + 1.556 + idle_disconnect_timer = timer_add (IDLE_DISCONNECT_INTERVAL, 1.557 + idle_disconnect, NULL); 1.558 + 1.559 + cleanup_timer = timer_add (CLEANUP_INTERVAL, 1.560 + cleanup, NULL); 1.561 + 1.562 + retry_all_timer = timer_add (RETRY_ALL_INTERVAL, 1.563 + retry_all, NULL); 1.564 + 1.565 + /* call it now so we don't have to wait the first time */ 1.566 + maintain (NULL); 1.567 +} 1.568 + 1.569 +void gt_netorg_cleanup (void) 1.570 +{ 1.571 + /* save the node cache */ 1.572 + gt_node_cache_cleanup (); 1.573 + 1.574 + timer_remove_zero (&disconnect_timer); 1.575 + 1.576 + timer_remove_zero (&maintain_timer); 1.577 + timer_remove_zero (&idle_disconnect_timer); 1.578 + timer_remove_zero (&cleanup_timer); 1.579 + timer_remove_zero (&retry_all_timer); 1.580 +}