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 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 pos NULL if not in friend list yet
256 attempt_connect (const struct GNUNET_PeerIdentity *peer,
257 struct PeerList *pos)
264 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
270 pos = GNUNET_malloc (sizeof(struct PeerList));
275 if (GNUNET_YES == pos->is_friend)
276 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
278 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
279 GNUNET_CORE_notify_transmit_ready (handle,
281 GNUNET_TIME_UNIT_MINUTES,
283 sizeof(struct GNUNET_MessageHeader),
290 * Is this peer one of our friends?
293 is_friend (const struct GNUNET_PeerIdentity * peer)
295 struct PeerList *pos;
300 if ( (GNUNET_YES == pos->is_friend) &&
301 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
310 * Check if an additional connection from the given peer is allowed.
313 is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
315 if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
316 return GNUNET_SYSERR; /* disallow connections to self */
317 if (is_friend (peer))
319 if (GNUNET_YES == friends_only)
320 return GNUNET_SYSERR;
321 if (friend_count >= minimum_friend_count)
323 return GNUNET_SYSERR;
328 * Method called whenever a peer connects.
331 * @param peer peer identity this notification is about
333 static void connect_notify (void *cls,
335 GNUNET_PeerIdentity * peer)
337 struct PeerList *pos;
343 if ( (GNUNET_YES == pos->is_friend) &&
344 (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
346 GNUNET_assert (GNUNET_NO == pos->is_connected);
347 pos->is_connected = GNUNET_YES;
348 pos->blacklisted_until.value = 0; /* remove blacklisting */
354 pos = GNUNET_malloc (sizeof(struct PeerList));
356 pos->is_connected = GNUNET_YES;
359 if (GNUNET_OK != is_connection_allowed (peer))
360 force_disconnect (peer);
365 * Disconnect from all non-friends (we're below quota).
370 struct PeerList *pos;
375 if (GNUNET_NO == pos->is_friend)
377 GNUNET_assert (GNUNET_YES == pos->is_connected);
378 force_disconnect (&pos->id);
386 * Method called whenever a peer disconnects.
389 * @param peer peer identity this notification is about
391 static void disconnect_notify (void *cls,
393 GNUNET_PeerIdentity * peer)
395 struct PeerList *pos;
396 struct PeerList *prev;
403 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
405 GNUNET_assert (GNUNET_YES == pos->is_connected);
406 pos->is_connected = GNUNET_NO;
407 if (GNUNET_YES == pos->is_friend)
410 if (friend_count < minimum_friend_count)
412 /* disconnect from all non-friends */
414 attempt_connect (peer, pos);
423 prev->next = pos->next;
436 * Find more peers that we should connect to and ask the
437 * core to establish connections.
440 find_more_peers (void *cls,
441 const struct GNUNET_SCHEDULER_TaskContext *tc);
445 * Determine when we should try again to find more peers and
449 schedule_peer_search ()
451 struct GNUNET_TIME_Relative delay;
453 /* Typically, we try again every 15 minutes; the minimum period is
454 15s; if we are above the connection target, we reduce re-trying
455 by the square of how much we are above; so for example, with 200%
456 of the connection target we would only look for more peers once
457 every hour (after all, we're quite busy processing twice as many
458 connections as we intended to have); similarly, if we are at only
459 25% of our connectivity goal, we will try 16x as hard to connect
460 (so roughly once a minute, plus the 15s minimum delay */
461 delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
462 15 + 15 * 60 * connection_count * connection_count / target_connection_count / target_connection_count);
463 GNUNET_SCHEDULER_add_delayed (sched,
465 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
466 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
476 * Iterator called on each address.
478 * @param cls flag that we will set if we see any addresses.
481 address_iterator (void *cls,
483 struct GNUNET_TIME_Absolute expiration,
484 const void *addr, size_t addrlen)
488 return GNUNET_SYSERR;
493 * We've gotten a HELLO from another peer.
494 * Consider it for advertising.
497 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
500 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
501 struct GNUNET_PeerIdentity pid;
502 struct HelloList *pos;
505 have_address = GNUNET_NO;
506 GNUNET_HELLO_iterate_addresses (hello,
510 if (GNUNET_NO == have_address)
511 return; /* no point in advertising this one... */
512 GNUNET_HELLO_get_key (hello, &pkey);
513 GNUNET_CRYPTO_hash (&pkey, sizeof (pkey), &pid.hashPubKey);
517 if (0 == memcmp (&pos->id,
519 sizeof(struct GNUNET_PeerIdentity)))
520 return; /* duplicate, at least "mostly" */
523 size = GNUNET_HELLO_size (hello);
524 pos = GNUNET_malloc (sizeof(struct HelloList) + size);
525 pos->msg = (struct GNUNET_HELLO_Message*) &pos[1];
526 memcpy (&pos->msg, hello, size);
528 pos->expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
529 /* 2^{-5} chance of not sending a HELLO to a peer is
530 acceptably small (if the filter is 50% full);
531 64 bytes of memory are small compared to the rest
532 of the data structure and would only really become
533 "useless" once a HELLO has been passed on to ~100
534 other peers, which is likely more than enough in
535 any case; hence 64, 5 as bloomfilter parameters. */
536 pos->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
537 /* never send a peer its own HELLO */
538 GNUNET_CONTAINER_bloomfilter_add (pos->filter, &pos->id.hashPubKey);
545 * Peerinfo calls this function to let us know about a
546 * possible peer that we might want to connect to.
549 process_peer (void *cls,
550 const struct GNUNET_PeerIdentity *peer,
551 const struct GNUNET_HELLO_Message *hello,
554 struct PeerList *pos;
558 /* last call, schedule 'find_more_peers' again... */
559 schedule_peer_search ();
564 /* no HELLO known; can not connect, ignore! */
567 if (0 == memcmp (&my_identity,
568 peer, sizeof (struct GNUNET_PeerIdentity)))
569 return; /* that's me! */
571 consider_for_advertising (hello);
575 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
577 if (GNUNET_YES == pos->is_connected)
579 if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
580 return; /* peer still blacklisted */
581 if (GNUNET_YES == pos->is_friend)
583 attempt_connect (peer, pos);
589 if (GNUNET_YES == friends_only)
591 if (friend_count < minimum_friend_count)
593 attempt_connect (peer, NULL);
598 * Try to add more friends to our connection set.
603 struct PeerList *pos;
608 if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
609 (GNUNET_YES == pos->is_friend) &&
610 (GNUNET_YES != pos->is_connected) )
611 attempt_connect (&pos->id, pos);
618 * Discard peer entries for blacklisted peers
619 * where the blacklisting has expired.
622 discard_old_blacklist_entries ()
624 struct PeerList *pos;
625 struct PeerList *next;
626 struct PeerList *prev;
630 while (NULL != (pos = next))
633 if ( (GNUNET_NO == pos->is_friend) &&
634 (GNUNET_NO == pos->is_connected) &&
635 (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
637 /* delete 'pos' from list */
653 * Find more peers that we should connect to and ask the
654 * core to establish connections.
657 find_more_peers (void *cls,
658 const struct GNUNET_SCHEDULER_TaskContext *tc)
660 discard_old_blacklist_entries ();
661 if (target_connection_count <= connection_count)
663 schedule_peer_search ();
666 if ( (GNUNET_YES == friends_only) ||
667 (friend_count < minimum_friend_count) )
670 schedule_peer_search ();
673 GNUNET_PEERINFO_for_all (cfg,
676 0, GNUNET_TIME_UNIT_FOREVER_REL,
677 &process_peer, NULL);
682 * Function called after GNUNET_CORE_connect has succeeded
683 * (or failed for good).
686 * @param server handle to the server, NULL if we failed
687 * @param my_id ID of this peer, NULL if we failed
688 * @param publicKey public key of this peer, NULL if we failed
691 core_init (void *cls,
692 struct GNUNET_CORE_Handle * server,
693 const struct GNUNET_PeerIdentity *
696 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
701 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
702 _("Failed to connect to core service, can not manage topology!\n"));
706 my_identity = *my_id;
708 GNUNET_SCHEDULER_add_delayed (sched,
710 GNUNET_SCHEDULER_PRIORITY_DEFAULT,
711 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
712 GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
719 * gnunet-daemon-topology command line options.
721 static struct GNUNET_GETOPT_CommandLineOption options[] = {
722 GNUNET_GETOPT_OPTION_END
727 * Read the friends file.
730 read_friends_file (struct GNUNET_CONFIGURATION_Handle *cfg)
737 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
738 unsigned int entries_found;
742 GNUNET_CONFIGURATION_get_value_filename (cfg,
746 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
747 GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
748 | GNUNET_DISK_PERM_USER_WRITE);
749 if (0 != STAT (fn, &frstat))
751 if ((friends_only) || (minimum_friend_count > 0))
753 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
754 _("Could not read friends list `%s'\n"), fn);
759 if (frstat.st_size == 0)
761 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
762 _("Friends file `%s' is empty.\n"),
767 data = GNUNET_malloc_large (frstat.st_size);
768 if (frstat.st_size !=
769 GNUNET_DISK_fn_read (fn, data, frstat.st_size))
771 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
772 _("Failed to read friends list from `%s'\n"), fn);
779 while ((pos < frstat.st_size) && isspace (data[pos]))
781 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
782 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
784 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
785 if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
787 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
788 _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
789 (unsigned long long) pos);
791 while ((pos < frstat.st_size) && (!isspace (data[pos])))
795 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
796 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
798 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
799 _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
800 (unsigned long long) pos,
806 fl = GNUNET_malloc (sizeof(struct PeerList));
807 fl->is_friend = GNUNET_YES;
808 fl->id.hashPubKey = hc;
812 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
813 while ((pos < frstat.st_size) && isspace (data[pos]))
818 if ( (minimum_friend_count > entries_found) &&
819 (friends_only == GNUNET_NO) )
821 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
822 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
824 if ( (minimum_friend_count > target_connection_count) &&
825 (friends_only == GNUNET_NO) )
827 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
828 _("More friendly connections required than target total number of connections.\n"));
834 * This function is called whenever an encrypted HELLO message is
838 * @param peer the other peer involved (sender or receiver, NULL
839 * for loopback messages where we are both sender and receiver)
840 * @param message the actual HELLO message
841 * @return GNUNET_OK to keep the connection open,
842 * GNUNET_SYSERR to close it (signal serious error)
845 handle_encrypted_hello (void *cls,
846 const struct GNUNET_PeerIdentity * other,
847 const struct GNUNET_MessageHeader *
850 if (transport != NULL)
851 GNUNET_TRANSPORT_offer_hello (transport,
858 * Peerinfo calls this function to let us know about a
859 * possible peer that we might want to connect to.
862 gather_hello_callback (void *cls,
863 const struct GNUNET_PeerIdentity *peer,
864 const struct GNUNET_HELLO_Message *hello,
869 hello_gathering_active = GNUNET_NO;
873 consider_for_advertising (hello);
878 * Function to fill send buffer with HELLO.
880 * @param receiver the receiver of the message
881 * @param position is the reference to the
882 * first unused position in the buffer where GNUnet is building
884 * @param padding is the number of bytes left in that buffer.
885 * @return the number of bytes written to
886 * that buffer (must be a positive number).
889 hello_advertising (void *cls,
890 const struct GNUNET_PeerIdentity *
892 void *position, unsigned int padding)
895 struct HelloList *pos;
896 struct HelloList *prev;
897 struct HelloList *next;
903 if (0 == memcmp (&pl->id, receiver, sizeof (struct GNUNET_PeerIdentity)))
912 /* find applicable HELLOs */
915 while (NULL != (pos = next))
919 GNUNET_CONTAINER_bloomfilter_test (pos->filter,
920 &receiver->hashPubKey))
922 if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
924 /* time to discard... */
929 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
939 size = GNUNET_HELLO_size (pos->msg);
942 memcpy (position, pos->msg, size);
943 GNUNET_CONTAINER_bloomfilter_add (pos->filter,
944 &receiver->hashPubKey);
952 if ( (GNUNET_NO == hello_gathering_active) &&
953 (GNUNET_TIME_absolute_get_duration (last_hello_gather_time).value >
954 MIN_HELLO_GATHER_DELAY.value) )
956 hello_gathering_active = GNUNET_YES;
957 last_hello_gather_time = GNUNET_TIME_absolute_get();
958 GNUNET_PEERINFO_for_all (cfg,
961 0, GNUNET_TIME_UNIT_FOREVER_REL,
962 &gather_hello_callback, NULL);
969 * Last task run during shutdown. Disconnects us from
970 * the transport and core.
973 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
977 GNUNET_TRANSPORT_disconnect (transport);
979 GNUNET_CORE_disconnect (handle);
981 while (NULL != (pl = friends))
990 * Main function that will be run.
993 * @param s the scheduler to use
994 * @param args remaining command-line arguments
995 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
996 * @param c configuration
1000 struct GNUNET_SCHEDULER_Handle * s,
1002 const char *cfgfile,
1003 struct GNUNET_CONFIGURATION_Handle * c)
1005 struct GNUNET_CORE_MessageHandler handlers[] =
1007 { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1010 unsigned long long opt;
1014 autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1017 friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1021 GNUNET_CONFIGURATION_get_value_number (cfg,
1025 minimum_friend_count = (unsigned int) opt;
1027 GNUNET_CONFIGURATION_get_value_number (cfg,
1029 "TARGET-CONNECTION-COUNT",
1031 target_connection_count = (unsigned int) opt;
1033 if ( (friends_only == GNUNET_YES) ||
1034 (minimum_friend_count > 0) )
1035 read_friends_file (cfg);
1037 transport = GNUNET_TRANSPORT_connect (sched,
1043 GNUNET_CORE_connect (sched,
1045 GNUNET_TIME_UNIT_FOREVER_REL,
1055 GNUNET_SCHEDULER_add_delayed (sched,
1057 GNUNET_SCHEDULER_PRIORITY_IDLE,
1058 GNUNET_SCHEDULER_NO_PREREQUISITE_TASK,
1059 GNUNET_TIME_UNIT_FOREVER_REL,
1060 &cleaning_task, NULL);
1065 * The main function for the topology daemon.
1067 * @param argc number of arguments from the command line
1068 * @param argv command line arguments
1069 * @return 0 ok, 1 on error
1072 main (int argc, char *const *argv)
1077 GNUNET_PROGRAM_run (argc,
1080 _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1082 &run, NULL)) ? 0 : 1;
1086 /* end of gnunet-daemon-topology.c */