implementing new scheduler shutdown semantics
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2015 GNUnet e.V.
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  */
510 static void
511 schedule_next_hello (void *cls)
512 {
513   struct Peer *pl = cls;
514   struct FindAdvHelloContext fah;
515   size_t next_want;
516   struct GNUNET_TIME_Relative delay;
517
518   pl->hello_delay_task = NULL;
519   GNUNET_assert (GNUNET_YES == pl->is_connected);
520   if (pl->hello_req != NULL)
521     return;                     /* did not finish sending the previous one */
522   /* find applicable HELLOs */
523   fah.peer = pl;
524   fah.result = NULL;
525   fah.max_size = GNUNET_SERVER_MAX_MESSAGE_SIZE - 1;
526   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
527   GNUNET_CONTAINER_multipeermap_iterate (peers,
528                                          &find_advertisable_hello,
529                                          &fah);
530   pl->hello_delay_task =
531       GNUNET_SCHEDULER_add_delayed (fah.next_adv,
532                                     &schedule_next_hello,
533                                     pl);
534   if (NULL == fah.result)
535     return;
536   next_want = GNUNET_HELLO_size (fah.result->hello);
537   delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
538   if (0 == delay.rel_value_us)
539   {
540     /* now! */
541     pl->hello_req =
542         GNUNET_CORE_notify_transmit_ready (handle, GNUNET_YES,
543                                            GNUNET_CORE_PRIO_BEST_EFFORT,
544                                            GNUNET_CONSTANTS_SERVICE_TIMEOUT,
545                                            &pl->pid, next_want,
546                                            &hello_advertising_ready, pl);
547   }
548 }
549
550
551 /**
552  * Cancel existing requests for sending HELLOs to this peer
553  * and recalculate when we should send HELLOs to it based
554  * on our current state (something changed!).
555  *
556  * @param cls closure `struct Peer` to skip, or NULL
557  * @param pid identity of a peer
558  * @param value `struct Peer *` for the peer
559  * @return #GNUNET_YES (always)
560  */
561 static int
562 reschedule_hellos (void *cls,
563                    const struct GNUNET_PeerIdentity *pid,
564                    void *value)
565 {
566   struct Peer *peer = value;
567   struct Peer *skip = cls;
568
569   if (skip == peer)
570     return GNUNET_YES;
571   if (!peer->is_connected)
572     return GNUNET_YES;
573   if (peer->hello_req != NULL)
574   {
575     GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
576     peer->hello_req = NULL;
577   }
578   if (peer->hello_delay_task != NULL)
579   {
580     GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
581     peer->hello_delay_task = NULL;
582   }
583   peer->hello_delay_task =
584       GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer);
585   return GNUNET_YES;
586 }
587
588
589 /**
590  * Method called whenever a peer connects.
591  *
592  * @param cls closure
593  * @param peer peer identity this notification is about
594  */
595 static void
596 connect_notify (void *cls,
597                 const struct GNUNET_PeerIdentity *peer)
598 {
599   struct Peer *pos;
600
601   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
602               "Core told us that we are connecting to `%s'\n",
603               GNUNET_i2s (peer));
604   if (0 == memcmp (&my_identity,
605                    peer,
606                    sizeof (struct GNUNET_PeerIdentity)))
607     return;
608
609   connection_count++;
610   GNUNET_STATISTICS_set (stats,
611                          gettext_noop ("# peers connected"),
612                          connection_count,
613                          GNUNET_NO);
614   pos = GNUNET_CONTAINER_multipeermap_get (peers,
615                                            peer);
616   if (NULL == pos)
617   {
618     pos = make_peer (peer,
619                      NULL,
620                      GNUNET_NO);
621   }
622   else
623   {
624     GNUNET_assert (GNUNET_NO == pos->is_connected);
625   }
626   pos->is_connected = GNUNET_YES;
627   if (pos->is_friend)
628   {
629     friend_count++;
630     if ( (friend_count == minimum_friend_count) &&
631          (GNUNET_YES != friends_only) )
632       whitelist_peers ();
633     GNUNET_STATISTICS_set (stats,
634                            gettext_noop ("# friends connected"),
635                            friend_count,
636                            GNUNET_NO);
637   }
638   reschedule_hellos (NULL,
639                      peer,
640                      pos);
641 }
642
643
644 /**
645  * Try to add more peers to our connection set.
646  *
647  * @param cls closure, not used
648  * @param pid identity of a peer
649  * @param value `struct Peer *` for the peer
650  * @return #GNUNET_YES (continue to iterate)
651  */
652 static int
653 try_add_peers (void *cls,
654                const struct GNUNET_PeerIdentity *pid,
655                void *value)
656 {
657   struct Peer *pos = value;
658
659   attempt_connect (pos);
660   return GNUNET_YES;
661 }
662
663
664 /**
665  * Add peers and schedule connection attempt
666  *
667  * @param cls unused, NULL
668  */
669 static void
670 add_peer_task (void *cls)
671 {
672   add_task = NULL;
673
674   GNUNET_CONTAINER_multipeermap_iterate (peers,
675                                          &try_add_peers,
676                                          NULL);
677 }
678
679
680 /**
681  * Method called whenever a peer disconnects.
682  *
683  * @param cls closure
684  * @param peer peer identity this notification is about
685  */
686 static void
687 disconnect_notify (void *cls,
688                    const struct GNUNET_PeerIdentity *peer)
689 {
690   struct Peer *pos;
691
692   if (0 == memcmp (&my_identity,
693                    peer,
694                    sizeof (struct GNUNET_PeerIdentity)))
695     return;
696   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697               "Core told us that we disconnected from `%s'\n",
698               GNUNET_i2s (peer));
699   pos = GNUNET_CONTAINER_multipeermap_get (peers, peer);
700   if (NULL == pos)
701   {
702     GNUNET_break (0);
703     return;
704   }
705   if (GNUNET_YES != pos->is_connected)
706   {
707     GNUNET_break (0);
708     return;
709   }
710   pos->is_connected = GNUNET_NO;
711   connection_count--;
712   if (NULL != pos->hello_req)
713   {
714     GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
715     pos->hello_req = NULL;
716   }
717   if (NULL != pos->hello_delay_task)
718   {
719     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
720     pos->hello_delay_task = NULL;
721   }
722   GNUNET_STATISTICS_set (stats,
723                          gettext_noop ("# peers connected"),
724                          connection_count,
725                          GNUNET_NO);
726   if (pos->is_friend)
727   {
728     friend_count--;
729     GNUNET_STATISTICS_set (stats,
730                            gettext_noop ("# friends connected"),
731                            friend_count,
732                            GNUNET_NO);
733   }
734   if ( ( (connection_count < target_connection_count) ||
735          (friend_count < minimum_friend_count)) &&
736        (NULL == add_task) )
737     add_task = GNUNET_SCHEDULER_add_now (&add_peer_task,
738                                          NULL);
739   if ( (friend_count < minimum_friend_count) &&
740        (NULL == blacklist))
741     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
742                                             &blacklist_check,
743                                             NULL);
744 }
745
746
747 /**
748  * Iterator called on each address.
749  *
750  * @param cls flag that we will set if we see any addresses
751  * @param address the address of the peer
752  * @param expiration when will the given address expire
753  * @return #GNUNET_SYSERR always, to terminate iteration
754  */
755 static int
756 address_iterator (void *cls,
757                   const struct GNUNET_HELLO_Address *address,
758                   struct GNUNET_TIME_Absolute expiration)
759 {
760   int *flag = cls;
761
762   *flag = GNUNET_YES;
763   return GNUNET_SYSERR;
764 }
765
766
767 /**
768  * We've gotten a HELLO from another peer.  Consider it for
769  * advertising.
770  *
771  * @param hello the HELLO we got
772  */
773 static void
774 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
775 {
776   int have_address;
777   struct GNUNET_PeerIdentity pid;
778   struct GNUNET_TIME_Absolute dt;
779   struct GNUNET_HELLO_Message *nh;
780   struct Peer *peer;
781   uint16_t size;
782
783   if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
784   {
785     GNUNET_break (0);
786     return;
787   }
788   if (0 == memcmp (&pid,
789                    &my_identity,
790                    sizeof (struct GNUNET_PeerIdentity)))
791     return;                     /* that's me! */
792   have_address = GNUNET_NO;
793   GNUNET_HELLO_iterate_addresses (hello,
794                                   GNUNET_NO,
795                                   &address_iterator,
796                                   &have_address);
797   if (GNUNET_NO == have_address)
798     return;                     /* no point in advertising this one... */
799   peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
800   if (NULL == peer)
801   {
802     peer = make_peer (&pid,
803                       hello,
804                       GNUNET_NO);
805   }
806   else if (NULL != peer->hello)
807   {
808     dt = GNUNET_HELLO_equals (peer->hello,
809                               hello,
810                               GNUNET_TIME_absolute_get ());
811     if (dt.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
812       return;                   /* nothing new here */
813   }
814   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
815               "Found HELLO from peer `%s' for advertising\n",
816               GNUNET_i2s (&pid));
817   if (NULL != peer->hello)
818   {
819     nh = GNUNET_HELLO_merge (peer->hello,
820                              hello);
821     GNUNET_free (peer->hello);
822     peer->hello = nh;
823   }
824   else
825   {
826     size = GNUNET_HELLO_size (hello);
827     peer->hello = GNUNET_malloc (size);
828     memcpy (peer->hello,
829             hello,
830             size);
831   }
832   if (NULL != peer->filter)
833   {
834     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
835     peer->filter = NULL;
836   }
837   setup_filter (peer);
838   /* since we have a new HELLO to pick from, re-schedule all
839    * HELLO requests that are not bound by the HELLO send rate! */
840   GNUNET_CONTAINER_multipeermap_iterate (peers,
841                                          &reschedule_hellos,
842                                          peer);
843 }
844
845
846 /**
847  * PEERINFO calls this function to let us know about a possible peer
848  * that we might want to connect to.
849  *
850  * @param cls closure (not used)
851  * @param peer potential peer to connect to
852  * @param hello HELLO for this peer (or NULL)
853  * @param err_msg NULL if successful, otherwise contains error message
854  */
855 static void
856 process_peer (void *cls,
857               const struct GNUNET_PeerIdentity *peer,
858               const struct GNUNET_HELLO_Message *hello,
859               const char *err_msg)
860 {
861   struct Peer *pos;
862
863   if (NULL != err_msg)
864   {
865     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
866                 _("Error in communication with PEERINFO service: %s\n"),
867                 err_msg);
868     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
869     peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
870                                               GNUNET_NO,
871                                               &process_peer,
872                                               NULL);
873     return;
874   }
875   GNUNET_assert (NULL != peer);
876   if (0 == memcmp (&my_identity,
877                    peer,
878                    sizeof (struct GNUNET_PeerIdentity)))
879     return;                     /* that's me! */
880   if (NULL == hello)
881   {
882     /* free existing HELLO, if any */
883     pos = GNUNET_CONTAINER_multipeermap_get (peers,
884                                              peer);
885     if (NULL != pos)
886     {
887       GNUNET_free_non_null (pos->hello);
888       pos->hello = NULL;
889       if (NULL != pos->filter)
890       {
891         GNUNET_CONTAINER_bloomfilter_free (pos->filter);
892         pos->filter = NULL;
893       }
894       if ( (GNUNET_NO == pos->is_connected) &&
895            (GNUNET_NO == pos->is_friend) )
896         free_peer (NULL,
897                    &pos->pid,
898                    pos);
899     }
900     return;
901   }
902   consider_for_advertising (hello);
903   pos = GNUNET_CONTAINER_multipeermap_get (peers,
904                                            peer);
905   if (NULL == pos)
906     pos = make_peer (peer,
907                      hello,
908                      GNUNET_NO);
909   attempt_connect (pos);
910 }
911
912
913 /**
914  * Function called after #GNUNET_CORE_connect has succeeded
915  * (or failed for good).
916  *
917  * @param cls closure
918  * @param my_id ID of this peer, NULL if we failed
919  */
920 static void
921 core_init (void *cls,
922            const struct GNUNET_PeerIdentity *my_id)
923 {
924   if (NULL == my_id)
925   {
926     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
927                 _("Failed to connect to core service, can not manage topology!\n"));
928     GNUNET_SCHEDULER_shutdown ();
929     return;
930   }
931   my_identity = *my_id;
932   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
933               "I am peer `%s'\n",
934               GNUNET_i2s (my_id));
935   peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
936                                             GNUNET_NO,
937                                             &process_peer,
938                                             NULL);
939 }
940
941
942 /**
943  * Process friend found in FRIENDS file.
944  *
945  * @param cls pointer to an `unsigned int` to be incremented per friend found
946  * @param pid identity of the friend
947  */
948 static void
949 handle_friend (void *cls,
950                const struct GNUNET_PeerIdentity *pid)
951 {
952   unsigned int *entries_found = cls;
953   struct Peer *fl;
954
955   if (0 == memcmp (pid,
956                    &my_identity,
957                    sizeof (struct GNUNET_PeerIdentity)))
958   {
959     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
960                 _("Found myself `%s' in friend list (useless, ignored)\n"),
961                 GNUNET_i2s (pid));
962     return;
963   }
964   (*entries_found)++;
965   fl = make_peer (pid, NULL, GNUNET_YES);
966   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
967               _("Found friend `%s' in configuration\n"),
968               GNUNET_i2s (&fl->pid));
969 }
970
971
972 /**
973  * Read the friends file.
974  */
975 static void
976 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
977 {
978   unsigned int entries_found;
979
980   entries_found = 0;
981   if (GNUNET_OK !=
982       GNUNET_FRIENDS_parse (cfg,
983                             &handle_friend,
984                             &entries_found))
985   {
986     if ( (GNUNET_YES == friends_only) ||
987          (minimum_friend_count > 0))
988       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
989                   _("Encountered errors parsing friends list!\n"));
990   }
991   GNUNET_STATISTICS_update (stats,
992                             gettext_noop ("# friends in configuration"),
993                             entries_found,
994                             GNUNET_NO);
995   if ( (minimum_friend_count > entries_found) &&
996        (GNUNET_NO == friends_only) )
997   {
998     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
999                 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1000   }
1001   if ( (minimum_friend_count > target_connection_count) &&
1002        (GNUNET_NO == friends_only))
1003   {
1004     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1005                 _("More friendly connections required than target total number of connections.\n"));
1006   }
1007 }
1008
1009
1010 /**
1011  * This function is called whenever an encrypted HELLO message is
1012  * received.
1013  *
1014  * @param cls closure
1015  * @param other the other peer involved (sender or receiver, NULL
1016  *        for loopback messages where we are both sender and receiver)
1017  * @param message the actual HELLO message
1018  * @return #GNUNET_OK to keep the connection open,
1019  *         #GNUNET_SYSERR to close it (signal serious error)
1020  */
1021 static int
1022 handle_encrypted_hello (void *cls,
1023                         const struct GNUNET_PeerIdentity *other,
1024                         const struct GNUNET_MessageHeader *message)
1025 {
1026   struct Peer *peer;
1027   struct GNUNET_PeerIdentity pid;
1028
1029   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1030               "Received encrypted HELLO from peer `%s'",
1031               GNUNET_i2s (other));
1032   if (GNUNET_OK !=
1033       GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) message, &pid))
1034   {
1035     GNUNET_break_op (0);
1036     return GNUNET_SYSERR;
1037   }
1038   GNUNET_STATISTICS_update (stats,
1039                             gettext_noop ("# HELLO messages received"),
1040                             1,
1041                             GNUNET_NO);
1042   peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
1043   if (NULL == peer)
1044   {
1045     if ( (GNUNET_YES == friends_only) ||
1046          (friend_count < minimum_friend_count) )
1047       return GNUNET_OK;
1048   }
1049   else
1050   {
1051     if ( (GNUNET_YES != peer->is_friend) &&
1052          (GNUNET_YES == friends_only) )
1053       return GNUNET_OK;
1054     if ((GNUNET_YES != peer->is_friend) &&
1055         (friend_count < minimum_friend_count))
1056       return GNUNET_OK;
1057   }
1058   if (NULL != transport)
1059     GNUNET_TRANSPORT_offer_hello (transport,
1060                                   message,
1061                                   NULL,
1062                                   NULL);
1063   return GNUNET_OK;
1064 }
1065
1066
1067 /**
1068  * Function to fill send buffer with HELLO.
1069  *
1070  * @param cls `struct Peer` of the target peer
1071  * @param size number of bytes available in buf
1072  * @param buf where the callee should write the message
1073  * @return number of bytes written to buf
1074  */
1075 static size_t
1076 hello_advertising_ready (void *cls,
1077                          size_t size,
1078                          void *buf)
1079 {
1080   struct Peer *pl = cls;
1081   struct FindAdvHelloContext fah;
1082   size_t want;
1083   struct GNUNET_HashCode hc;
1084
1085   pl->hello_req = NULL;
1086   GNUNET_assert (GNUNET_YES == pl->is_connected);
1087   /* find applicable HELLOs */
1088   fah.peer = pl;
1089   fah.result = NULL;
1090   fah.max_size = size;
1091   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
1092   GNUNET_CONTAINER_multipeermap_iterate (peers,
1093                                          &find_advertisable_hello,
1094                                          &fah);
1095   want = 0;
1096   if (NULL != fah.result)
1097   {
1098     want = GNUNET_HELLO_size (fah.result->hello);
1099     GNUNET_assert (want <= size);
1100     memcpy (buf,
1101             fah.result->hello,
1102             want);
1103     GNUNET_CRYPTO_hash (&pl->pid,
1104                         sizeof (struct GNUNET_PeerIdentity),
1105                         &hc);
1106     GNUNET_CONTAINER_bloomfilter_add (fah.result->filter,
1107                                       &hc);
1108     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1109                 "Sending HELLO with %u bytes",
1110                 (unsigned int) want);
1111     GNUNET_STATISTICS_update (stats,
1112                               gettext_noop ("# HELLO messages gossipped"), 1,
1113                               GNUNET_NO);
1114   }
1115
1116   if (pl->hello_delay_task != NULL)
1117     GNUNET_SCHEDULER_cancel (pl->hello_delay_task);
1118   pl->next_hello_allowed =
1119       GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1120   pl->hello_delay_task = GNUNET_SCHEDULER_add_now (&schedule_next_hello, pl);
1121   return want;
1122 }
1123
1124
1125 /**
1126  * Last task run during shutdown.  Disconnects us from
1127  * the transport and core.
1128  *
1129  * @param cls unused, NULL
1130  */
1131 static void
1132 cleaning_task (void *cls)
1133 {
1134   if (NULL != peerinfo_notify)
1135   {
1136     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1137     peerinfo_notify = NULL;
1138   }
1139   if (NULL != transport)
1140   {
1141     GNUNET_TRANSPORT_disconnect (transport);
1142     transport = NULL;
1143   }
1144   if (NULL != handle)
1145   {
1146     GNUNET_CORE_disconnect (handle);
1147     handle = NULL;
1148   }
1149   whitelist_peers ();
1150   if (NULL != add_task)
1151   {
1152     GNUNET_SCHEDULER_cancel (add_task);
1153     add_task = NULL;
1154   }
1155   GNUNET_CONTAINER_multipeermap_iterate (peers,
1156                                          &free_peer,
1157                                          NULL);
1158   GNUNET_CONTAINER_multipeermap_destroy (peers);
1159   peers = NULL;
1160   if (NULL != ats)
1161   {
1162     GNUNET_ATS_connectivity_done (ats);
1163     ats = NULL;
1164   }
1165   if (NULL != stats)
1166   {
1167     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1168     stats = NULL;
1169   }
1170 }
1171
1172
1173 /**
1174  * Main function that will be run.
1175  *
1176  * @param cls closure
1177  * @param args remaining command-line arguments
1178  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1179  * @param c configuration
1180  */
1181 static void
1182 run (void *cls,
1183      char *const *args,
1184      const char *cfgfile,
1185      const struct GNUNET_CONFIGURATION_Handle *c)
1186 {
1187   static struct GNUNET_CORE_MessageHandler handlers[] = {
1188     {&handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1189     {NULL, 0, 0}
1190   };
1191   unsigned long long opt;
1192
1193   cfg = c;
1194   stats = GNUNET_STATISTICS_create ("topology", cfg);
1195   friends_only =
1196       GNUNET_CONFIGURATION_get_value_yesno (cfg,
1197                                             "TOPOLOGY",
1198                                             "FRIENDS-ONLY");
1199   if (GNUNET_OK !=
1200       GNUNET_CONFIGURATION_get_value_number (cfg,
1201                                              "TOPOLOGY",
1202                                              "MINIMUM-FRIENDS",
1203                                              &opt))
1204     opt = 0;
1205   minimum_friend_count = (unsigned int) opt;
1206   if (GNUNET_OK !=
1207       GNUNET_CONFIGURATION_get_value_number (cfg,
1208                                              "TOPOLOGY",
1209                                              "TARGET-CONNECTION-COUNT",
1210                                              &opt))
1211     opt = 16;
1212   target_connection_count = (unsigned int) opt;
1213   peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2,
1214                                                 GNUNET_NO);
1215   read_friends_file (cfg);
1216   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1217               "Topology would like %u connections with at least %u friends\n",
1218               target_connection_count,
1219               minimum_friend_count);
1220   if ( (friend_count < minimum_friend_count) &&
1221        (NULL == blacklist))
1222     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
1223                                             &blacklist_check,
1224                                             NULL);
1225   ats = GNUNET_ATS_connectivity_init (cfg);
1226   transport = GNUNET_TRANSPORT_connect (cfg,
1227                                         NULL,
1228                                         NULL,
1229                                         NULL,
1230                                         NULL,
1231                                         NULL);
1232   handle =
1233       GNUNET_CORE_connect (cfg, NULL,
1234                            &core_init,
1235                            &connect_notify,
1236                            &disconnect_notify,
1237                            NULL, GNUNET_NO,
1238                            NULL, GNUNET_NO,
1239                            handlers);
1240   GNUNET_SCHEDULER_add_shutdown (&cleaning_task,
1241                                  NULL);
1242   if (NULL == transport)
1243   {
1244     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1245                 _("Failed to connect to `%s' service.\n"),
1246                 "transport");
1247     GNUNET_SCHEDULER_shutdown ();
1248     return;
1249   }
1250   if (NULL == handle)
1251   {
1252     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1253                 _("Failed to connect to `%s' service.\n"),
1254                 "core");
1255     GNUNET_SCHEDULER_shutdown ();
1256     return;
1257   }
1258 }
1259
1260
1261 /**
1262  * The main function for the topology daemon.
1263  *
1264  * @param argc number of arguments from the command line
1265  * @param argv command line arguments
1266  * @return 0 ok, 1 on error
1267  */
1268 int
1269 main (int argc, char *const *argv)
1270 {
1271   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1272     GNUNET_GETOPT_OPTION_END
1273   };
1274   int ret;
1275
1276   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1277     return 2;
1278
1279   ret =
1280       (GNUNET_OK ==
1281        GNUNET_PROGRAM_run (argc, argv,
1282                            "gnunet-daemon-topology",
1283                            _("GNUnet topology control"),
1284                            options, &run, NULL)) ? 0 : 1;
1285   GNUNET_free ((void*) argv);
1286   return ret;
1287 }
1288
1289
1290 #if defined(LINUX) && defined(__GLIBC__)
1291 #include <malloc.h>
1292
1293 /**
1294  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1295  */
1296 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
1297 {
1298   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1299   mallopt (M_TOP_PAD, 1 * 1024);
1300   malloc_trim (0);
1301 }
1302 #endif
1303
1304 /* end of gnunet-daemon-topology.c */