-change topology to use ATS connectivity API instead of deprecated transport try_connect
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2015 Christian Grothoff (and other contributing authors)
4
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 3, or (at your
8      option) any later version.
9
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.
14
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20
21 /**
22  * @file topology/gnunet-daemon-topology.c
23  * @brief code for maintaining the overlay topology
24  * @author Christian Grothoff
25  *
26  * This daemon combines three functions:
27  * - suggesting to ATS which peers we might want to connect to
28  * - enforcing the F2F restrictions (by blacklisting)
29  * - gossping HELLOs
30  *
31  * All three require similar information (who are our friends
32  * impacts connectivity suggestions; connectivity suggestions
33  * should consider blacklisting; connectivity suggestions
34  * should consider available/known HELLOs; gossip requires
35  * connectivity data; connectivity suggestions require
36  * connectivity data), which is why they are combined in this
37  * program.
38  */
39 #include "platform.h"
40 #include "gnunet_util_lib.h"
41 #include "gnunet_friends_lib.h"
42 #include "gnunet_constants.h"
43 #include "gnunet_core_service.h"
44 #include "gnunet_protocols.h"
45 #include "gnunet_peerinfo_service.h"
46 #include "gnunet_statistics_service.h"
47 #include "gnunet_transport_service.h"
48 #include "gnunet_ats_service.h"
49
50
51 /**
52  * At what frequency do we sent HELLOs to a peer?
53  */
54 #define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 5)
55
56 /**
57  * After what time period do we expire the HELLO Bloom filter?
58  */
59 #define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
60
61
62 /**
63  * Record for neighbours, friends and blacklisted peers.
64  */
65 struct Peer
66 {
67   /**
68    * Which peer is this entry about?
69    */
70   struct GNUNET_PeerIdentity pid;
71
72   /**
73    * Our handle for the request to transmit HELLOs to this peer; NULL
74    * if no such request is pending.
75    */
76   struct GNUNET_CORE_TransmitHandle *hello_req;
77
78   /**
79    * Pointer to the HELLO message of this peer; can be NULL.
80    */
81   struct GNUNET_HELLO_Message *hello;
82
83   /**
84    * Bloom filter used to mark which peers already got the HELLO
85    * from this peer.
86    */
87   struct GNUNET_CONTAINER_BloomFilter *filter;
88
89   /**
90    * Next time we are allowed to transmit a HELLO to this peer?
91    */
92   struct GNUNET_TIME_Absolute next_hello_allowed;
93
94   /**
95    * When should we reset the bloom filter of this entry?
96    */
97   struct GNUNET_TIME_Absolute filter_expiration;
98
99   /**
100    * ID of task we use to wait for the time to send the next HELLO
101    * to this peer.
102    */
103   struct GNUNET_SCHEDULER_Task *hello_delay_task;
104
105   /**
106    * Handle for our connectivity suggestion for this peer.
107    */
108   struct GNUNET_ATS_ConnectivitySuggestHandle *sh;
109
110   /**
111    * How much would we like to connect to this peer?
112    */
113   uint32_t strength;
114
115   /**
116    * Is this peer listed here because he is a friend?
117    */
118   int is_friend;
119
120   /**
121    * Are we connected to this peer right now?
122    */
123   int is_connected;
124
125 };
126
127
128 /**
129  * Our peerinfo notification context.  We use notification
130  * to instantly learn about new peers as they are discovered.
131  */
132 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
133
134 /**
135  * Our configuration.
136  */
137 static const struct GNUNET_CONFIGURATION_Handle *cfg;
138
139 /**
140  * Handle to the CORE service.
141  */
142 static struct GNUNET_CORE_Handle *handle;
143
144 /**
145  * Handle to the TRANSPORT service.
146  */
147 static struct GNUNET_TRANSPORT_Handle *transport;
148
149 /**
150  * Handle to the ATS service.
151  */
152 static struct GNUNET_ATS_ConnectivityHandle *ats;
153
154 /**
155  * Identity of this peer.
156  */
157 static struct GNUNET_PeerIdentity my_identity;
158
159 /**
160  * All of our friends, all of our current neighbours and all peers for
161  * which we have HELLOs.  So pretty much everyone.  Maps peer identities
162  * to `struct Peer *` values.
163  */
164 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
165
166 /**
167  * Handle for reporting statistics.
168  */
169 static struct GNUNET_STATISTICS_Handle *stats;
170
171 /**
172  * Blacklist (NULL if we have none).
173  */
174 static struct GNUNET_TRANSPORT_Blacklist *blacklist;
175
176 /**
177  * Task scheduled to asynchronously reconsider adding/removing
178  * peer connectivity suggestions.
179  */
180 static struct GNUNET_SCHEDULER_Task *add_task;
181
182 /**
183  * Flag to disallow non-friend connections (pure F2F mode).
184  */
185 static int friends_only;
186
187 /**
188  * Minimum number of friends to have in the
189  * connection set before we allow non-friends.
190  */
191 static unsigned int minimum_friend_count;
192
193 /**
194  * Number of peers (friends and others) that we are currently connected to.
195  */
196 static unsigned int connection_count;
197
198 /**
199  * Target number of connections.
200  */
201 static unsigned int target_connection_count;
202
203 /**
204  * Number of friends that we are currently connected to.
205  */
206 static unsigned int friend_count;
207
208
209 /**
210  * Function that decides if a connection is acceptable or not.
211  * If we have a blacklist, only friends are allowed, so the check
212  * is rather simple.
213  *
214  * @param cls closure
215  * @param pid peer to approve or disapprove
216  * @return #GNUNET_OK if the connection is allowed
217  */
218 static int
219 blacklist_check (void *cls,
220                  const struct GNUNET_PeerIdentity *pid)
221 {
222   struct Peer *pos;
223
224   pos = GNUNET_CONTAINER_multipeermap_get (peers,
225                                            pid);
226   if ( (NULL != pos) &&
227        (GNUNET_YES == pos->is_friend))
228     return GNUNET_OK;
229   GNUNET_STATISTICS_update (stats,
230                             gettext_noop ("# peers blacklisted"),
231                             1,
232                             GNUNET_NO);
233   return GNUNET_SYSERR;
234 }
235
236
237 /**
238  * Whitelist all peers that we blacklisted; we've passed
239  * the minimum number of friends.
240  */
241 static void
242 whitelist_peers ()
243 {
244   if (NULL != blacklist)
245   {
246     GNUNET_TRANSPORT_blacklist_cancel (blacklist);
247     blacklist = NULL;
248   }
249 }
250
251
252 /**
253  * Free all resources associated with the given peer.
254  *
255  * @param cls closure (not used)
256  * @param pid identity of the peer
257  * @param value peer to free
258  * @return #GNUNET_YES (always: continue to iterate)
259  */
260 static int
261 free_peer (void *cls,
262            const struct GNUNET_PeerIdentity * pid,
263            void *value)
264 {
265   struct Peer *pos = value;
266
267   GNUNET_break (GNUNET_NO == pos->is_connected);
268   GNUNET_break (GNUNET_OK ==
269                 GNUNET_CONTAINER_multipeermap_remove (peers,
270                                                       pid,
271                                                       pos));
272   if (NULL != pos->hello_req)
273   {
274     GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
275     pos->hello_req = NULL;
276   }
277   if (NULL != pos->hello_delay_task)
278   {
279     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
280     pos->hello_delay_task = NULL;
281   }
282   if (NULL != pos->sh)
283   {
284     GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
285     pos->sh = NULL;
286   }
287   if (NULL != pos->hello)
288   {
289     GNUNET_free_non_null (pos->hello);
290     pos->hello = NULL;
291   }
292   if (NULL != pos->filter)
293   {
294     GNUNET_CONTAINER_bloomfilter_free (pos->filter);
295     pos->filter = NULL;
296   }
297   GNUNET_free (pos);
298   return GNUNET_YES;
299 }
300
301
302 /**
303  * Recalculate how much we want to be connected to the specified peer
304  * and let ATS know about the result.
305  *
306  * @param pos peer to consider connecting to
307  */
308 static void
309 attempt_connect (struct Peer *pos)
310 {
311   uint32_t strength;
312
313   if (0 ==
314       memcmp (&my_identity,
315               &pos->pid,
316               sizeof (struct GNUNET_PeerIdentity)))
317     return; /* This is myself, nothing to do. */
318   if (connection_count < target_connection_count)
319     strength = 1;
320   else
321     strength = 0;
322   if ( (friend_count < minimum_friend_count) ||
323        (GNUNET_YES == friends_only) )
324   {
325     if (pos->is_friend)
326       strength += 10; /* urgently needed */
327     else
328       strength = 0; /* disallowed */
329   }
330   if (pos->is_friend)
331     strength *= 2; /* friends always count more */
332   if (pos->is_connected)
333     strength *= 2; /* existing connections preferred */
334   if (strength == pos->strength)
335     return; /* nothing to do */
336   if (NULL != pos->sh)
337   {
338     GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
339     pos->sh = NULL;
340   }
341   pos->strength = strength;
342   if (0 != strength)
343   {
344     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
345                 "Asking to connect to `%s' with strength %u\n",
346                 GNUNET_i2s (&pos->pid),
347                 (unsigned int) strength);
348     GNUNET_STATISTICS_update (stats,
349                               gettext_noop ("# connect requests issued to ATS"),
350                               1,
351                               GNUNET_NO);
352     pos->sh = GNUNET_ATS_connectivity_suggest (ats,
353                                                &pos->pid,
354                                                strength);
355   }
356 }
357
358
359 /**
360  * Create a new entry in the peer list.
361  *
362  * @param peer identity of the new entry
363  * @param hello hello message, can be NULL
364  * @param is_friend is the new entry for a friend?
365  * @return the new entry
366  */
367 static struct Peer *
368 make_peer (const struct GNUNET_PeerIdentity *peer,
369            const struct GNUNET_HELLO_Message *hello,
370            int is_friend)
371 {
372   struct Peer *ret;
373
374   ret = GNUNET_new (struct Peer);
375   ret->pid = *peer;
376   ret->is_friend = is_friend;
377   if (NULL != hello)
378   {
379     ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
380     memcpy (ret->hello,
381             hello,
382             GNUNET_HELLO_size (hello));
383   }
384   GNUNET_break (GNUNET_OK ==
385                 GNUNET_CONTAINER_multipeermap_put (peers,
386                                                    peer,
387                                                    ret,
388                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
389   return ret;
390 }
391
392
393 /**
394  * Setup bloom filter for the given peer entry.
395  *
396  * @param peer entry to initialize
397  */
398 static void
399 setup_filter (struct Peer *peer)
400 {
401   struct GNUNET_HashCode hc;
402
403   /* 2^{-5} chance of not sending a HELLO to a peer is
404    * acceptably small (if the filter is 50% full);
405    * 64 bytes of memory are small compared to the rest
406    * of the data structure and would only really become
407    * "useless" once a HELLO has been passed on to ~100
408    * other peers, which is likely more than enough in
409    * any case; hence 64, 5 as bloomfilter parameters. */
410   peer->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, 64, 5);
411   peer->filter_expiration =
412       GNUNET_TIME_relative_to_absolute
413       (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
414   /* never send a peer its own HELLO */
415   GNUNET_CRYPTO_hash (&peer->pid,
416                       sizeof (struct GNUNET_PeerIdentity),
417                       &hc);
418   GNUNET_CONTAINER_bloomfilter_add (peer->filter, &hc);
419 }
420
421
422 /**
423  * Function to fill send buffer with HELLO.
424  *
425  * @param cls `struct Peer` of the target peer
426  * @param size number of bytes available in @a buf
427  * @param buf where the callee should write the message
428  * @return number of bytes written to @a buf
429  */
430 static size_t
431 hello_advertising_ready (void *cls,
432                          size_t size,
433                          void *buf);
434
435
436 /**
437  * Closure for #find_advertisable_hello().
438  */
439 struct FindAdvHelloContext
440 {
441
442   /**
443    * Peer we want to advertise to.
444    */
445   struct Peer *peer;
446
447   /**
448    * Where to store the result (peer selected for advertising).
449    */
450   struct Peer *result;
451
452   /**
453    * Maximum HELLO size we can use right now.
454    */
455   size_t max_size;
456
457   struct GNUNET_TIME_Relative next_adv;
458 };
459
460
461 /**
462  * Find a peer that would be reasonable for advertising.
463  *
464  * @param cls closure
465  * @param pid identity of a peer
466  * @param value 'struct Peer*' for the peer we are considering
467  * @return #GNUNET_YES (continue iteration)
468  */
469 static int
470 find_advertisable_hello (void *cls,
471                          const struct GNUNET_PeerIdentity *pid,
472                          void *value)
473 {
474   struct FindAdvHelloContext *fah = cls;
475   struct Peer *pos = value;
476   struct GNUNET_TIME_Relative rst_time;
477   struct GNUNET_HashCode hc;
478   size_t hs;
479
480   if (pos == fah->peer)
481     return GNUNET_YES;
482   if (pos->hello == NULL)
483     return GNUNET_YES;
484   rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
485   if (0 == rst_time.rel_value_us)
486   {
487     /* time to discard... */
488     GNUNET_CONTAINER_bloomfilter_free (pos->filter);
489     setup_filter (pos);
490   }
491   fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv);
492   hs = GNUNET_HELLO_size (pos->hello);
493   if (hs > fah->max_size)
494     return GNUNET_YES;
495   GNUNET_CRYPTO_hash (&fah->peer->pid, sizeof (struct GNUNET_PeerIdentity), &hc);
496   if (GNUNET_NO ==
497       GNUNET_CONTAINER_bloomfilter_test (pos->filter,
498                                          &hc))
499     fah->result = pos;
500   return GNUNET_YES;
501 }
502
503
504 /**
505  * Calculate when we would like to send the next HELLO to this
506  * peer and ask for it.
507  *
508  * @param cls for which peer to schedule the HELLO
509  * @param tc task context
510  */
511 static void
512 schedule_next_hello (void *cls,
513                      const struct GNUNET_SCHEDULER_TaskContext *tc)
514 {
515   struct Peer *pl = cls;
516   struct FindAdvHelloContext fah;
517   size_t next_want;
518   struct GNUNET_TIME_Relative delay;
519
520   pl->hello_delay_task = NULL;
521   GNUNET_assert (GNUNET_YES == pl->is_connected);
522   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
523     return;                     /* we're out of here */
524   if (pl->hello_req != NULL)
525     return;                     /* did not finish sending the previous one */
526   /* find applicable HELLOs */
527   fah.peer = pl;
528   fah.result = NULL;
529   fah.max_size = GNUNET_SERVER_MAX_MESSAGE_SIZE - 1;
530   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
531   GNUNET_CONTAINER_multipeermap_iterate (peers,
532                                          &find_advertisable_hello,
533                                          &fah);
534   pl->hello_delay_task =
535       GNUNET_SCHEDULER_add_delayed (fah.next_adv,
536                                     &schedule_next_hello,
537                                     pl);
538   if (fah.result == NULL)
539     return;
540   next_want = GNUNET_HELLO_size (fah.result->hello);
541   delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
542   if (0 == delay.rel_value_us)
543   {
544     /* now! */
545     pl->hello_req =
546         GNUNET_CORE_notify_transmit_ready (handle, GNUNET_YES,
547                                            GNUNET_CORE_PRIO_BEST_EFFORT,
548                                            GNUNET_CONSTANTS_SERVICE_TIMEOUT,
549                                            &pl->pid, next_want,
550                                            &hello_advertising_ready, pl);
551   }
552 }
553
554
555 /**
556  * Cancel existing requests for sending HELLOs to this peer
557  * and recalculate when we should send HELLOs to it based
558  * on our current state (something changed!).
559  *
560  * @param cls closure `struct Peer` to skip, or NULL
561  * @param pid identity of a peer
562  * @param value `struct Peer *` for the peer
563  * @return #GNUNET_YES (always)
564  */
565 static int
566 reschedule_hellos (void *cls,
567                    const struct GNUNET_PeerIdentity *pid,
568                    void *value)
569 {
570   struct Peer *peer = value;
571   struct Peer *skip = cls;
572
573   if (skip == peer)
574     return GNUNET_YES;
575   if (!peer->is_connected)
576     return GNUNET_YES;
577   if (peer->hello_req != NULL)
578   {
579     GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
580     peer->hello_req = NULL;
581   }
582   if (peer->hello_delay_task != NULL)
583   {
584     GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
585     peer->hello_delay_task = NULL;
586   }
587   peer->hello_delay_task =
588       GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer);
589   return GNUNET_YES;
590 }
591
592
593 /**
594  * Method called whenever a peer connects.
595  *
596  * @param cls closure
597  * @param peer peer identity this notification is about
598  */
599 static void
600 connect_notify (void *cls,
601                 const struct GNUNET_PeerIdentity *peer)
602 {
603   struct Peer *pos;
604
605   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
606               "Core told us that we are connecting to `%s'\n",
607               GNUNET_i2s (peer));
608   if (0 == memcmp (&my_identity,
609                    peer,
610                    sizeof (struct GNUNET_PeerIdentity)))
611     return;
612
613   connection_count++;
614   GNUNET_STATISTICS_set (stats,
615                          gettext_noop ("# peers connected"),
616                          connection_count,
617                          GNUNET_NO);
618   pos = GNUNET_CONTAINER_multipeermap_get (peers,
619                                            peer);
620   if (NULL == pos)
621   {
622     pos = make_peer (peer,
623                      NULL,
624                      GNUNET_NO);
625   }
626   else
627   {
628     GNUNET_assert (GNUNET_NO == pos->is_connected);
629   }
630   pos->is_connected = GNUNET_YES;
631   if (pos->is_friend)
632   {
633     friend_count++;
634     if ( (friend_count == minimum_friend_count) &&
635          (GNUNET_YES != friends_only) )
636       whitelist_peers ();
637     GNUNET_STATISTICS_set (stats,
638                            gettext_noop ("# friends connected"),
639                            friend_count,
640                            GNUNET_NO);
641   }
642   reschedule_hellos (NULL,
643                      peer,
644                      pos);
645 }
646
647
648 /**
649  * Try to add more peers to our connection set.
650  *
651  * @param cls closure, not used
652  * @param pid identity of a peer
653  * @param value `struct Peer *` for the peer
654  * @return #GNUNET_YES (continue to iterate)
655  */
656 static int
657 try_add_peers (void *cls,
658                const struct GNUNET_PeerIdentity *pid,
659                void *value)
660 {
661   struct Peer *pos = value;
662
663   attempt_connect (pos);
664   return GNUNET_YES;
665 }
666
667
668 /**
669  * Add peers and schedule connection attempt
670  *
671  * @param cls unused, NULL
672  * @param tc scheduler context
673  */
674 static void
675 add_peer_task (void *cls,
676                const struct GNUNET_SCHEDULER_TaskContext *tc)
677 {
678   add_task = NULL;
679
680   GNUNET_CONTAINER_multipeermap_iterate (peers,
681                                          &try_add_peers,
682                                          NULL);
683 }
684
685
686 /**
687  * Method called whenever a peer disconnects.
688  *
689  * @param cls closure
690  * @param peer peer identity this notification is about
691  */
692 static void
693 disconnect_notify (void *cls,
694                    const struct GNUNET_PeerIdentity *peer)
695 {
696   struct Peer *pos;
697
698   if (0 == memcmp (&my_identity,
699                    peer,
700                    sizeof (struct GNUNET_PeerIdentity)))
701     return;
702   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
703               "Core told us that we disconnected from `%s'\n",
704               GNUNET_i2s (peer));
705   pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
706   if (NULL == pos)
707   {
708     GNUNET_break (0);
709     return;
710   }
711   if (GNUNET_YES != pos->is_connected)
712   {
713     GNUNET_break (0);
714     return;
715   }
716   pos->is_connected = GNUNET_NO;
717   connection_count--;
718   if (NULL != pos->hello_req)
719   {
720     GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
721     pos->hello_req = NULL;
722   }
723   if (NULL != pos->hello_delay_task)
724   {
725     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
726     pos->hello_delay_task = NULL;
727   }
728   GNUNET_STATISTICS_set (stats,
729                          gettext_noop ("# peers connected"),
730                          connection_count,
731                          GNUNET_NO);
732   if (pos->is_friend)
733   {
734     friend_count--;
735     GNUNET_STATISTICS_set (stats,
736                            gettext_noop ("# friends connected"),
737                            friend_count,
738                            GNUNET_NO);
739   }
740   if ( ( (connection_count < target_connection_count) ||
741          (friend_count < minimum_friend_count)) &&
742        (NULL == add_task) )
743     add_task = GNUNET_SCHEDULER_add_now (&add_peer_task,
744                                          NULL);
745   if ( (friend_count < minimum_friend_count) &&
746        (NULL == blacklist))
747     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
748                                             &blacklist_check,
749                                             NULL);
750 }
751
752
753 /**
754  * Iterator called on each address.
755  *
756  * @param cls flag that we will set if we see any addresses
757  * @param address the address of the peer
758  * @param expiration when will the given address expire
759  * @return #GNUNET_SYSERR always, to terminate iteration
760  */
761 static int
762 address_iterator (void *cls,
763                   const struct GNUNET_HELLO_Address *address,
764                   struct GNUNET_TIME_Absolute expiration)
765 {
766   int *flag = cls;
767
768   *flag = GNUNET_YES;
769   return GNUNET_SYSERR;
770 }
771
772
773 /**
774  * We've gotten a HELLO from another peer.  Consider it for
775  * advertising.
776  *
777  * @param hello the HELLO we got
778  */
779 static void
780 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
781 {
782   int have_address;
783   struct GNUNET_PeerIdentity pid;
784   struct GNUNET_TIME_Absolute dt;
785   struct GNUNET_HELLO_Message *nh;
786   struct Peer *peer;
787   uint16_t size;
788
789   if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
790   {
791     GNUNET_break (0);
792     return;
793   }
794   if (0 == memcmp (&pid,
795                    &my_identity,
796                    sizeof (struct GNUNET_PeerIdentity)))
797     return;                     /* that's me! */
798   have_address = GNUNET_NO;
799   GNUNET_HELLO_iterate_addresses (hello,
800                                   GNUNET_NO,
801                                   &address_iterator,
802                                   &have_address);
803   if (GNUNET_NO == have_address)
804     return;                     /* no point in advertising this one... */
805   peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
806   if (NULL == peer)
807   {
808     peer = make_peer (&pid,
809                       hello,
810                       GNUNET_NO);
811   }
812   else if (NULL != peer->hello)
813   {
814     dt = GNUNET_HELLO_equals (peer->hello,
815                               hello,
816                               GNUNET_TIME_absolute_get ());
817     if (dt.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
818       return;                   /* nothing new here */
819   }
820   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
821               "Found HELLO from peer `%s' for advertising\n",
822               GNUNET_i2s (&pid));
823   if (NULL != peer->hello)
824   {
825     nh = GNUNET_HELLO_merge (peer->hello,
826                              hello);
827     GNUNET_free (peer->hello);
828     peer->hello = nh;
829   }
830   else
831   {
832     size = GNUNET_HELLO_size (hello);
833     peer->hello = GNUNET_malloc (size);
834     memcpy (peer->hello,
835             hello,
836             size);
837   }
838   if (NULL != peer->filter)
839   {
840     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
841     peer->filter = NULL;
842   }
843   setup_filter (peer);
844   /* since we have a new HELLO to pick from, re-schedule all
845    * HELLO requests that are not bound by the HELLO send rate! */
846   GNUNET_CONTAINER_multipeermap_iterate (peers,
847                                          &reschedule_hellos,
848                                          peer);
849 }
850
851
852 /**
853  * PEERINFO calls this function to let us know about a possible peer
854  * that we might want to connect to.
855  *
856  * @param cls closure (not used)
857  * @param peer potential peer to connect to
858  * @param hello HELLO for this peer (or NULL)
859  * @param err_msg NULL if successful, otherwise contains error message
860  */
861 static void
862 process_peer (void *cls,
863               const struct GNUNET_PeerIdentity *peer,
864               const struct GNUNET_HELLO_Message *hello,
865               const char *err_msg)
866 {
867   struct Peer *pos;
868
869   if (NULL != err_msg)
870   {
871     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
872                 _("Error in communication with PEERINFO service: %s\n"),
873                 err_msg);
874     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
875     peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
876                                               GNUNET_NO,
877                                               &process_peer,
878                                               NULL);
879     return;
880   }
881   GNUNET_assert (NULL != peer);
882   if (0 == memcmp (&my_identity,
883                    peer,
884                    sizeof (struct GNUNET_PeerIdentity)))
885     return;                     /* that's me! */
886   if (NULL == hello)
887   {
888     /* free existing HELLO, if any */
889     pos = GNUNET_CONTAINER_multipeermap_get (peers,
890                                              peer);
891     if (NULL != pos)
892     {
893       GNUNET_free_non_null (pos->hello);
894       pos->hello = NULL;
895       if (NULL != pos->filter)
896       {
897         GNUNET_CONTAINER_bloomfilter_free (pos->filter);
898         pos->filter = NULL;
899       }
900       if ( (GNUNET_NO == pos->is_connected) &&
901            (GNUNET_NO == pos->is_friend) )
902         free_peer (NULL,
903                    &pos->pid,
904                    pos);
905     }
906     return;
907   }
908   consider_for_advertising (hello);
909   pos = GNUNET_CONTAINER_multipeermap_get (peers,
910                                            peer);
911   if (NULL == pos)
912     pos = make_peer (peer,
913                      hello,
914                      GNUNET_NO);
915   attempt_connect (pos);
916 }
917
918
919 /**
920  * Function called after #GNUNET_CORE_connect has succeeded
921  * (or failed for good).
922  *
923  * @param cls closure
924  * @param my_id ID of this peer, NULL if we failed
925  */
926 static void
927 core_init (void *cls,
928            const struct GNUNET_PeerIdentity *my_id)
929 {
930   if (NULL == my_id)
931   {
932     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
933                 _("Failed to connect to core service, can not manage topology!\n"));
934     GNUNET_SCHEDULER_shutdown ();
935     return;
936   }
937   my_identity = *my_id;
938   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939               "I am peer `%s'\n",
940               GNUNET_i2s (my_id));
941   peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
942                                             GNUNET_NO,
943                                             &process_peer,
944                                             NULL);
945 }
946
947
948 /**
949  * Process friend found in FRIENDS file.
950  *
951  * @param cls pointer to an `unsigned int` to be incremented per friend found
952  * @param pid identity of the friend
953  */
954 static void
955 handle_friend (void *cls,
956                const struct GNUNET_PeerIdentity *pid)
957 {
958   unsigned int *entries_found = cls;
959   struct Peer *fl;
960
961   if (0 == memcmp (pid,
962                    &my_identity,
963                    sizeof (struct GNUNET_PeerIdentity)))
964   {
965     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
966                 _("Found myself `%s' in friend list (useless, ignored)\n"),
967                 GNUNET_i2s (pid));
968     return;
969   }
970   (*entries_found)++;
971   fl = make_peer (pid, NULL, GNUNET_YES);
972   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
973               _("Found friend `%s' in configuration\n"),
974               GNUNET_i2s (&fl->pid));
975 }
976
977
978 /**
979  * Read the friends file.
980  */
981 static void
982 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
983 {
984   unsigned int entries_found;
985
986   entries_found = 0;
987   if (GNUNET_OK !=
988       GNUNET_FRIENDS_parse (cfg,
989                             &handle_friend,
990                             &entries_found))
991   {
992     if ( (GNUNET_YES == friends_only) ||
993          (minimum_friend_count > 0))
994       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
995                   _("Encountered errors parsing friends list!\n"));
996   }
997   GNUNET_STATISTICS_update (stats,
998                             gettext_noop ("# friends in configuration"),
999                             entries_found,
1000                             GNUNET_NO);
1001   if ( (minimum_friend_count > entries_found) &&
1002        (GNUNET_NO == friends_only) )
1003   {
1004     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1005                 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1006   }
1007   if ( (minimum_friend_count > target_connection_count) &&
1008        (GNUNET_NO == friends_only))
1009   {
1010     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1011                 _("More friendly connections required than target total number of connections.\n"));
1012   }
1013 }
1014
1015
1016 /**
1017  * This function is called whenever an encrypted HELLO message is
1018  * received.
1019  *
1020  * @param cls closure
1021  * @param other the other peer involved (sender or receiver, NULL
1022  *        for loopback messages where we are both sender and receiver)
1023  * @param message the actual HELLO message
1024  * @return #GNUNET_OK to keep the connection open,
1025  *         #GNUNET_SYSERR to close it (signal serious error)
1026  */
1027 static int
1028 handle_encrypted_hello (void *cls,
1029                         const struct GNUNET_PeerIdentity *other,
1030                         const struct GNUNET_MessageHeader *message)
1031 {
1032   struct Peer *peer;
1033   struct GNUNET_PeerIdentity pid;
1034
1035   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1036               "Received encrypted HELLO from peer `%s'",
1037               GNUNET_i2s (other));
1038   if (GNUNET_OK !=
1039       GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) message, &pid))
1040   {
1041     GNUNET_break_op (0);
1042     return GNUNET_SYSERR;
1043   }
1044   GNUNET_STATISTICS_update (stats,
1045                             gettext_noop ("# HELLO messages received"),
1046                             1,
1047                             GNUNET_NO);
1048   peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
1049   if (NULL == peer)
1050   {
1051     if ( (GNUNET_YES == friends_only) ||
1052          (friend_count < minimum_friend_count) )
1053       return GNUNET_OK;
1054   }
1055   else
1056   {
1057     if ( (GNUNET_YES != peer->is_friend) &&
1058          (GNUNET_YES == friends_only) )
1059       return GNUNET_OK;
1060     if ((GNUNET_YES != peer->is_friend) &&
1061         (friend_count < minimum_friend_count))
1062       return GNUNET_OK;
1063   }
1064   if (NULL != transport)
1065     GNUNET_TRANSPORT_offer_hello (transport,
1066                                   message,
1067                                   NULL,
1068                                   NULL);
1069   return GNUNET_OK;
1070 }
1071
1072
1073 /**
1074  * Function to fill send buffer with HELLO.
1075  *
1076  * @param cls `struct Peer` of the target peer
1077  * @param size number of bytes available in buf
1078  * @param buf where the callee should write the message
1079  * @return number of bytes written to buf
1080  */
1081 static size_t
1082 hello_advertising_ready (void *cls,
1083                          size_t size,
1084                          void *buf)
1085 {
1086   struct Peer *pl = cls;
1087   struct FindAdvHelloContext fah;
1088   size_t want;
1089   struct GNUNET_HashCode hc;
1090
1091   pl->hello_req = NULL;
1092   GNUNET_assert (GNUNET_YES == pl->is_connected);
1093   /* find applicable HELLOs */
1094   fah.peer = pl;
1095   fah.result = NULL;
1096   fah.max_size = size;
1097   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
1098   GNUNET_CONTAINER_multipeermap_iterate (peers,
1099                                          &find_advertisable_hello,
1100                                          &fah);
1101   want = 0;
1102   if (NULL != fah.result)
1103   {
1104     want = GNUNET_HELLO_size (fah.result->hello);
1105     GNUNET_assert (want <= size);
1106     memcpy (buf,
1107             fah.result->hello,
1108             want);
1109     GNUNET_CRYPTO_hash (&pl->pid,
1110                         sizeof (struct GNUNET_PeerIdentity),
1111                         &hc);
1112     GNUNET_CONTAINER_bloomfilter_add (fah.result->filter,
1113                                       &hc);
1114     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1115                 "Sending HELLO with %u bytes",
1116                 (unsigned int) want);
1117     GNUNET_STATISTICS_update (stats,
1118                               gettext_noop ("# HELLO messages gossipped"), 1,
1119                               GNUNET_NO);
1120   }
1121
1122   if (pl->hello_delay_task != NULL)
1123     GNUNET_SCHEDULER_cancel (pl->hello_delay_task);
1124   pl->next_hello_allowed =
1125       GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1126   pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl);
1127   return want;
1128 }
1129
1130
1131 /**
1132  * Last task run during shutdown.  Disconnects us from
1133  * the transport and core.
1134  *
1135  * @param cls unused, NULL
1136  * @param tc scheduler context
1137  */
1138 static void
1139 cleaning_task (void *cls,
1140                const struct GNUNET_SCHEDULER_TaskContext *tc)
1141 {
1142   if (NULL != peerinfo_notify)
1143   {
1144     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1145     peerinfo_notify = NULL;
1146   }
1147   if (NULL != transport)
1148   {
1149     GNUNET_TRANSPORT_disconnect (transport);
1150     transport = NULL;
1151   }
1152   if (NULL != handle)
1153   {
1154     GNUNET_CORE_disconnect (handle);
1155     handle = NULL;
1156   }
1157   whitelist_peers ();
1158   if (NULL != add_task)
1159   {
1160     GNUNET_SCHEDULER_cancel (add_task);
1161     add_task = NULL;
1162   }
1163   GNUNET_CONTAINER_multipeermap_iterate (peers,
1164                                          &free_peer,
1165                                          NULL);
1166   GNUNET_CONTAINER_multipeermap_destroy (peers);
1167   peers = NULL;
1168   if (NULL != ats)
1169   {
1170     GNUNET_ATS_connectivity_done (ats);
1171     ats = NULL;
1172   }
1173   if (NULL != stats)
1174   {
1175     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1176     stats = NULL;
1177   }
1178 }
1179
1180
1181 /**
1182  * Main function that will be run.
1183  *
1184  * @param cls closure
1185  * @param args remaining command-line arguments
1186  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1187  * @param c configuration
1188  */
1189 static void
1190 run (void *cls,
1191      char *const *args,
1192      const char *cfgfile,
1193      const struct GNUNET_CONFIGURATION_Handle *c)
1194 {
1195   static struct GNUNET_CORE_MessageHandler handlers[] = {
1196     {&handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1197     {NULL, 0, 0}
1198   };
1199   unsigned long long opt;
1200
1201   cfg = c;
1202   stats = GNUNET_STATISTICS_create ("topology", cfg);
1203   friends_only =
1204       GNUNET_CONFIGURATION_get_value_yesno (cfg,
1205                                             "TOPOLOGY",
1206                                             "FRIENDS-ONLY");
1207   if (GNUNET_OK !=
1208       GNUNET_CONFIGURATION_get_value_number (cfg,
1209                                              "TOPOLOGY",
1210                                              "MINIMUM-FRIENDS",
1211                                              &opt))
1212     opt = 0;
1213   minimum_friend_count = (unsigned int) opt;
1214   if (GNUNET_OK !=
1215       GNUNET_CONFIGURATION_get_value_number (cfg,
1216                                              "TOPOLOGY",
1217                                              "TARGET-CONNECTION-COUNT",
1218                                              &opt))
1219     opt = 16;
1220   target_connection_count = (unsigned int) opt;
1221   peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2,
1222                                                 GNUNET_NO);
1223   read_friends_file (cfg);
1224   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1225               "Topology would like %u connections with at least %u friends\n",
1226               target_connection_count,
1227               minimum_friend_count);
1228   if ( (friend_count < minimum_friend_count) &&
1229        (NULL == blacklist))
1230     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
1231                                             &blacklist_check,
1232                                             NULL);
1233   ats = GNUNET_ATS_connectivity_init (cfg);
1234   transport = GNUNET_TRANSPORT_connect (cfg,
1235                                         NULL,
1236                                         NULL,
1237                                         NULL,
1238                                         NULL,
1239                                         NULL);
1240   handle =
1241       GNUNET_CORE_connect (cfg, NULL,
1242                            &core_init,
1243                            &connect_notify,
1244                            &disconnect_notify,
1245                            NULL, GNUNET_NO,
1246                            NULL, GNUNET_NO,
1247                            handlers);
1248   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1249                                 &cleaning_task,
1250                                 NULL);
1251   if (NULL == transport)
1252   {
1253     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1254                 _("Failed to connect to `%s' service.\n"),
1255                 "transport");
1256     GNUNET_SCHEDULER_shutdown ();
1257     return;
1258   }
1259   if (NULL == handle)
1260   {
1261     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1262                 _("Failed to connect to `%s' service.\n"),
1263                 "core");
1264     GNUNET_SCHEDULER_shutdown ();
1265     return;
1266   }
1267 }
1268
1269
1270 /**
1271  * The main function for the topology daemon.
1272  *
1273  * @param argc number of arguments from the command line
1274  * @param argv command line arguments
1275  * @return 0 ok, 1 on error
1276  */
1277 int
1278 main (int argc, char *const *argv)
1279 {
1280   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1281     GNUNET_GETOPT_OPTION_END
1282   };
1283   int ret;
1284
1285   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1286     return 2;
1287
1288   ret =
1289       (GNUNET_OK ==
1290        GNUNET_PROGRAM_run (argc, argv,
1291                            "gnunet-daemon-topology",
1292                            _("GNUnet topology control"),
1293                            options, &run, NULL)) ? 0 : 1;
1294   GNUNET_free ((void*) argv);
1295   return ret;
1296 }
1297
1298
1299 #if defined(LINUX) && defined(__GLIBC__)
1300 #include <malloc.h>
1301
1302 /**
1303  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1304  */
1305 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1306 {
1307   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1308   mallopt (M_TOP_PAD, 1 * 1024);
1309   malloc_trim (0);
1310 }
1311 #endif
1312
1313 /* end of gnunet-daemon-topology.c */