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_transport_service.h"
33 #include "gnunet_util_lib.h"
36 #define DEBUG_TOPOLOGY GNUNET_NO
39 * For how long do we blacklist a peer after a failed
42 #define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
45 * For how long do we blacklist a friend after a failed
48 #define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
51 * How frequently are we allowed to ask PEERINFO for more
52 * HELLO's to advertise (at most)?
54 #define MIN_HELLO_GATHER_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 27)
57 * How often do we at most advertise the same HELLO to the same peer?
58 * Also used to remove HELLOs of peers that PEERINFO no longer lists
61 #define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
65 * List of neighbours, friends and blacklisted peers.
71 * This is a linked list.
73 struct PeerList *next;
76 * Is this peer listed here because he is a friend?
81 * Are we connected to this peer right now?
86 * Until what time should we not try to connect again
89 struct GNUNET_TIME_Absolute blacklisted_until;
92 * Last time we transmitted a HELLO to this peer?
94 struct GNUNET_TIME_Absolute last_hello_sent;
99 struct GNUNET_PeerIdentity id;
105 * List of HELLOs we may consider for advertising.
110 * This is a linked list.
112 struct HelloList *next;
115 * Pointer to the HELLO message. Memory allocated as part
116 * of the "struct HelloList" --- do not free!
118 struct GNUNET_HELLO_Message *msg;
121 * Bloom filter used to mark which peers already got
124 struct GNUNET_CONTAINER_BloomFilter *filter;
127 * What peer is this HELLO for?
129 struct GNUNET_PeerIdentity id;
132 * When should we remove this entry from the linked list (either
133 * resetting the filter or possibly eliminating it for good because
134 * we no longer consider the peer to be participating in the
137 struct GNUNET_TIME_Absolute expiration;
142 * Linked list of HELLOs for advertising.
144 static struct HelloList *hellos;
149 static struct GNUNET_SCHEDULER_Handle * sched;
154 static const struct GNUNET_CONFIGURATION_Handle * cfg;
157 * Handle to the core API.
159 static struct GNUNET_CORE_Handle *handle;
162 * Handle to the transport API.
164 static struct GNUNET_TRANSPORT_Handle *transport;
167 * Identity of this peer.
169 static struct GNUNET_PeerIdentity my_identity;
172 * Linked list of all of our friends and all of our current
175 static struct PeerList *friends;
178 * Timestamp from the last time we tried to gather HELLOs.
180 static struct GNUNET_TIME_Absolute last_hello_gather_time;
183 * Flag to disallow non-friend connections (pure F2F mode).
185 static int friends_only;
188 * Minimum number of friends to have in the
189 * connection set before we allow non-friends.
191 static unsigned int minimum_friend_count;
194 * Number of peers (friends and others) that we are currently connected to.
196 static unsigned int connection_count;
199 * Target number of connections.
201 static unsigned int target_connection_count;
204 * Number of friends that we are currently connected to.
206 static unsigned int friend_count;
209 * Should the topology daemon try to establish connections?
211 static int autoconnect;
214 * Are we currently having a request pending with
215 * PEERINFO asking for HELLOs for advertising?
217 static int hello_gathering_active;
222 * Force a disconnect from the specified peer.
225 force_disconnect (const struct GNUNET_PeerIdentity *peer)
227 GNUNET_CORE_peer_configure (handle,
229 GNUNET_TIME_UNIT_FOREVER_REL,
239 * Function called by core when our attempt to connect
240 * succeeded. Does nothing.
243 ready_callback (void *cls,
244 size_t size, void *buf)
251 * Try to connect to the specified peer.
253 * @param peer who we should try to connect to
254 * @param pos entry in our friend list; NULL if not in friend list yet
257 attempt_connect (const struct GNUNET_PeerIdentity *peer,
258 struct PeerList *pos)
265 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
271 pos = GNUNET_malloc (sizeof(struct PeerList));
276 if (GNUNET_YES == pos->is_friend)
277 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
279 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
280 GNUNET_CORE_notify_transmit_ready (handle,
282 GNUNET_TIME_UNIT_MINUTES,
284 sizeof(struct GNUNET_MessageHeader),
291 * Is this peer one of our friends?
294 is_friend (const struct GNUNET_PeerIdentity * peer)
296 struct PeerList *pos;
301 if ( (GNUNET_YES == pos->is_friend) &&
302 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
311 * Check if an additional connection from the given peer is allowed.
314 is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
316 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
317 return GNUNET_SYSERR; /* disallow connections to self */
318 if (is_friend (peer))
320 if (GNUNET_YES == friends_only)
321 return GNUNET_SYSERR;
322 if (friend_count >= minimum_friend_count)
324 return GNUNET_SYSERR;
329 * Method called whenever a peer connects.
332 * @param peer peer identity this notification is about
334 static void connect_notify (void *cls,
336 GNUNET_PeerIdentity * peer)
338 struct PeerList *pos;
344 if ( (GNUNET_YES == pos->is_friend) &&
345 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
347 GNUNET_assert (GNUNET_NO == pos->is_connected);
348 pos->is_connected = GNUNET_YES;
349 pos->blacklisted_until.value = 0; /* remove blacklisting */
355 pos = GNUNET_malloc (sizeof(struct PeerList));
357 pos->is_connected = GNUNET_YES;
360 if (GNUNET_OK != is_connection_allowed (peer))
361 force_disconnect (peer);
366 * Disconnect from all non-friends (we're below quota).
371 struct PeerList *pos;
376 if (GNUNET_NO == pos->is_friend)
378 GNUNET_assert (GNUNET_YES == pos->is_connected);
379 force_disconnect (&pos->id);
387 * Method called whenever a peer disconnects.
390 * @param peer peer identity this notification is about
392 static void disconnect_notify (void *cls,
394 GNUNET_PeerIdentity * peer)
396 struct PeerList *pos;
397 struct PeerList *prev;
404 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
406 GNUNET_assert (GNUNET_YES == pos->is_connected);
407 pos->is_connected = GNUNET_NO;
408 if (GNUNET_YES == pos->is_friend)
411 if (friend_count < minimum_friend_count)
413 /* disconnect from all non-friends */
415 attempt_connect (peer, pos);
424 prev->next = pos->next;
437 * Find more peers that we should connect to and ask the
438 * core to establish connections.
441 find_more_peers (void *cls,
442 const struct GNUNET_SCHEDULER_TaskContext *tc);
446 * Determine when we should try again to find more peers and
450 schedule_peer_search ()
452 struct GNUNET_TIME_Relative delay;
454 /* Typically, we try again every 15 minutes; the minimum period is
455 15s; if we are above the connection target, we reduce re-trying
456 by the square of how much we are above; so for example, with 200%
457 of the connection target we would only look for more peers once
458 every hour (after all, we're quite busy processing twice as many
459 connections as we intended to have); similarly, if we are at only
460 25% of our connectivity goal, we will try 16x as hard to connect
461 (so roughly once a minute, plus the 15s minimum delay */
462 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
463 15 + 15 * 60 * connection_count * connection_count / target_connection_count / target_connection_count);
464 GNUNET_SCHEDULER_add_delayed (sched,
466 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
467 GNUNET_SCHEDULER_NO_TASK,
477 * Iterator called on each address.
479 * @param cls flag that we will set if we see any addresses
480 * @param tname name of the transport
481 * @param expiration when will the given address expire
482 * @param addr the address of the peer
483 * @param addrlen number of bytes in addr
484 * @return GNUNET_SYSERR always, to terminate iteration
487 address_iterator (void *cls,
489 struct GNUNET_TIME_Absolute expiration,
490 const void *addr, size_t addrlen)
494 return GNUNET_SYSERR;
499 * We've gotten a HELLO from another peer.
500 * Consider it for advertising.
503 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
506 struct GNUNET_PeerIdentity pid;
507 struct HelloList *pos;
510 have_address = GNUNET_NO;
511 GNUNET_HELLO_iterate_addresses (hello,
515 if (GNUNET_NO == have_address)
516 return; /* no point in advertising this one... */
517 GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
521 if (0 == memcmp (&pos->id,
523 sizeof(struct GNUNET_PeerIdentity)))
524 return; /* duplicate, at least "mostly" */
527 size = GNUNET_HELLO_size (hello);
528 pos = GNUNET_malloc (sizeof(struct HelloList) + size);
529 pos->msg = (struct GNUNET_HELLO_Message*) &pos[1];
530 memcpy (&pos->msg, hello, size);
532 pos->expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
533 /* 2^{-5} chance of not sending a HELLO to a peer is
534 acceptably small (if the filter is 50% full);
535 64 bytes of memory are small compared to the rest
536 of the data structure and would only really become
537 "useless" once a HELLO has been passed on to ~100
538 other peers, which is likely more than enough in
539 any case; hence 64, 5 as bloomfilter parameters. */
540 pos->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
541 /* never send a peer its own HELLO */
542 GNUNET_CONTAINER_bloomfilter_add (pos->filter, &pos->id.hashPubKey);
549 * Peerinfo calls this function to let us know about a
550 * possible peer that we might want to connect to.
553 process_peer (void *cls,
554 const struct GNUNET_PeerIdentity *peer,
555 const struct GNUNET_HELLO_Message *hello,
558 struct PeerList *pos;
562 /* last call, schedule 'find_more_peers' again... */
563 schedule_peer_search ();
568 /* no HELLO known; can not connect, ignore! */
571 if (0 == memcmp (&my_identity,
572 peer, sizeof (struct GNUNET_PeerIdentity)))
573 return; /* that's me! */
575 consider_for_advertising (hello);
579 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
581 if (GNUNET_YES == pos->is_connected)
583 if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
584 return; /* peer still blacklisted */
585 if (GNUNET_YES == pos->is_friend)
587 attempt_connect (peer, pos);
593 if (GNUNET_YES == friends_only)
595 if (friend_count < minimum_friend_count)
597 attempt_connect (peer, NULL);
602 * Try to add more friends to our connection set.
607 struct PeerList *pos;
612 if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
613 (GNUNET_YES == pos->is_friend) &&
614 (GNUNET_YES != pos->is_connected) )
615 attempt_connect (&pos->id, pos);
622 * Discard peer entries for blacklisted peers
623 * where the blacklisting has expired.
626 discard_old_blacklist_entries ()
628 struct PeerList *pos;
629 struct PeerList *next;
630 struct PeerList *prev;
634 while (NULL != (pos = next))
637 if ( (GNUNET_NO == pos->is_friend) &&
638 (GNUNET_NO == pos->is_connected) &&
639 (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
641 /* delete 'pos' from list */
657 * Find more peers that we should connect to and ask the
658 * core to establish connections.
661 find_more_peers (void *cls,
662 const struct GNUNET_SCHEDULER_TaskContext *tc)
664 discard_old_blacklist_entries ();
665 if (target_connection_count <= connection_count)
667 schedule_peer_search ();
670 if ( (GNUNET_YES == friends_only) ||
671 (friend_count < minimum_friend_count) )
674 schedule_peer_search ();
677 GNUNET_PEERINFO_for_all (cfg,
680 0, GNUNET_TIME_UNIT_FOREVER_REL,
681 &process_peer, NULL);
686 * Function called after GNUNET_CORE_connect has succeeded
687 * (or failed for good).
690 * @param server handle to the server, NULL if we failed
691 * @param my_id ID of this peer, NULL if we failed
692 * @param publicKey public key of this peer, NULL if we failed
695 core_init (void *cls,
696 struct GNUNET_CORE_Handle * server,
697 const struct GNUNET_PeerIdentity *
700 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
705 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
706 _("Failed to connect to core service, can not manage topology!\n"));
710 my_identity = *my_id;
712 GNUNET_SCHEDULER_add_delayed (sched,
714 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
715 GNUNET_SCHEDULER_NO_TASK,
716 GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
723 * gnunet-daemon-topology command line options.
725 static struct GNUNET_GETOPT_CommandLineOption options[] = {
726 GNUNET_GETOPT_OPTION_END
731 * Read the friends file.
734 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
741 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
742 unsigned int entries_found;
746 GNUNET_CONFIGURATION_get_value_filename (cfg,
751 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
752 _("Option `%s' in section `%s' not specified!\n"),
757 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
758 GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
759 | GNUNET_DISK_PERM_USER_WRITE);
760 if (0 != STAT (fn, &frstat))
762 if ((friends_only) || (minimum_friend_count > 0))
764 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
765 _("Could not read friends list `%s'\n"), fn);
770 if (frstat.st_size == 0)
772 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
773 _("Friends file `%s' is empty.\n"),
778 data = GNUNET_malloc_large (frstat.st_size);
779 if (frstat.st_size !=
780 GNUNET_DISK_fn_read (fn, data, frstat.st_size))
782 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783 _("Failed to read friends list from `%s'\n"), fn);
790 while ((pos < frstat.st_size) && isspace (data[pos]))
792 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
793 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
795 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
796 if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
798 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
799 _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
800 (unsigned long long) pos);
802 while ((pos < frstat.st_size) && (!isspace (data[pos])))
806 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
807 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
809 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
810 _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
811 (unsigned long long) pos,
817 fl = GNUNET_malloc (sizeof(struct PeerList));
818 fl->is_friend = GNUNET_YES;
819 fl->id.hashPubKey = hc;
823 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
824 while ((pos < frstat.st_size) && isspace (data[pos]))
829 if ( (minimum_friend_count > entries_found) &&
830 (friends_only == GNUNET_NO) )
832 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
833 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
835 if ( (minimum_friend_count > target_connection_count) &&
836 (friends_only == GNUNET_NO) )
838 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
839 _("More friendly connections required than target total number of connections.\n"));
845 * This function is called whenever an encrypted HELLO message is
849 * @param other the other peer involved (sender or receiver, NULL
850 * for loopback messages where we are both sender and receiver)
851 * @param message the actual HELLO message
852 * @return GNUNET_OK to keep the connection open,
853 * GNUNET_SYSERR to close it (signal serious error)
856 handle_encrypted_hello (void *cls,
857 const struct GNUNET_PeerIdentity * other,
858 const struct GNUNET_MessageHeader *
861 if (transport != NULL)
862 GNUNET_TRANSPORT_offer_hello (transport,
869 * Peerinfo calls this function to let us know about a
870 * possible peer that we might want to connect to.
873 * @param peer NULL for the end of the list, otherwise a peer identity
874 * @param hello a HELLO for a peer, or NULL
875 * @param trust how much do we trust the given peer?
878 gather_hello_callback (void *cls,
879 const struct GNUNET_PeerIdentity *peer,
880 const struct GNUNET_HELLO_Message *hello,
885 hello_gathering_active = GNUNET_NO;
889 consider_for_advertising (hello);
894 * Function to fill send buffer with HELLO.
897 * @param receiver the receiver of the message
898 * @param position is the reference to the
899 * first unused position in the buffer where GNUnet is building
901 * @param padding is the number of bytes left in that buffer.
902 * @return the number of bytes written to
903 * that buffer (must be a positive number).
906 hello_advertising (void *cls,
907 const struct GNUNET_PeerIdentity *
913 struct HelloList *pos;
914 struct HelloList *prev;
915 struct HelloList *next;
921 if (0 == memcmp (&pl->id, receiver, sizeof (struct GNUNET_PeerIdentity)))
930 /* find applicable HELLOs */
933 while (NULL != (pos = next))
937 GNUNET_CONTAINER_bloomfilter_test (pos->filter,
938 &receiver->hashPubKey))
940 if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
942 /* time to discard... */
947 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
957 size = GNUNET_HELLO_size (pos->msg);
960 memcpy (position, pos->msg, size);
961 GNUNET_CONTAINER_bloomfilter_add (pos->filter,
962 &receiver->hashPubKey);
970 if ( (GNUNET_NO == hello_gathering_active) &&
971 (GNUNET_TIME_absolute_get_duration (last_hello_gather_time).value >
972 MIN_HELLO_GATHER_DELAY.value) )
974 hello_gathering_active = GNUNET_YES;
975 last_hello_gather_time = GNUNET_TIME_absolute_get();
976 GNUNET_PEERINFO_for_all (cfg,
979 0, GNUNET_TIME_UNIT_FOREVER_REL,
980 &gather_hello_callback, NULL);
987 * Last task run during shutdown. Disconnects us from
988 * the transport and core.
991 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
995 GNUNET_TRANSPORT_disconnect (transport);
997 GNUNET_CORE_disconnect (handle);
999 while (NULL != (pl = friends))
1008 * Main function that will be run.
1010 * @param cls closure
1011 * @param s the scheduler to use
1012 * @param args remaining command-line arguments
1013 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1014 * @param c configuration
1018 struct GNUNET_SCHEDULER_Handle * s,
1020 const char *cfgfile,
1021 const struct GNUNET_CONFIGURATION_Handle * c)
1023 struct GNUNET_CORE_MessageHandler handlers[] =
1025 { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1028 unsigned long long opt;
1032 autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1035 friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1039 GNUNET_CONFIGURATION_get_value_number (cfg,
1044 minimum_friend_count = (unsigned int) opt;
1046 GNUNET_CONFIGURATION_get_value_number (cfg,
1048 "TARGET-CONNECTION-COUNT",
1051 target_connection_count = (unsigned int) opt;
1053 if ( (friends_only == GNUNET_YES) ||
1054 (minimum_friend_count > 0) )
1055 read_friends_file (cfg);
1057 transport = GNUNET_TRANSPORT_connect (sched,
1063 GNUNET_CORE_connect (sched,
1065 GNUNET_TIME_UNIT_FOREVER_REL,
1075 GNUNET_SCHEDULER_add_delayed (sched,
1077 GNUNET_SCHEDULER_PRIORITY_IDLE,
1078 GNUNET_SCHEDULER_NO_TASK,
1079 GNUNET_TIME_UNIT_FOREVER_REL,
1080 &cleaning_task, NULL);
1085 * The main function for the topology daemon.
1087 * @param argc number of arguments from the command line
1088 * @param argv command line arguments
1089 * @return 0 ok, 1 on error
1092 main (int argc, char *const *argv)
1097 GNUNET_PROGRAM_run (argc,
1100 _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1102 &run, NULL)) ? 0 : 1;
1106 /* end of gnunet-daemon-topology.c */