2 This file is part of GNUnet.
3 (C) 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file topology/gnunet-daemon-topology.c
23 * @brief code for bootstrapping via topology servers
24 * @author Christian Grothoff
29 #include "gnunet_core_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_util_lib.h"
35 #define DEBUG_TOPOLOGY GNUNET_NO
38 * For how long do we blacklist a peer after a failed
41 #define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
44 * For how long do we blacklist a friend after a failed
47 #define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
51 * List of neighbours, friends and blacklisted peers.
57 * This is a linked list.
59 struct PeerList *next;
62 * Is this peer listed here because he is a friend?
67 * Are we connected to this peer right now?
72 * Until what time should we not try to connect again
75 struct GNUNET_TIME_Absolute blacklisted_until;
80 struct GNUNET_PeerIdentity id;
88 static struct GNUNET_SCHEDULER_Handle * sched;
93 static struct GNUNET_CONFIGURATION_Handle * cfg;
96 * Handle to the core API.
98 static struct GNUNET_CORE_Handle *handle;
101 * Identity of this peer.
103 static struct GNUNET_PeerIdentity my_identity;
106 * Linked list of all of our friends and all of our current
109 static struct PeerList *friends;
112 * Flag to disallow non-friend connections (pure F2F mode).
114 static int friends_only;
117 * Minimum number of friends to have in the
118 * connection set before we allow non-friends.
120 static unsigned int minimum_friend_count;
123 * Number of peers (friends and others) that we are currently connected to.
125 static unsigned int connection_count;
128 * Target number of connections.
130 static unsigned int target_connection_count;
133 * Number of friends that we are currently connected to.
135 static unsigned int friend_count;
138 * Should the topology daemon try to establish connections?
140 static int autoconnect;
145 * Force a disconnect from the specified peer.
148 force_disconnect (const struct GNUNET_PeerIdentity *peer)
150 GNUNET_CORE_peer_configure (handle,
152 GNUNET_TIME_UNIT_FOREVER_REL,
162 * Function called by core when our attempt to connect
163 * succeeded. Does nothing.
166 ready_callback (void *cls,
167 size_t size, void *buf)
174 * Try to connect to the specified peer.
176 * @param pos NULL if not in friend list yet
179 attempt_connect (const struct GNUNET_PeerIdentity *peer,
180 struct PeerList *pos)
187 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
193 pos = GNUNET_malloc (sizeof(struct PeerList));
198 if (GNUNET_YES == pos->is_friend)
199 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
201 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
202 GNUNET_CORE_notify_transmit_ready (handle,
204 GNUNET_TIME_UNIT_MINUTES,
206 sizeof(struct GNUNET_MessageHeader),
213 * Is this peer one of our friends?
216 is_friend (const struct GNUNET_PeerIdentity * peer)
218 struct PeerList *pos;
223 if ( (GNUNET_YES == pos->is_friend) &&
224 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
233 * Check if an additional connection from the given peer is allowed.
236 is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
238 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
239 return GNUNET_SYSERR; /* disallow connections to self */
240 if (is_friend (peer))
242 if (GNUNET_YES == friends_only)
243 return GNUNET_SYSERR;
244 if (friend_count >= minimum_friend_count)
246 return GNUNET_SYSERR;
251 * Method called whenever a peer connects.
254 * @param peer peer identity this notification is about
256 static void connect_notify (void *cls,
258 GNUNET_PeerIdentity * peer)
260 struct PeerList *pos;
266 if ( (GNUNET_YES == pos->is_friend) &&
267 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
269 GNUNET_assert (GNUNET_NO == pos->is_connected);
270 pos->is_connected = GNUNET_YES;
271 pos->blacklisted_until.value = 0; /* remove blacklisting */
277 pos = GNUNET_malloc (sizeof(struct PeerList));
279 pos->is_connected = GNUNET_YES;
282 if (GNUNET_OK != is_connection_allowed (peer))
283 force_disconnect (peer);
288 * Disconnect from all non-friends (we're below quota).
293 struct PeerList *pos;
298 if (GNUNET_NO == pos->is_friend)
300 GNUNET_assert (GNUNET_YES == pos->is_connected);
301 force_disconnect (&pos->id);
309 * Method called whenever a peer disconnects.
312 * @param peer peer identity this notification is about
314 static void disconnect_notify (void *cls,
316 GNUNET_PeerIdentity * peer)
318 struct PeerList *pos;
319 struct PeerList *prev;
326 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
328 GNUNET_assert (GNUNET_YES == pos->is_connected);
329 pos->is_connected = GNUNET_NO;
330 if (GNUNET_YES == pos->is_friend)
333 if (friend_count < minimum_friend_count)
335 /* disconnect from all non-friends */
337 attempt_connect (peer, pos);
346 prev->next = pos->next;
359 * Find more peers that we should connect to and ask the
360 * core to establish connections.
363 find_more_peers (void *cls,
364 const struct GNUNET_SCHEDULER_TaskContext *tc);
368 * Determine when we should try again to find more peers and
372 schedule_peer_search ()
374 struct GNUNET_TIME_Relative delay;
376 /* Typically, we try again every 15 minutes; the minimum period is
377 15s; if we are above the connection target, we reduce re-trying
378 by the square of how much we are above; so for example, with 200%
379 of the connection target we would only look for more peers once
380 every hour (after all, we're quite busy processing twice as many
381 connections as we intended to have); similarly, if we are at only
382 25% of our connectivity goal, we will try 16x as hard to connect
383 (so roughly once a minute, plus the 15s minimum delay */
384 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
385 15 + 15 * 60 * connection_count * connection_count / target_connection_count / target_connection_count);
386 GNUNET_SCHEDULER_add_delayed (sched,
388 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
389 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
397 * Peerinfo calls this function to let us know about a
398 * possible peer that we might want to connect to.
401 process_peer (void *cls,
402 const struct GNUNET_PeerIdentity * peer,
403 const struct GNUNET_HELLO_Message * hello,
406 struct PeerList *pos;
410 /* last call, schedule 'find_more_peers' again... */
411 schedule_peer_search ();
416 /* no HELLO known; can not connect, ignore! */
419 if (0 == memcmp (&my_identity,
420 peer, sizeof (struct GNUNET_PeerIdentity)))
421 return; /* that's me! */
426 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
428 if (GNUNET_YES == pos->is_connected)
430 if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
431 return; /* peer still blacklisted */
432 if (GNUNET_YES == pos->is_friend)
434 attempt_connect (peer, pos);
440 if (GNUNET_YES == friends_only)
442 if (friend_count < minimum_friend_count)
444 attempt_connect (peer, NULL);
449 * Try to add more friends to our connection set.
454 struct PeerList *pos;
459 if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
460 (GNUNET_YES == pos->is_friend) &&
461 (GNUNET_YES != pos->is_connected) )
462 attempt_connect (&pos->id, pos);
469 * Discard peer entries for blacklisted peers
470 * where the blacklisting has expired.
473 discard_old_blacklist_entries ()
475 struct PeerList *pos;
476 struct PeerList *next;
477 struct PeerList *prev;
481 while (NULL != (pos = next))
484 if ( (GNUNET_NO == pos->is_friend) &&
485 (GNUNET_NO == pos->is_connected) &&
486 (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
488 /* delete 'pos' from list */
504 * Find more peers that we should connect to and ask the
505 * core to establish connections.
508 find_more_peers (void *cls,
509 const struct GNUNET_SCHEDULER_TaskContext *tc)
511 discard_old_blacklist_entries ();
512 if (target_connection_count <= connection_count)
514 schedule_peer_search ();
517 if ( (GNUNET_YES == friends_only) ||
518 (friend_count < minimum_friend_count) )
521 schedule_peer_search ();
524 GNUNET_PEERINFO_for_all (cfg,
527 0, GNUNET_TIME_UNIT_FOREVER_REL,
528 &process_peer, NULL);
533 * Function called after GNUNET_CORE_connect has succeeded
534 * (or failed for good).
537 * @param server handle to the server, NULL if we failed
538 * @param my_id ID of this peer, NULL if we failed
539 * @param publicKey public key of this peer, NULL if we failed
542 core_init (void *cls,
543 struct GNUNET_CORE_Handle * server,
544 const struct GNUNET_PeerIdentity *
547 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
552 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
553 _("Failed to connect to core service, can not manage topology!\n"));
557 my_identity = *my_id;
559 GNUNET_SCHEDULER_add_delayed (sched,
561 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
562 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
563 GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
570 * gnunet-daemon-topology command line options.
572 static struct GNUNET_GETOPT_CommandLineOption options[] = {
573 GNUNET_GETOPT_OPTION_END
578 * Read the friends file.
581 read_friends_file (struct GNUNET_CONFIGURATION_Handle *cfg)
588 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
589 unsigned int entries_found;
593 GNUNET_CONFIGURATION_get_value_filename (cfg,
597 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
598 GNUNET_DISK_file_write (fn, NULL, 0, "600");
599 if (0 != STAT (fn, &frstat))
601 if ((friends_only) || (minimum_friend_count > 0))
603 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
604 _("Could not read friends list `%s'\n"), fn);
609 if (frstat.st_size == 0)
611 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
612 _("Friends file `%s' is empty.\n"),
617 data = GNUNET_malloc_large (frstat.st_size);
618 if (frstat.st_size !=
619 GNUNET_DISK_file_read (fn, frstat.st_size, data))
621 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
622 _("Failed to read friends list from `%s'\n"), fn);
629 while ((pos < frstat.st_size) && isspace (data[pos]))
631 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
632 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
634 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
635 if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
637 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
638 _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
639 (unsigned long long) pos);
641 while ((pos < frstat.st_size) && (!isspace (data[pos])))
645 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
646 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
648 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
649 _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
650 (unsigned long long) pos,
656 fl = GNUNET_malloc (sizeof(struct PeerList));
657 fl->is_friend = GNUNET_YES;
658 fl->id.hashPubKey = hc;
662 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
663 while ((pos < frstat.st_size) && isspace (data[pos]))
668 if ( (minimum_friend_count > entries_found) &&
669 (friends_only == GNUNET_NO) )
671 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
672 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
674 if ( (minimum_friend_count > target_connection_count) &&
675 (friends_only == GNUNET_NO) )
677 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
678 _("More friendly connections required than target total number of connections.\n"));
684 * Main function that will be run.
687 * @param s the scheduler to use
688 * @param args remaining command-line arguments
689 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
690 * @param c configuration
694 struct GNUNET_SCHEDULER_Handle * s,
697 struct GNUNET_CONFIGURATION_Handle * c)
699 struct GNUNET_CORE_MessageHandler handlers[] =
703 unsigned long long opt;
707 autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
710 friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
714 GNUNET_CONFIGURATION_get_value_number (cfg,
718 minimum_friend_count = (unsigned int) opt;
720 GNUNET_CONFIGURATION_get_value_number (cfg,
722 "TARGET-CONNECTION-COUNT",
724 target_connection_count = (unsigned int) opt;
726 if ( (friends_only == GNUNET_YES) ||
727 (minimum_friend_count > 0) )
728 read_friends_file (cfg);
729 GNUNET_CORE_connect (sched,
731 GNUNET_TIME_UNIT_FOREVER_REL,
744 * The main function for the topology daemon.
746 * @param argc number of arguments from the command line
747 * @param argv command line arguments
748 * @return 0 ok, 1 on error
751 main (int argc, char *const *argv)
756 GNUNET_PROGRAM_run (argc,
759 _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
761 &run, NULL)) ? 0 : 1;
765 /* end of gnunet-daemon-topology.c */