2 This file is part of GNUnet.
3 (C) 2007, 2008, 2009, 2010 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 maintaining the mesh topology
24 * @author Christian Grothoff
29 #include "gnunet_core_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_util_lib.h"
37 #define DEBUG_TOPOLOGY GNUNET_NO
40 * For how long do we blacklist a peer after a failed connection
43 #define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
46 * For how long do we blacklist a friend after a failed connection
49 #define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
52 * How often do we at most advertise any HELLO to a peer?
54 #define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
57 * How often do we at most advertise the same HELLO to the same peer?
59 #define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
63 * List of neighbours, friends and blacklisted peers.
69 * This is a linked list.
71 struct PeerList *next;
74 * Our handle for the request to transmit HELLOs to this peer; NULL
75 * if no such request is pending.
77 struct GNUNET_CORE_TransmitHandle *hello_req;
80 * Our handle for the request to connect to this peer; NULL if no
81 * such request is pending.
83 struct GNUNET_CORE_PeerRequestHandle *connect_req;
86 * Pointer to the HELLO message of this peer; can be NULL.
88 struct GNUNET_HELLO_Message *hello;
91 * Bloom filter used to mark which peers already got the HELLO
94 struct GNUNET_CONTAINER_BloomFilter *filter;
97 * Our request handle for *whitelisting* this peer (NULL if
98 * no whitelisting request is pending).
100 struct GNUNET_TRANSPORT_BlacklistRequest *wh;
103 * Is this peer listed here because he is a friend?
108 * Are we connected to this peer right now?
113 * Are we currently blocking this peer (via blacklist)?
118 * Until what time should we not try to connect again
121 struct GNUNET_TIME_Absolute blacklisted_until;
124 * Next time we are allowed to transmit a HELLO to this peer?
126 struct GNUNET_TIME_Absolute next_hello_allowed;
129 * When should we reset the bloom filter of this entry?
131 struct GNUNET_TIME_Absolute filter_expiration;
134 * ID of task we use to wait for the time to send the next HELLO
137 GNUNET_SCHEDULER_TaskIdentifier hello_delay_task;
142 struct GNUNET_PeerIdentity id;
148 * Entry in linked list of active 'disconnect' requests that we have issued.
150 struct DisconnectList
153 * This is a doubly-linked list.
155 struct DisconnectList *next;
158 * This is a doubly-linked list.
160 struct DisconnectList *prev;
163 * Our request handle.
165 struct GNUNET_TRANSPORT_BlacklistRequest *rh;
168 * Peer we tried to disconnect.
170 struct GNUNET_PeerIdentity peer;
176 * Our peerinfo notification context. We use notification
177 * to instantly learn about new peers as they are discovered.
179 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
184 static struct GNUNET_SCHEDULER_Handle *sched;
189 static const struct GNUNET_CONFIGURATION_Handle *cfg;
192 * Handle to the core API.
194 static struct GNUNET_CORE_Handle *handle;
197 * Handle to the transport API.
199 static struct GNUNET_TRANSPORT_Handle *transport;
202 * Identity of this peer.
204 static struct GNUNET_PeerIdentity my_identity;
207 * Linked list of all of our friends, all of our current neighbours
208 * and all peers for which we have HELLOs. So pretty much everyone.
210 static struct PeerList *peers;
213 * Handle for reporting statistics.
215 static struct GNUNET_STATISTICS_Handle *stats;
218 * Flag to disallow non-friend connections (pure F2F mode).
220 static int friends_only;
223 * Minimum number of friends to have in the
224 * connection set before we allow non-friends.
226 static unsigned int minimum_friend_count;
229 * Number of peers (friends and others) that we are currently connected to.
231 static unsigned int connection_count;
234 * Target number of connections.
236 static unsigned int target_connection_count;
239 * Number of friends that we are currently connected to.
241 static unsigned int friend_count;
244 * Should the topology daemon try to establish connections?
246 static int autoconnect;
249 * Head of doubly-linked list of active 'disconnect' requests that we have issued.
251 static struct DisconnectList *disconnect_head;
254 * Head of doubly-linked list of active 'disconnect' requests that we have issued.
256 static struct DisconnectList *disconnect_tail;
260 * Function called once our request to 'disconnect' a peer
263 * @param cls our 'struct DisconnectList'
267 disconnect_done (void *cls,
268 const struct GNUNET_SCHEDULER_TaskContext *tc)
270 struct DisconnectList *dl = cls;
272 GNUNET_STATISTICS_update (stats,
273 gettext_noop ("# peers blacklisted"),
276 GNUNET_CONTAINER_DLL_remove (disconnect_head,
284 * Force a disconnect from the specified peer.
287 force_disconnect (struct PeerList *pl)
289 const struct GNUNET_PeerIdentity *peer = &pl->id;
290 struct DisconnectList *dl;
294 GNUNET_TRANSPORT_blacklist_cancel (pl->wh);
297 pl->is_blocked = GNUNET_YES;
298 dl = GNUNET_malloc (sizeof (struct DisconnectList));
300 GNUNET_CONTAINER_DLL_insert (disconnect_head,
303 dl->rh = GNUNET_TRANSPORT_blacklist (sched, cfg,
305 GNUNET_TIME_UNIT_FOREVER_REL,
306 GNUNET_TIME_UNIT_FOREVER_REL,
314 * Function called once our request to 'whitelist' a peer
317 * @param cls our 'struct PeerList'
321 whitelist_done (void *cls,
322 const struct GNUNET_SCHEDULER_TaskContext *tc)
324 struct PeerList *pl = cls;
327 GNUNET_STATISTICS_update (stats,
328 gettext_noop ("# peers blacklisted"),
335 * Whitelist all peers that we blacklisted; we've passed
336 * the minimum number of friends.
342 struct DisconnectList *dl;
344 /* first, cancel all blacklisting requests */
345 while (NULL != (dl = disconnect_head))
347 GNUNET_CONTAINER_DLL_remove (disconnect_head,
350 GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
353 /* then, specifically whitelist all peers that we
354 know to have blacklisted */
360 pl->wh = GNUNET_TRANSPORT_blacklist (sched, cfg,
362 GNUNET_TIME_UNIT_ZERO,
363 GNUNET_TIME_UNIT_FOREVER_REL,
366 pl->is_blocked = GNUNET_NO;
374 * Function called by core when our attempt to connect succeeded.
377 connect_completed_callback (void *cls,
378 const struct GNUNET_SCHEDULER_TaskContext *tc)
380 struct PeerList *pos = cls;
382 pos->connect_req = NULL;
387 * Try to connect to the specified peer.
389 * @param pos peer to connect to
392 attempt_connect (struct PeerList *pos)
394 if (GNUNET_YES == pos->is_friend)
395 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
397 pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400 "Asking core to connect to `%s'\n",
401 GNUNET_i2s (&pos->id));
403 GNUNET_STATISTICS_update (stats,
404 gettext_noop ("# connect requests issued to core"),
407 pos->connect_req = GNUNET_CORE_peer_request_connect (sched, cfg,
408 GNUNET_TIME_UNIT_MINUTES,
410 &connect_completed_callback,
416 * Find a peer in our linked list.
417 * FIXME: should probably use a hash map instead.
420 find_peer (const struct GNUNET_PeerIdentity * peer)
422 struct PeerList *pos;
427 if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
436 * Check if an additional connection from the given peer is allowed.
439 is_connection_allowed (struct PeerList *peer)
441 if (0 == memcmp (&my_identity, &peer->id, sizeof (struct GNUNET_PeerIdentity)))
442 return GNUNET_SYSERR; /* disallow connections to self */
445 if (GNUNET_YES == friends_only)
448 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449 "Determined that `%s' is not allowed to connect (not a friend)\n",
450 GNUNET_i2s (&peer->id));
452 return GNUNET_SYSERR;
454 if (friend_count >= minimum_friend_count)
457 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458 "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
459 GNUNET_i2s (&peer->id));
461 return GNUNET_SYSERR;
466 * Create a new entry in the peer list.
468 * @param peer identity of the new entry
469 * @param hello hello message, can be NULL
470 * @param is_friend is the new entry for a friend?
471 * @return the new entry
473 static struct PeerList *
474 make_peer (const struct
475 GNUNET_PeerIdentity * peer,
476 const struct GNUNET_HELLO_Message *hello,
479 struct PeerList *ret;
481 ret = GNUNET_malloc (sizeof (struct PeerList));
483 ret->is_friend = is_friend;
486 ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
487 memcpy (ret->hello, hello,
488 GNUNET_HELLO_size (hello));
497 * Free all resources associated with the given peer.
499 * @param peer peer to free
502 free_peer (struct PeerList *peer)
504 struct PeerList *pos;
505 struct PeerList *prev;
514 GNUNET_assert (pos != NULL);
518 prev->next = pos->next;
519 if (pos->hello_req != NULL)
520 GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
522 GNUNET_TRANSPORT_blacklist_cancel (pos->wh);
523 if (pos->connect_req != NULL)
524 GNUNET_CORE_peer_request_connect_cancel (pos->connect_req);
525 if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
526 GNUNET_SCHEDULER_cancel (sched,
527 pos->hello_delay_task);
528 GNUNET_free_non_null (pos->hello);
529 if (pos->filter != NULL)
530 GNUNET_CONTAINER_bloomfilter_free (peer->filter);
536 * Setup bloom filter for the given peer entry.
538 * @param peer entry to initialize
541 setup_filter (struct PeerList *peer)
543 /* 2^{-5} chance of not sending a HELLO to a peer is
544 acceptably small (if the filter is 50% full);
545 64 bytes of memory are small compared to the rest
546 of the data structure and would only really become
547 "useless" once a HELLO has been passed on to ~100
548 other peers, which is likely more than enough in
549 any case; hence 64, 5 as bloomfilter parameters. */
550 peer->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
551 peer->filter_expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
552 /* never send a peer its own HELLO */
553 GNUNET_CONTAINER_bloomfilter_add (peer->filter, &peer->id.hashPubKey);
558 * Function to fill send buffer with HELLO.
560 * @param cls 'struct PeerList' of the target peer
561 * @param size number of bytes available in buf
562 * @param buf where the callee should write the message
563 * @return number of bytes written to buf
566 hello_advertising_ready (void *cls,
572 * Calculate when we would like to send the next HELLO to this
573 * peer and ask for it.
575 * @param cls for which peer to schedule the HELLO
576 * @param tc task context
579 schedule_next_hello (void *cls,
580 const struct GNUNET_SCHEDULER_TaskContext *tc)
582 struct PeerList *pl = cls;
583 struct PeerList *pos;
584 struct PeerList *next;
586 struct GNUNET_TIME_Relative next_adv;
587 struct GNUNET_TIME_Relative rst_time;
589 pl->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
590 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
591 return; /* we're out of here */
593 next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
594 /* find applicable HELLOs */
596 while (NULL != (pos = next))
599 if (pos->hello == NULL)
601 rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
602 if (0 == rst_time.value)
604 /* time to discard... */
605 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
610 if (rst_time.value < next_adv.value)
611 next_want = GNUNET_HELLO_size (pos->hello);
612 next_adv = GNUNET_TIME_relative_min (rst_time,
616 GNUNET_CONTAINER_bloomfilter_test (pos->filter,
621 next_adv = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
622 if (next_adv.value == 0)
625 pl->hello_req = GNUNET_CORE_notify_transmit_ready (handle, 0,
629 &hello_advertising_ready,
634 = GNUNET_SCHEDULER_add_delayed (sched,
636 &schedule_next_hello,
642 * Cancel existing requests for sending HELLOs to this peer
643 * and recalculate when we should send HELLOs to it based
644 * on our current state (something changed!).
647 reschedule_hellos (struct PeerList *peer)
649 if (peer->hello_req != NULL)
651 GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
652 peer->hello_req = NULL;
654 if (peer->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
656 GNUNET_SCHEDULER_cancel (sched,
657 peer->hello_delay_task);
658 peer->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
660 peer->hello_delay_task
661 = GNUNET_SCHEDULER_add_now (sched,
662 &schedule_next_hello,
668 * Method called whenever a peer connects.
671 * @param peer peer identity this notification is about
672 * @param latency reported latency of the connection with 'other'
673 * @param distance reported distance (DV) to 'other'
676 connect_notify (void *cls,
678 GNUNET_PeerIdentity * peer,
679 struct GNUNET_TIME_Relative latency,
682 struct PeerList *pos;
685 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686 "Core told us that we are connecting to `%s'\n",
690 GNUNET_STATISTICS_set (stats,
691 gettext_noop ("# peers connected"),
694 pos = find_peer (peer);
697 pos = make_peer (peer, NULL, GNUNET_NO);
698 if (GNUNET_OK != is_connection_allowed (pos))
700 GNUNET_assert (pos->is_friend == GNUNET_NO);
701 pos->is_connected = GNUNET_YES;
703 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
704 "Connection to `%s' is forbidden, forcing disconnect!\n",
707 force_disconnect (pos);
713 GNUNET_assert (GNUNET_NO == pos->is_connected);
714 pos->blacklisted_until.value = 0; /* remove blacklisting */
716 pos->is_connected = GNUNET_YES;
719 if ( (friend_count == minimum_friend_count - 1) &&
720 (GNUNET_YES != friends_only) )
723 GNUNET_STATISTICS_set (stats,
724 gettext_noop ("# friends connected"),
728 reschedule_hellos (pos);
733 * Disconnect from all non-friends (we're below quota).
738 struct PeerList *pos;
743 if ( (GNUNET_NO == pos->is_friend) &&
744 (GNUNET_YES == pos->is_connected) )
747 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748 "Connection to `%s' is not from a friend, forcing disconnect!\n",
749 GNUNET_i2s (&pos->id));
751 force_disconnect (pos);
759 * Try to add more peers to our connection set.
764 struct PeerList *pos;
769 if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
770 ( (GNUNET_YES == pos->is_friend) ||
771 (friend_count >= minimum_friend_count) ) &&
772 (GNUNET_YES != pos->is_connected) )
773 attempt_connect (pos);
780 * Method called whenever a peer disconnects.
783 * @param peer peer identity this notification is about
786 disconnect_notify (void *cls,
788 GNUNET_PeerIdentity * peer)
790 struct PeerList *pos;
793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
794 "Core told us that we disconnected from `%s'\n",
797 pos = find_peer (peer);
803 if (pos->is_connected != GNUNET_YES)
809 GNUNET_STATISTICS_set (stats,
810 gettext_noop ("# peers connected"),
816 GNUNET_STATISTICS_set (stats,
817 gettext_noop ("# friends connected"),
821 if ( (connection_count < target_connection_count) ||
822 (friend_count < minimum_friend_count) )
824 if (friend_count < minimum_friend_count)
826 /* disconnect from all non-friends */
828 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
829 "Not enough friendly connections, dropping all non-friend connections\n");
837 * Iterator called on each address.
839 * @param cls flag that we will set if we see any addresses
840 * @param tname name of the transport
841 * @param expiration when will the given address expire
842 * @param addr the address of the peer
843 * @param addrlen number of bytes in addr
844 * @return GNUNET_SYSERR always, to terminate iteration
847 address_iterator (void *cls,
849 struct GNUNET_TIME_Absolute expiration,
850 const void *addr, size_t addrlen)
854 return GNUNET_SYSERR;
859 * We've gotten a HELLO from another peer. Consider it for
863 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
866 struct GNUNET_PeerIdentity pid;
867 struct PeerList *peer;
868 struct PeerList *pos;
871 GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
872 if (0 == memcmp (&pid,
874 sizeof (struct GNUNET_PeerIdentity)))
875 return; /* that's me! */
876 have_address = GNUNET_NO;
877 GNUNET_HELLO_iterate_addresses (hello,
881 if (GNUNET_NO == have_address)
882 return; /* no point in advertising this one... */
883 peer = find_peer (&pid);
885 peer = make_peer (&pid, hello, GNUNET_NO);
886 // FIXME: check if 'hello' is any different from peer->hello?
888 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
889 "Found `%s' from peer `%s' for advertising\n",
893 size = GNUNET_HELLO_size (hello);
894 GNUNET_free_non_null (peer->hello);
895 peer->hello = GNUNET_malloc (size);
896 memcpy (peer->hello, hello, size);
897 if (peer->filter != NULL)
898 GNUNET_CONTAINER_bloomfilter_free (peer->filter);
900 /* since we have a new HELLO to pick from, re-schedule all
901 HELLO requests that are not bound by the HELLO send rate! */
907 if ( (pos->is_connected) &&
908 (GNUNET_TIME_absolute_get_remaining (pos->next_hello_allowed).value <= HELLO_ADVERTISEMENT_MIN_FREQUENCY.value) )
909 reschedule_hellos (pos);
917 * Peerinfo calls this function to let us know about a possible peer
918 * that we might want to connect to.
921 process_peer (void *cls,
922 const struct GNUNET_PeerIdentity *peer,
923 const struct GNUNET_HELLO_Message *hello,
926 struct PeerList *pos;
928 GNUNET_assert (peer != NULL);
929 if (0 == memcmp (&my_identity,
930 peer, sizeof (struct GNUNET_PeerIdentity)))
931 return; /* that's me! */
934 /* free existing HELLO, if any */
935 if (NULL != (pos = find_peer (peer)))
937 GNUNET_free_non_null (pos->hello);
939 if (pos->filter != NULL)
941 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
944 if ( (! pos->is_connected) &&
945 (! pos->is_friend) &&
946 (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
951 consider_for_advertising (hello);
952 pos = find_peer (peer);
954 pos = make_peer (peer, hello, GNUNET_NO);
955 GNUNET_assert (NULL != pos);
957 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
958 "Considering connecting to peer `%s'\n",
961 if (GNUNET_YES == pos->is_connected)
964 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
965 "Already connected to peer `%s'\n",
970 if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
973 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
974 "Already tried peer `%s' recently\n",
977 return; /* peer still blacklisted */
979 if ( (GNUNET_YES == pos->is_friend) ||
980 (GNUNET_YES != friends_only) ||
981 (friend_count >= minimum_friend_count) )
982 attempt_connect (pos);
987 * Discard peer entries for blacklisted peers
988 * where the blacklisting has expired.
991 discard_old_blacklist_entries (void *cls,
992 const struct GNUNET_SCHEDULER_TaskContext *tc)
994 struct PeerList *pos;
995 struct PeerList *next;
997 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1000 while (NULL != (pos = next))
1003 if ( (GNUNET_NO == pos->is_friend) &&
1004 (GNUNET_NO == pos->is_connected) &&
1005 (GNUNET_NO == pos->is_blocked) &&
1006 (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
1009 GNUNET_SCHEDULER_add_delayed (sched,
1010 BLACKLIST_AFTER_ATTEMPT,
1011 &discard_old_blacklist_entries,
1017 * Function called after GNUNET_CORE_connect has succeeded
1018 * (or failed for good).
1020 * @param cls closure
1021 * @param server handle to the server, NULL if we failed
1022 * @param my_id ID of this peer, NULL if we failed
1023 * @param publicKey public key of this peer, NULL if we failed
1026 core_init (void *cls,
1027 struct GNUNET_CORE_Handle * server,
1028 const struct GNUNET_PeerIdentity *
1031 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
1036 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1037 _("Failed to connect to core service, can not manage topology!\n"));
1038 GNUNET_SCHEDULER_shutdown (sched);
1042 my_identity = *my_id;
1044 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1046 GNUNET_i2s (my_id));
1048 GNUNET_SCHEDULER_add_delayed (sched,
1049 BLACKLIST_AFTER_ATTEMPT,
1050 &discard_old_blacklist_entries,
1052 peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1059 * Read the friends file.
1062 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
1067 struct GNUNET_PeerIdentity pid;
1069 struct GNUNET_CRYPTO_HashAsciiEncoded enc;
1070 unsigned int entries_found;
1071 struct PeerList *fl;
1074 GNUNET_CONFIGURATION_get_value_filename (cfg,
1079 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1080 _("Option `%s' in section `%s' not specified!\n"),
1085 if (GNUNET_OK != GNUNET_DISK_file_test (fn))
1086 GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
1087 | GNUNET_DISK_PERM_USER_WRITE);
1088 if (0 != STAT (fn, &frstat))
1090 if ((friends_only) || (minimum_friend_count > 0))
1092 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1093 _("Could not read friends list `%s'\n"), fn);
1098 if (frstat.st_size == 0)
1100 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1101 _("Friends file `%s' is empty.\n"),
1106 data = GNUNET_malloc_large (frstat.st_size);
1107 if (frstat.st_size !=
1108 GNUNET_DISK_fn_read (fn, data, frstat.st_size))
1110 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1111 _("Failed to read friends list from `%s'\n"), fn);
1118 while ((pos < frstat.st_size) && isspace (data[pos]))
1120 while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
1121 (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
1123 memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
1124 if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
1126 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1127 _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
1128 (unsigned long long) pos);
1130 while ((pos < frstat.st_size) && (!isspace (data[pos])))
1134 enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
1135 if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
1137 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1138 _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
1139 (unsigned long long) pos,
1144 if (0 != memcmp (&pid,
1146 sizeof (struct GNUNET_PeerIdentity)))
1149 fl = make_peer (&pid,
1152 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1153 _("Found friend `%s' in configuration\n"),
1154 GNUNET_i2s (&fl->id));
1158 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1159 _("Found myself `%s' in friend list (useless, ignored)\n"),
1163 pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
1164 while ((pos < frstat.st_size) && isspace (data[pos]))
1169 GNUNET_STATISTICS_update (stats,
1170 gettext_noop ("# friends in configuration"),
1173 if ( (minimum_friend_count > entries_found) &&
1174 (friends_only == GNUNET_NO) )
1176 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1177 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1179 if ( (minimum_friend_count > target_connection_count) &&
1180 (friends_only == GNUNET_NO) )
1182 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1183 _("More friendly connections required than target total number of connections.\n"));
1189 * This function is called whenever an encrypted HELLO message is
1192 * @param cls closure
1193 * @param other the other peer involved (sender or receiver, NULL
1194 * for loopback messages where we are both sender and receiver)
1195 * @param message the actual HELLO message
1196 * @param latency reported latency of the connection with 'other'
1197 * @param distance reported distance (DV) to 'other'
1198 * @return GNUNET_OK to keep the connection open,
1199 * GNUNET_SYSERR to close it (signal serious error)
1202 handle_encrypted_hello (void *cls,
1203 const struct GNUNET_PeerIdentity * other,
1204 const struct GNUNET_MessageHeader *
1206 struct GNUNET_TIME_Relative latency,
1210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1211 "Received encrypted `%s' from peer `%s'",
1213 GNUNET_i2s (other));
1215 GNUNET_STATISTICS_update (stats,
1216 gettext_noop ("# HELLO messages received"),
1219 if (transport != NULL)
1220 GNUNET_TRANSPORT_offer_hello (transport,
1227 * Function to fill send buffer with HELLO.
1229 * @param cls 'struct PeerList' of the target peer
1230 * @param size number of bytes available in buf
1231 * @param buf where the callee should write the message
1232 * @return number of bytes written to buf
1235 hello_advertising_ready (void *cls,
1239 struct PeerList *pl = cls;
1240 struct PeerList *pos;
1241 struct PeerList *next;
1245 pl->hello_req = NULL;
1247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1248 "Data solicited for `%s', considering sending `%s'",
1249 GNUNET_i2s (&pl->id),
1252 /* find applicable HELLOs */
1254 while (NULL != (pos = next))
1257 if (pos->hello == NULL)
1259 if (0 == GNUNET_TIME_absolute_get_remaining (pos->filter_expiration).value)
1261 /* time to discard... */
1262 GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1266 GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1267 &pl->id.hashPubKey))
1273 hs = GNUNET_HELLO_size (pos->hello);
1277 memcpy (buf, pos->hello, want);
1278 GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1279 &pl->id.hashPubKey);
1281 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1282 "Sending %u bytes of `%s's",
1283 (unsigned int) want,
1286 GNUNET_STATISTICS_update (stats,
1287 gettext_noop ("# HELLO messages gossipped"),
1292 pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1293 pl->hello_delay_task
1294 = GNUNET_SCHEDULER_add_now (sched,
1295 &schedule_next_hello,
1302 * Last task run during shutdown. Disconnects us from
1303 * the transport and core.
1306 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1308 struct DisconnectList *dl;
1310 if (NULL != peerinfo_notify)
1312 GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1313 peerinfo_notify = NULL;
1315 GNUNET_TRANSPORT_disconnect (transport);
1317 while (NULL != peers)
1321 GNUNET_CORE_disconnect (handle);
1324 while (NULL != (dl = disconnect_head))
1326 GNUNET_CONTAINER_DLL_remove (disconnect_head,
1329 GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
1334 GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1341 * Main function that will be run.
1343 * @param cls closure
1344 * @param s the scheduler to use
1345 * @param args remaining command-line arguments
1346 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1347 * @param c configuration
1351 struct GNUNET_SCHEDULER_Handle * s,
1353 const char *cfgfile,
1354 const struct GNUNET_CONFIGURATION_Handle * c)
1356 struct GNUNET_CORE_MessageHandler handlers[] =
1358 { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1361 unsigned long long opt;
1365 stats = GNUNET_STATISTICS_create (sched, "topology", cfg);
1366 autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1369 friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1373 GNUNET_CONFIGURATION_get_value_number (cfg,
1378 minimum_friend_count = (unsigned int) opt;
1380 GNUNET_CONFIGURATION_get_value_number (cfg,
1382 "TARGET-CONNECTION-COUNT",
1385 target_connection_count = (unsigned int) opt;
1387 if ( (friends_only == GNUNET_YES) ||
1388 (minimum_friend_count > 0) )
1389 read_friends_file (cfg);
1391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1392 "Topology would like %u connections with at least %u friends (%s)\n",
1393 target_connection_count,
1394 minimum_friend_count,
1395 autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1397 transport = GNUNET_TRANSPORT_connect (sched,
1403 handle = GNUNET_CORE_connect (sched,
1405 GNUNET_TIME_UNIT_FOREVER_REL,
1414 GNUNET_SCHEDULER_add_delayed (sched,
1415 GNUNET_TIME_UNIT_FOREVER_REL,
1416 &cleaning_task, NULL);
1417 if (NULL == transport)
1419 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1420 _("Failed to connect to `%s' service.\n"),
1422 GNUNET_SCHEDULER_shutdown (sched);
1427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1428 _("Failed to connect to `%s' service.\n"),
1430 GNUNET_SCHEDULER_shutdown (sched);
1437 * gnunet-daemon-topology command line options.
1439 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1440 GNUNET_GETOPT_OPTION_END
1445 * The main function for the topology daemon.
1447 * @param argc number of arguments from the command line
1448 * @param argv command line arguments
1449 * @return 0 ok, 1 on error
1452 main (int argc, char *const *argv)
1457 GNUNET_PROGRAM_run (argc,
1460 _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1462 &run, NULL)) ? 0 : 1;
1466 /* end of gnunet-daemon-topology.c */