RECLAIM/OIDC: code cleanup
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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 transmitting to this peer; NULL
74    * if peer is not connected.
75    */
76   struct GNUNET_MQ_Handle *mq;
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 it is a friend?
117    */
118   int is_friend;
119
120 };
121
122
123 /**
124  * Our peerinfo notification context.  We use notification
125  * to instantly learn about new peers as they are discovered.
126  */
127 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
128
129 /**
130  * Our configuration.
131  */
132 static const struct GNUNET_CONFIGURATION_Handle *cfg;
133
134 /**
135  * Handle to the CORE service.
136  */
137 static struct GNUNET_CORE_Handle *handle;
138
139 /**
140  * Handle to the PEERINFO service.
141  */
142 static struct GNUNET_PEERINFO_Handle *pi;
143
144 /**
145  * Handle to the ATS service.
146  */
147 static struct GNUNET_ATS_ConnectivityHandle *ats;
148
149 /**
150  * Identity of this peer.
151  */
152 static struct GNUNET_PeerIdentity my_identity;
153
154 /**
155  * All of our friends, all of our current neighbours and all peers for
156  * which we have HELLOs.  So pretty much everyone.  Maps peer identities
157  * to `struct Peer *` values.
158  */
159 static struct GNUNET_CONTAINER_MultiPeerMap *peers;
160
161 /**
162  * Handle for reporting statistics.
163  */
164 static struct GNUNET_STATISTICS_Handle *stats;
165
166 /**
167  * Blacklist (NULL if we have none).
168  */
169 static struct GNUNET_TRANSPORT_Blacklist *blacklist;
170
171 /**
172  * Task scheduled to asynchronously reconsider adding/removing
173  * peer connectivity suggestions.
174  */
175 static struct GNUNET_SCHEDULER_Task *add_task;
176
177 /**
178  * Active HELLO offering to transport service.
179  */
180 static struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
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 (NULL == pos->mq);
268   GNUNET_break (GNUNET_OK ==
269                 GNUNET_CONTAINER_multipeermap_remove (peers,
270                                                       pid,
271                                                       pos));
272   if (NULL != pos->hello_delay_task)
273   {
274     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
275     pos->hello_delay_task = NULL;
276   }
277   if (NULL != pos->sh)
278   {
279     GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
280     pos->sh = NULL;
281   }
282   if (NULL != pos->hello)
283   {
284     GNUNET_free_non_null (pos->hello);
285     pos->hello = NULL;
286   }
287   if (NULL != pos->filter)
288   {
289     GNUNET_CONTAINER_bloomfilter_free (pos->filter);
290     pos->filter = NULL;
291   }
292   GNUNET_free (pos);
293   return GNUNET_YES;
294 }
295
296
297 /**
298  * Recalculate how much we want to be connected to the specified peer
299  * and let ATS know about the result.
300  *
301  * @param pos peer to consider connecting to
302  */
303 static void
304 attempt_connect (struct Peer *pos)
305 {
306   uint32_t strength;
307
308   if (0 ==
309       GNUNET_memcmp (&my_identity,
310               &pos->pid))
311     return; /* This is myself, nothing to do. */
312   if (connection_count < target_connection_count)
313     strength = 1;
314   else
315     strength = 0;
316   if ( (friend_count < minimum_friend_count) ||
317        (GNUNET_YES == friends_only) )
318   {
319     if (pos->is_friend)
320       strength += 10; /* urgently needed */
321     else
322       strength = 0; /* disallowed */
323   }
324   if (pos->is_friend)
325     strength *= 2; /* friends always count more */
326   if (NULL != pos->mq)
327     strength *= 2; /* existing connections preferred */
328   if (strength == pos->strength)
329     return; /* nothing to do */
330   if (NULL != pos->sh)
331   {
332     GNUNET_ATS_connectivity_suggest_cancel (pos->sh);
333     pos->sh = NULL;
334   }
335   pos->strength = strength;
336   if (0 != strength)
337   {
338     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
339                 "Asking to connect to `%s' with strength %u\n",
340                 GNUNET_i2s (&pos->pid),
341                 (unsigned int) strength);
342     GNUNET_STATISTICS_update (stats,
343                               gettext_noop ("# connect requests issued to ATS"),
344                               1,
345                               GNUNET_NO);
346     pos->sh = GNUNET_ATS_connectivity_suggest (ats,
347                                                &pos->pid,
348                                                strength);
349   }
350 }
351
352
353 /**
354  * Create a new entry in the peer list.
355  *
356  * @param peer identity of the new entry
357  * @param hello hello message, can be NULL
358  * @param is_friend is the new entry for a friend?
359  * @return the new entry
360  */
361 static struct Peer *
362 make_peer (const struct GNUNET_PeerIdentity *peer,
363            const struct GNUNET_HELLO_Message *hello,
364            int is_friend)
365 {
366   struct Peer *ret;
367
368   ret = GNUNET_new (struct Peer);
369   ret->pid = *peer;
370   ret->is_friend = is_friend;
371   if (NULL != hello)
372   {
373     ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
374     GNUNET_memcpy (ret->hello,
375                    hello,
376                    GNUNET_HELLO_size (hello));
377   }
378   GNUNET_break (GNUNET_OK ==
379                 GNUNET_CONTAINER_multipeermap_put (peers,
380                                                    peer,
381                                                    ret,
382                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
383   return ret;
384 }
385
386
387 /**
388  * Setup bloom filter for the given peer entry.
389  *
390  * @param peer entry to initialize
391  */
392 static void
393 setup_filter (struct Peer *peer)
394 {
395   struct GNUNET_HashCode hc;
396
397   /* 2^{-5} chance of not sending a HELLO to a peer is
398    * acceptably small (if the filter is 50% full);
399    * 64 bytes of memory are small compared to the rest
400    * of the data structure and would only really become
401    * "useless" once a HELLO has been passed on to ~100
402    * other peers, which is likely more than enough in
403    * any case; hence 64, 5 as bloomfilter parameters. */
404   peer->filter = GNUNET_CONTAINER_bloomfilter_init (NULL, 64, 5);
405   peer->filter_expiration =
406       GNUNET_TIME_relative_to_absolute
407       (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
408   /* never send a peer its own HELLO */
409   GNUNET_CRYPTO_hash (&peer->pid,
410                       sizeof (struct GNUNET_PeerIdentity),
411                       &hc);
412   GNUNET_CONTAINER_bloomfilter_add (peer->filter, &hc);
413 }
414
415
416 /**
417  * Closure for #find_advertisable_hello().
418  */
419 struct FindAdvHelloContext
420 {
421
422   /**
423    * Peer we want to advertise to.
424    */
425   struct Peer *peer;
426
427   /**
428    * Where to store the result (peer selected for advertising).
429    */
430   struct Peer *result;
431
432   /**
433    * Maximum HELLO size we can use right now.
434    */
435   size_t max_size;
436
437   struct GNUNET_TIME_Relative next_adv;
438 };
439
440
441 /**
442  * Find a peer that would be reasonable for advertising.
443  *
444  * @param cls closure
445  * @param pid identity of a peer
446  * @param value 'struct Peer*' for the peer we are considering
447  * @return #GNUNET_YES (continue iteration)
448  */
449 static int
450 find_advertisable_hello (void *cls,
451                          const struct GNUNET_PeerIdentity *pid,
452                          void *value)
453 {
454   struct FindAdvHelloContext *fah = cls;
455   struct Peer *pos = value;
456   struct GNUNET_TIME_Relative rst_time;
457   struct GNUNET_HashCode hc;
458   size_t hs;
459
460   if (pos == fah->peer)
461     return GNUNET_YES;
462   if (pos->hello == NULL)
463     return GNUNET_YES;
464   rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
465   if (0 == rst_time.rel_value_us)
466   {
467     /* time to discard... */
468     GNUNET_CONTAINER_bloomfilter_free (pos->filter);
469     setup_filter (pos);
470   }
471   fah->next_adv = GNUNET_TIME_relative_min (rst_time, fah->next_adv);
472   hs = GNUNET_HELLO_size (pos->hello);
473   if (hs > fah->max_size)
474     return GNUNET_YES;
475   GNUNET_CRYPTO_hash (&fah->peer->pid,
476                       sizeof (struct GNUNET_PeerIdentity), &hc);
477   if (GNUNET_NO ==
478       GNUNET_CONTAINER_bloomfilter_test (pos->filter,
479                                          &hc))
480     fah->result = pos;
481   return GNUNET_YES;
482 }
483
484
485 /**
486  * Calculate when we would like to send the next HELLO to this
487  * peer and ask for it.
488  *
489  * @param cls for which peer to schedule the HELLO
490  */
491 static void
492 schedule_next_hello (void *cls)
493 {
494   struct Peer *pl = cls;
495   struct FindAdvHelloContext fah;
496   struct GNUNET_MQ_Envelope *env;
497   size_t want;
498   struct GNUNET_TIME_Relative delay;
499   struct GNUNET_HashCode hc;
500
501   pl->hello_delay_task = NULL;
502   GNUNET_assert (NULL != pl->mq);
503   /* find applicable HELLOs */
504   fah.peer = pl;
505   fah.result = NULL;
506   fah.max_size = GNUNET_MAX_MESSAGE_SIZE - 1;
507   fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
508   GNUNET_CONTAINER_multipeermap_iterate (peers,
509                                          &find_advertisable_hello,
510                                          &fah);
511   pl->hello_delay_task =
512       GNUNET_SCHEDULER_add_delayed (fah.next_adv,
513                                     &schedule_next_hello,
514                                     pl);
515   if (NULL == fah.result)
516     return;
517   delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
518   if (0 != delay.rel_value_us)
519     return;
520
521   want = GNUNET_HELLO_size (fah.result->hello);
522   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
523               "Sending HELLO with %u bytes",
524               (unsigned int) want);
525   env = GNUNET_MQ_msg_copy (&fah.result->hello->header);
526   GNUNET_MQ_send (pl->mq,
527                   env);
528
529   /* avoid sending this one again soon */
530   GNUNET_CRYPTO_hash (&pl->pid,
531                       sizeof (struct GNUNET_PeerIdentity),
532                       &hc);
533   GNUNET_CONTAINER_bloomfilter_add (fah.result->filter,
534                                     &hc);
535
536   GNUNET_STATISTICS_update (stats,
537                             gettext_noop ("# HELLO messages gossipped"),
538                             1,
539                             GNUNET_NO);
540   /* prepare to send the next one */
541   pl->next_hello_allowed
542     = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
543   if (NULL != pl->hello_delay_task)
544     GNUNET_SCHEDULER_cancel (pl->hello_delay_task);
545   pl->hello_delay_task
546     = GNUNET_SCHEDULER_add_now (&schedule_next_hello,
547                                 pl);
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 (NULL == peer->mq)
572     return GNUNET_YES;
573   if (NULL != peer->hello_delay_task)
574   {
575     GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
576     peer->hello_delay_task = NULL;
577   }
578   peer->hello_delay_task =
579       GNUNET_SCHEDULER_add_now (&schedule_next_hello, peer);
580   return GNUNET_YES;
581 }
582
583
584 /**
585  * Method called whenever a peer connects.
586  *
587  * @param cls closure
588  * @param peer peer identity this notification is about
589  * @param mq message queue for communicating with @a peer
590  * @return our `struct Peer` for @a peer
591  */
592 static void *
593 connect_notify (void *cls,
594                 const struct GNUNET_PeerIdentity *peer,
595                 struct GNUNET_MQ_Handle *mq)
596 {
597   struct Peer *pos;
598   uint64_t flags;
599   const void *extra;
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 == GNUNET_memcmp (&my_identity,
605                    peer))
606     return NULL;
607   extra = GNUNET_CORE_get_mq_options (GNUNET_YES,
608                                       GNUNET_CORE_PRIO_BEST_EFFORT,
609                                       &flags);
610   GNUNET_MQ_set_options (mq,
611                          flags,
612                          extra);
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 (NULL == pos->mq);
629   }
630   pos->mq = mq;
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   return pos;
646 }
647
648
649 /**
650  * Try to add more peers to our connection set.
651  *
652  * @param cls closure, not used
653  * @param pid identity of a peer
654  * @param value `struct Peer *` for the peer
655  * @return #GNUNET_YES (continue to iterate)
656  */
657 static int
658 try_add_peers (void *cls,
659                const struct GNUNET_PeerIdentity *pid,
660                void *value)
661 {
662   struct Peer *pos = value;
663
664   attempt_connect (pos);
665   return GNUNET_YES;
666 }
667
668
669 /**
670  * Add peers and schedule connection attempt
671  *
672  * @param cls unused, NULL
673  */
674 static void
675 add_peer_task (void *cls)
676 {
677   add_task = NULL;
678
679   GNUNET_CONTAINER_multipeermap_iterate (peers,
680                                          &try_add_peers,
681                                          NULL);
682 }
683
684
685 /**
686  * Method called whenever a peer disconnects.
687  *
688  * @param cls closure
689  * @param peer peer identity this notification is about
690  * @param internal_cls the `struct Peer` for this peer
691  */
692 static void
693 disconnect_notify (void *cls,
694                    const struct GNUNET_PeerIdentity *peer,
695                    void *internal_cls)
696 {
697   struct Peer *pos = internal_cls;
698
699   if (NULL == pos)
700     return; /* myself, we're shutting down */
701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702               "Core told us that we disconnected from `%s'\n",
703               GNUNET_i2s (peer));
704   if (NULL == pos->mq)
705   {
706     GNUNET_break (0);
707     return;
708   }
709   pos->mq = NULL;
710   connection_count--;
711   if (NULL != pos->hello_delay_task)
712   {
713     GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
714     pos->hello_delay_task = NULL;
715   }
716   GNUNET_STATISTICS_set (stats,
717                          gettext_noop ("# peers connected"),
718                          connection_count,
719                          GNUNET_NO);
720   if (pos->is_friend)
721   {
722     friend_count--;
723     GNUNET_STATISTICS_set (stats,
724                            gettext_noop ("# friends connected"),
725                            friend_count,
726                            GNUNET_NO);
727   }
728   if ( ( (connection_count < target_connection_count) ||
729          (friend_count < minimum_friend_count)) &&
730        (NULL == add_task) )
731     add_task = GNUNET_SCHEDULER_add_now (&add_peer_task,
732                                          NULL);
733   if ( (friend_count < minimum_friend_count) &&
734        (NULL == blacklist))
735     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
736                                             &blacklist_check,
737                                             NULL);
738 }
739
740
741 /**
742  * Iterator called on each address.
743  *
744  * @param cls flag that we will set if we see any addresses
745  * @param address the address of the peer
746  * @param expiration when will the given address expire
747  * @return #GNUNET_SYSERR always, to terminate iteration
748  */
749 static int
750 address_iterator (void *cls,
751                   const struct GNUNET_HELLO_Address *address,
752                   struct GNUNET_TIME_Absolute expiration)
753 {
754   int *flag = cls;
755
756   *flag = GNUNET_YES;
757   return GNUNET_SYSERR;
758 }
759
760
761 /**
762  * We've gotten a HELLO from another peer.  Consider it for
763  * advertising.
764  *
765  * @param hello the HELLO we got
766  */
767 static void
768 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
769 {
770   int have_address;
771   struct GNUNET_PeerIdentity pid;
772   struct GNUNET_TIME_Absolute dt;
773   struct GNUNET_HELLO_Message *nh;
774   struct Peer *peer;
775   uint16_t size;
776
777   if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
778   {
779     GNUNET_break (0);
780     return;
781   }
782   if (0 == GNUNET_memcmp (&pid,
783                    &my_identity))
784     return;                     /* that's me! */
785   have_address = GNUNET_NO;
786   GNUNET_HELLO_iterate_addresses (hello,
787                                   GNUNET_NO,
788                                   &address_iterator,
789                                   &have_address);
790   if (GNUNET_NO == have_address)
791     return;                     /* no point in advertising this one... */
792   peer = GNUNET_CONTAINER_multipeermap_get (peers, &pid);
793   if (NULL == peer)
794   {
795     peer = make_peer (&pid,
796                       hello,
797                       GNUNET_NO);
798   }
799   else if (NULL != peer->hello)
800   {
801     dt = GNUNET_HELLO_equals (peer->hello,
802                               hello,
803                               GNUNET_TIME_absolute_get ());
804     if (dt.abs_value_us == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us)
805       return;                   /* nothing new here */
806   }
807   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
808               "Found HELLO from peer `%s' for advertising\n",
809               GNUNET_i2s (&pid));
810   if (NULL != peer->hello)
811   {
812     nh = GNUNET_HELLO_merge (peer->hello,
813                              hello);
814     GNUNET_free (peer->hello);
815     peer->hello = nh;
816   }
817   else
818   {
819     size = GNUNET_HELLO_size (hello);
820     peer->hello = GNUNET_malloc (size);
821     GNUNET_memcpy (peer->hello,
822             hello,
823             size);
824   }
825   if (NULL != peer->filter)
826   {
827     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
828     peer->filter = NULL;
829   }
830   setup_filter (peer);
831   /* since we have a new HELLO to pick from, re-schedule all
832    * HELLO requests that are not bound by the HELLO send rate! */
833   GNUNET_CONTAINER_multipeermap_iterate (peers,
834                                          &reschedule_hellos,
835                                          peer);
836 }
837
838
839 /**
840  * PEERINFO calls this function to let us know about a possible peer
841  * that we might want to connect to.
842  *
843  * @param cls closure (not used)
844  * @param peer potential peer to connect to
845  * @param hello HELLO for this peer (or NULL)
846  * @param err_msg NULL if successful, otherwise contains error message
847  */
848 static void
849 process_peer (void *cls,
850               const struct GNUNET_PeerIdentity *peer,
851               const struct GNUNET_HELLO_Message *hello,
852               const char *err_msg)
853 {
854   struct Peer *pos;
855
856   if (NULL != err_msg)
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
859                 _("Error in communication with PEERINFO service: %s\n"),
860                 err_msg);
861     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
862     peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
863                                               GNUNET_NO,
864                                               &process_peer,
865                                               NULL);
866     return;
867   }
868   GNUNET_assert (NULL != peer);
869   if (0 == GNUNET_memcmp (&my_identity,
870                    peer))
871     return;                     /* that's me! */
872   if (NULL == hello)
873   {
874     /* free existing HELLO, if any */
875     pos = GNUNET_CONTAINER_multipeermap_get (peers,
876                                              peer);
877     if (NULL != pos)
878     {
879       GNUNET_free_non_null (pos->hello);
880       pos->hello = NULL;
881       if (NULL != pos->filter)
882       {
883         GNUNET_CONTAINER_bloomfilter_free (pos->filter);
884         pos->filter = NULL;
885       }
886       if ( (NULL == pos->mq) &&
887            (GNUNET_NO == pos->is_friend) )
888         free_peer (NULL,
889                    &pos->pid,
890                    pos);
891     }
892     return;
893   }
894   consider_for_advertising (hello);
895   pos = GNUNET_CONTAINER_multipeermap_get (peers,
896                                            peer);
897   if (NULL == pos)
898     pos = make_peer (peer,
899                      hello,
900                      GNUNET_NO);
901   attempt_connect (pos);
902 }
903
904
905 /**
906  * Function called after #GNUNET_CORE_connect has succeeded
907  * (or failed for good).
908  *
909  * @param cls closure
910  * @param my_id ID of this peer, NULL if we failed
911  */
912 static void
913 core_init (void *cls,
914            const struct GNUNET_PeerIdentity *my_id)
915 {
916   if (NULL == my_id)
917   {
918     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
919                 _("Failed to connect to core service, can not manage topology!\n"));
920     GNUNET_SCHEDULER_shutdown ();
921     return;
922   }
923   my_identity = *my_id;
924   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
925               "I am peer `%s'\n",
926               GNUNET_i2s (my_id));
927   peerinfo_notify = GNUNET_PEERINFO_notify (cfg,
928                                             GNUNET_NO,
929                                             &process_peer,
930                                             NULL);
931 }
932
933
934 /**
935  * Process friend found in FRIENDS file.
936  *
937  * @param cls pointer to an `unsigned int` to be incremented per friend found
938  * @param pid identity of the friend
939  */
940 static void
941 handle_friend (void *cls,
942                const struct GNUNET_PeerIdentity *pid)
943 {
944   unsigned int *entries_found = cls;
945   struct Peer *fl;
946
947   if (0 == GNUNET_memcmp (pid,
948                    &my_identity))
949   {
950     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
951                 _("Found myself `%s' in friend list (useless, ignored)\n"),
952                 GNUNET_i2s (pid));
953     return;
954   }
955   (*entries_found)++;
956   fl = make_peer (pid, NULL, GNUNET_YES);
957   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
958               _("Found friend `%s' in configuration\n"),
959               GNUNET_i2s (&fl->pid));
960 }
961
962
963 /**
964  * Read the friends file.
965  */
966 static void
967 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
968 {
969   unsigned int entries_found;
970
971   entries_found = 0;
972   if (GNUNET_OK !=
973       GNUNET_FRIENDS_parse (cfg,
974                             &handle_friend,
975                             &entries_found))
976   {
977     if ( (GNUNET_YES == friends_only) ||
978          (minimum_friend_count > 0))
979       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
980                   _("Encountered errors parsing friends list!\n"));
981   }
982   GNUNET_STATISTICS_update (stats,
983                             gettext_noop ("# friends in configuration"),
984                             entries_found,
985                             GNUNET_NO);
986   if ( (minimum_friend_count > entries_found) &&
987        (GNUNET_NO == friends_only) )
988   {
989     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
990                 _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
991   }
992   if ( (minimum_friend_count > target_connection_count) &&
993        (GNUNET_NO == friends_only))
994   {
995     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
996                 _("More friendly connections required than target total number of connections.\n"));
997   }
998 }
999
1000
1001 /**
1002  * This function is called whenever an encrypted HELLO message is
1003  * received.
1004  *
1005  * @param cls closure with the peer identity of the sender
1006  * @param message the actual HELLO message
1007  * @return #GNUNET_OK if @a message is well-formed
1008  *         #GNUNET_SYSERR if @a message is invalid
1009  */
1010 static int
1011 check_hello (void *cls,
1012              const struct GNUNET_HELLO_Message *message)
1013 {
1014   struct GNUNET_PeerIdentity pid;
1015
1016   if (GNUNET_OK !=
1017       GNUNET_HELLO_get_id (message,
1018                            &pid))
1019   {
1020     GNUNET_break_op (0);
1021     return GNUNET_SYSERR;
1022   }
1023   return GNUNET_OK;
1024 }
1025
1026
1027 /**
1028  * This function is called whenever an encrypted HELLO message is
1029  * received.
1030  *
1031  * @param cls closure with the peer identity of the sender
1032  * @param message the actual HELLO message
1033  */
1034 static void
1035 handle_hello (void *cls,
1036               const struct GNUNET_HELLO_Message *message)
1037 {
1038   const struct GNUNET_PeerIdentity *other = cls;
1039   struct Peer *peer;
1040   struct GNUNET_PeerIdentity pid;
1041
1042   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1043               "Received encrypted HELLO from peer `%s'",
1044               GNUNET_i2s (other));
1045   GNUNET_assert (GNUNET_OK ==
1046                  GNUNET_HELLO_get_id (message,
1047                                       &pid));
1048   GNUNET_STATISTICS_update (stats,
1049                             gettext_noop ("# HELLO messages received"),
1050                             1,
1051                             GNUNET_NO);
1052   peer = GNUNET_CONTAINER_multipeermap_get (peers,
1053                                             &pid);
1054   if (NULL == peer)
1055   {
1056     if ( (GNUNET_YES == friends_only) ||
1057          (friend_count < minimum_friend_count) )
1058       return;
1059   }
1060   else
1061   {
1062     if ( (GNUNET_YES != peer->is_friend) &&
1063          (GNUNET_YES == friends_only) )
1064       return;
1065     if ((GNUNET_YES != peer->is_friend) &&
1066         (friend_count < minimum_friend_count))
1067       return;
1068   }
1069   (void) GNUNET_PEERINFO_add_peer (pi,
1070                                    message,
1071                                    NULL,
1072                                    NULL);
1073 }
1074
1075
1076 /**
1077  * Last task run during shutdown.  Disconnects us from
1078  * the transport and core.
1079  *
1080  * @param cls unused, NULL
1081  */
1082 static void
1083 cleaning_task (void *cls)
1084 {
1085   if (NULL != peerinfo_notify)
1086   {
1087     GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1088     peerinfo_notify = NULL;
1089   }
1090   if (NULL != handle)
1091   {
1092     GNUNET_CORE_disconnect (handle);
1093     handle = NULL;
1094   }
1095   whitelist_peers ();
1096   if (NULL != add_task)
1097   {
1098     GNUNET_SCHEDULER_cancel (add_task);
1099     add_task = NULL;
1100   }
1101   if (NULL != oh)
1102   {
1103     GNUNET_TRANSPORT_offer_hello_cancel (oh);
1104     oh = NULL;
1105   }
1106   GNUNET_CONTAINER_multipeermap_iterate (peers,
1107                                          &free_peer,
1108                                          NULL);
1109   GNUNET_CONTAINER_multipeermap_destroy (peers);
1110   peers = NULL;
1111   if (NULL != ats)
1112   {
1113     GNUNET_ATS_connectivity_done (ats);
1114     ats = NULL;
1115   }
1116   if (NULL != pi)
1117   {
1118     GNUNET_PEERINFO_disconnect (pi);
1119     pi = NULL;
1120   }
1121   if (NULL != stats)
1122   {
1123     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1124     stats = NULL;
1125   }
1126 }
1127
1128
1129 /**
1130  * Main function that will be run.
1131  *
1132  * @param cls closure
1133  * @param args remaining command-line arguments
1134  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1135  * @param c configuration
1136  */
1137 static void
1138 run (void *cls,
1139      char *const *args,
1140      const char *cfgfile,
1141      const struct GNUNET_CONFIGURATION_Handle *c)
1142 {
1143   struct GNUNET_MQ_MessageHandler handlers[] = {
1144     GNUNET_MQ_hd_var_size (hello,
1145                            GNUNET_MESSAGE_TYPE_HELLO,
1146                            struct GNUNET_HELLO_Message,
1147                            NULL),
1148     GNUNET_MQ_handler_end ()
1149   };
1150   unsigned long long opt;
1151
1152   cfg = c;
1153   stats = GNUNET_STATISTICS_create ("topology", cfg);
1154   friends_only =
1155       GNUNET_CONFIGURATION_get_value_yesno (cfg,
1156                                             "TOPOLOGY",
1157                                             "FRIENDS-ONLY");
1158   if (GNUNET_OK !=
1159       GNUNET_CONFIGURATION_get_value_number (cfg,
1160                                              "TOPOLOGY",
1161                                              "MINIMUM-FRIENDS",
1162                                              &opt))
1163     opt = 0;
1164   minimum_friend_count = (unsigned int) opt;
1165   if (GNUNET_OK !=
1166       GNUNET_CONFIGURATION_get_value_number (cfg,
1167                                              "TOPOLOGY",
1168                                              "TARGET-CONNECTION-COUNT",
1169                                              &opt))
1170     opt = 16;
1171   target_connection_count = (unsigned int) opt;
1172   peers = GNUNET_CONTAINER_multipeermap_create (target_connection_count * 2,
1173                                                 GNUNET_NO);
1174   read_friends_file (cfg);
1175   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176               "Topology would like %u connections with at least %u friends\n",
1177               target_connection_count,
1178               minimum_friend_count);
1179   if ( (GNUNET_YES == friends_only) ||
1180        (minimum_friend_count > 0))
1181     blacklist = GNUNET_TRANSPORT_blacklist (cfg,
1182                                             &blacklist_check,
1183                                             NULL);
1184   ats = GNUNET_ATS_connectivity_init (cfg);
1185   pi = GNUNET_PEERINFO_connect (cfg);
1186   handle = GNUNET_CORE_connect (cfg,
1187                                 NULL,
1188                                 &core_init,
1189                                 &connect_notify,
1190                                 &disconnect_notify,
1191                                 handlers);
1192   GNUNET_SCHEDULER_add_shutdown (&cleaning_task,
1193                                  NULL);
1194   if (NULL == handle)
1195   {
1196     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1197                 _("Failed to connect to `%s' service.\n"),
1198                 "core");
1199     GNUNET_SCHEDULER_shutdown ();
1200     return;
1201   }
1202 }
1203
1204
1205 /**
1206  * The main function for the topology daemon.
1207  *
1208  * @param argc number of arguments from the command line
1209  * @param argv command line arguments
1210  * @return 0 ok, 1 on error
1211  */
1212 int
1213 main (int argc,
1214       char *const *argv)
1215 {
1216   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1217     GNUNET_GETOPT_OPTION_END
1218   };
1219   int ret;
1220
1221   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1222     return 2;
1223
1224   ret =
1225       (GNUNET_OK ==
1226        GNUNET_PROGRAM_run (argc, argv,
1227                            "gnunet-daemon-topology",
1228                            _("GNUnet topology control"),
1229                            options, &run, NULL)) ? 0 : 1;
1230   GNUNET_free ((void*) argv);
1231   return ret;
1232 }
1233
1234
1235 #if defined(LINUX) && defined(__GLIBC__)
1236 #include <malloc.h>
1237
1238 /**
1239  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
1240  */
1241 void __attribute__ ((constructor))
1242 GNUNET_ARM_memory_init ()
1243 {
1244   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
1245   mallopt (M_TOP_PAD, 1 * 1024);
1246   malloc_trim (0);
1247 }
1248 #endif
1249
1250 /* end of gnunet-daemon-topology.c */