rounding
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
1 /*
2      This file is part of GNUnet.
3      (C) 2007, 2008, 2009, 2010 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 2, 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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file topology/gnunet-daemon-topology.c
23  * @brief code for maintaining the mesh topology
24  * @author Christian Grothoff
25  */
26
27 #include <stdlib.h>
28 #include "platform.h"
29 #include "gnunet_core_service.h"
30 #include "gnunet_protocols.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_statistics_service.h"
33 #include "gnunet_transport_service.h"
34 #include "gnunet_util_lib.h"
35
36
37 #define DEBUG_TOPOLOGY GNUNET_NO
38
39 /**
40  * For how long do we blacklist a peer after a failed connection
41  * attempt?
42  */
43 #define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
44
45 /**
46  * For how long do we blacklist a friend after a failed connection
47  * attempt?
48  */
49 #define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
50
51 /**
52  * How often do we at most advertise any HELLO to a peer?
53  */
54 #define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
55
56 /**
57  * How often do we at most advertise the same HELLO to the same peer?
58  */
59 #define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
60
61
62 /**
63  * List of neighbours, friends and blacklisted peers.
64  */
65 struct PeerList
66 {
67
68   /**
69    * This is a linked list.
70    */
71   struct PeerList *next;
72
73   /**
74    * Our handle for the request to transmit HELLOs to this peer; NULL
75    * if no such request is pending.
76    */
77   struct GNUNET_CORE_TransmitHandle *hello_req;  
78
79   /**
80    * Our handle for the request to connect to this peer; NULL if no
81    * such request is pending.
82    */
83   struct GNUNET_CORE_PeerRequestHandle *connect_req;  
84
85   /**
86    * Pointer to the HELLO message of this peer; can be NULL.
87    */
88   struct GNUNET_HELLO_Message *hello;
89
90   /**
91    * Bloom filter used to mark which peers already got the HELLO
92    * from this peer.
93    */
94   struct GNUNET_CONTAINER_BloomFilter *filter;
95
96   /**
97    * Our request handle for *whitelisting* this peer (NULL if
98    * no whitelisting request is pending).
99    */
100   struct GNUNET_TRANSPORT_BlacklistRequest *wh;
101
102   /**
103    * Is this peer listed here because he is a friend?
104    */
105   int is_friend;
106
107   /**
108    * Are we connected to this peer right now?
109    */
110   int is_connected;
111
112   /**
113    * Are we currently blocking this peer (via blacklist)?
114    */
115   int is_blocked;
116
117   /**
118    * Until what time should we not try to connect again
119    * to this peer?
120    */
121   struct GNUNET_TIME_Absolute blacklisted_until;
122
123   /**
124    * Next time we are allowed to transmit a HELLO to this peer?
125    */
126   struct GNUNET_TIME_Absolute next_hello_allowed;
127
128   /**
129    * When should we reset the bloom filter of this entry?
130    */
131   struct GNUNET_TIME_Absolute filter_expiration;
132
133   /**
134    * ID of task we use to wait for the time to send the next HELLO
135    * to this peer.
136    */
137   GNUNET_SCHEDULER_TaskIdentifier hello_delay_task;
138
139   /**
140    * ID of the peer.
141    */
142   struct GNUNET_PeerIdentity id;
143
144 };
145
146
147 /**
148  * Entry in linked list of active 'disconnect' requests that we have issued.
149  */
150 struct DisconnectList
151 {
152   /**
153    * This is a doubly-linked list.
154    */
155   struct DisconnectList *next;
156
157   /**
158    * This is a doubly-linked list.
159    */
160   struct DisconnectList *prev;
161   
162   /**
163    * Our request handle.
164    */
165   struct GNUNET_TRANSPORT_BlacklistRequest *rh;
166   
167   /**
168    * Peer we tried to disconnect.
169    */
170   struct GNUNET_PeerIdentity peer;
171
172 };
173
174
175 /**
176  * Our peerinfo notification context.  We use notification
177  * to instantly learn about new peers as they are discovered.
178  */
179 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
180
181 /**
182  * Our scheduler.
183  */
184 static struct GNUNET_SCHEDULER_Handle *sched;
185
186 /**
187  * Our configuration.
188  */
189 static const struct GNUNET_CONFIGURATION_Handle *cfg;
190
191 /**
192  * Handle to the core API.
193  */
194 static struct GNUNET_CORE_Handle *handle;
195
196 /**
197  * Handle to the transport API.
198  */
199 static struct GNUNET_TRANSPORT_Handle *transport;
200
201 /**
202  * Identity of this peer.
203  */
204 static struct GNUNET_PeerIdentity my_identity;
205
206 /**
207  * Linked list of all of our friends, all of our current neighbours
208  * and all peers for which we have HELLOs.  So pretty much everyone.
209  */
210 static struct PeerList *peers;
211
212 /**
213  * Handle for reporting statistics.
214  */
215 static struct GNUNET_STATISTICS_Handle *stats;
216
217 /**
218  * Flag to disallow non-friend connections (pure F2F mode).
219  */
220 static int friends_only;
221
222 /**
223  * Minimum number of friends to have in the
224  * connection set before we allow non-friends.
225  */
226 static unsigned int minimum_friend_count;
227
228 /**
229  * Number of peers (friends and others) that we are currently connected to.
230  */
231 static unsigned int connection_count;
232
233 /**
234  * Target number of connections.
235  */
236 static unsigned int target_connection_count;
237
238 /**
239  * Number of friends that we are currently connected to.
240  */
241 static unsigned int friend_count;
242
243 /**
244  * Should the topology daemon try to establish connections?
245  */
246 static int autoconnect;
247
248 /**
249  * Head of doubly-linked list of active 'disconnect' requests that we have issued.
250  */
251 static struct DisconnectList *disconnect_head;
252
253 /**
254  * Head of doubly-linked list of active 'disconnect' requests that we have issued.
255  */
256 static struct DisconnectList *disconnect_tail;
257
258
259 /**
260  * Function called once our request to 'disconnect' a peer
261  * has completed.
262  *
263  * @param cls our 'struct DisconnectList'
264  * @param tc unused
265  */
266 static void
267 disconnect_done (void *cls,
268                  const struct GNUNET_SCHEDULER_TaskContext *tc)
269 {
270   struct DisconnectList *dl = cls;
271
272   GNUNET_STATISTICS_update (stats,
273                             gettext_noop ("# peers blacklisted"),
274                             1,
275                             GNUNET_NO);
276   GNUNET_CONTAINER_DLL_remove (disconnect_head,
277                                disconnect_tail,
278                                dl);
279   GNUNET_free (dl);
280 }
281
282
283 /**
284  * Force a disconnect from the specified peer. 
285  */
286 static void
287 force_disconnect (struct PeerList *pl)
288 {
289   const struct GNUNET_PeerIdentity *peer = &pl->id;
290   struct DisconnectList *dl;
291
292   if (NULL != pl->wh)
293     {
294       GNUNET_TRANSPORT_blacklist_cancel (pl->wh);
295       pl->wh = NULL;
296     }
297   pl->is_blocked = GNUNET_YES;
298   dl = GNUNET_malloc (sizeof (struct DisconnectList));
299   dl->peer = *peer;
300   GNUNET_CONTAINER_DLL_insert (disconnect_head,
301                                disconnect_tail,
302                                dl);
303   dl->rh = GNUNET_TRANSPORT_blacklist (sched, cfg,                                              
304                                        peer,
305                                        GNUNET_TIME_UNIT_FOREVER_REL,
306                                        GNUNET_TIME_UNIT_FOREVER_REL,
307                                        &disconnect_done,
308                                        dl);
309 }
310
311
312
313 /**
314  * Function called once our request to 'whitelist' a peer
315  * has completed.
316  *
317  * @param cls our 'struct PeerList'
318  * @param tc unused
319  */
320 static void
321 whitelist_done (void *cls,
322                 const struct GNUNET_SCHEDULER_TaskContext *tc)
323 {
324   struct PeerList *pl = cls;
325
326   pl->wh = NULL;
327   GNUNET_STATISTICS_update (stats,
328                             gettext_noop ("# peers blacklisted"),
329                             -1,
330                             GNUNET_NO);
331 }
332
333
334 /**
335  * Whitelist all peers that we blacklisted; we've passed
336  * the minimum number of friends.
337  */
338 static void
339 whitelist_peers ()
340 {
341   struct PeerList *pl;
342   struct DisconnectList *dl;
343
344   /* first, cancel all blacklisting requests */
345   while (NULL != (dl = disconnect_head))
346     {
347       GNUNET_CONTAINER_DLL_remove (disconnect_head,
348                                    disconnect_tail,
349                                    dl);
350       GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
351       GNUNET_free (dl);
352     }
353   /* then, specifically whitelist all peers that we
354      know to have blacklisted */
355   pl = peers;
356   while (pl != NULL)
357     {
358       if (pl->is_blocked)
359         {
360           pl->wh = GNUNET_TRANSPORT_blacklist (sched, cfg,                                              
361                                                &pl->id,
362                                                GNUNET_TIME_UNIT_ZERO,
363                                                GNUNET_TIME_UNIT_FOREVER_REL,
364                                                &whitelist_done,
365                                                pl);
366           pl->is_blocked = GNUNET_NO;
367         }
368       pl = pl->next;
369     }
370 }
371
372
373 /**
374  * Function called by core when our attempt to connect succeeded.
375  */
376 static void
377 connect_completed_callback (void *cls,
378                             const struct GNUNET_SCHEDULER_TaskContext *tc)
379 {
380   struct PeerList *pos = cls;
381
382   pos->connect_req = NULL;
383 }
384
385
386 /**
387  * Try to connect to the specified peer.
388  *
389  * @param pos peer to connect to
390  */
391 static void
392 attempt_connect (struct PeerList *pos)
393 {
394   if (GNUNET_YES == pos->is_friend)
395     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
396   else
397     pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
398 #if DEBUG_TOPOLOGY
399   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
400               "Asking core to connect to `%s'\n",
401               GNUNET_i2s (&pos->id));
402 #endif
403   GNUNET_STATISTICS_update (stats,
404                             gettext_noop ("# connect requests issued to core"),
405                             1,
406                             GNUNET_NO);
407   pos->connect_req = GNUNET_CORE_peer_request_connect (sched, cfg,
408                                                        GNUNET_TIME_UNIT_MINUTES,
409                                                        &pos->id,
410                                                        &connect_completed_callback,
411                                                        pos);
412 }
413
414
415 /**
416  * Find a peer in our linked list.  
417  * FIXME: should probably use a hash map instead.
418  */
419 struct PeerList *
420 find_peer (const struct GNUNET_PeerIdentity * peer)
421 {
422   struct PeerList *pos;
423
424   pos = peers;
425   while (pos != NULL)
426     {
427       if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
428         return pos;
429       pos = pos->next;
430     }
431   return NULL;
432 }
433
434
435 /**
436  * Check if an additional connection from the given peer is allowed.
437  */
438 static int
439 is_connection_allowed (struct PeerList *peer)
440 {
441   if (0 == memcmp (&my_identity, &peer->id, sizeof (struct GNUNET_PeerIdentity)))
442     return GNUNET_SYSERR;       /* disallow connections to self */
443   if (peer->is_friend)
444     return GNUNET_OK;
445   if (GNUNET_YES == friends_only)
446     {
447 #if DEBUG_TOPOLOGY
448       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
449                   "Determined that `%s' is not allowed to connect (not a friend)\n",
450                   GNUNET_i2s (&peer->id));
451 #endif       
452       return GNUNET_SYSERR;
453     }
454   if (friend_count >= minimum_friend_count)
455     return GNUNET_OK;
456 #if DEBUG_TOPOLOGY
457   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
458               "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
459               GNUNET_i2s (&peer->id));
460 #endif       
461   return GNUNET_SYSERR;
462 }
463
464
465 /**
466  * Create a new entry in the peer list.
467  *
468  * @param peer identity of the new entry
469  * @param hello hello message, can be NULL
470  * @param is_friend is the new entry for a friend?
471  * @return the new entry
472  */
473 static struct PeerList *
474 make_peer (const struct
475            GNUNET_PeerIdentity * peer,
476            const struct GNUNET_HELLO_Message *hello,
477            int is_friend)
478 {
479   struct PeerList *ret;
480   
481   ret = GNUNET_malloc (sizeof (struct PeerList));
482   ret->id = *peer;
483   ret->is_friend = is_friend;
484   if (hello != NULL)
485     {
486       ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
487       memcpy (ret->hello, hello,
488               GNUNET_HELLO_size (hello));
489     }
490   ret->next = peers;
491   peers = ret;
492   return ret;
493 }
494
495
496 /**
497  * Free all resources associated with the given peer.
498  *
499  * @param peer peer to free
500  */
501 static void
502 free_peer (struct PeerList *peer)
503 {
504   struct PeerList *pos;
505   struct PeerList *prev;
506   
507   prev = NULL;
508   pos = peers;
509   while (peer != pos)
510     {
511       prev = pos;
512       pos = pos->next;
513     }
514   GNUNET_assert (pos != NULL);
515    if (prev == NULL)
516      peers = pos->next;
517    else
518      prev->next = pos->next;
519    if (pos->hello_req != NULL)
520      GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
521    if (pos->wh != NULL)
522      GNUNET_TRANSPORT_blacklist_cancel (pos->wh);
523    if (pos->connect_req != NULL)
524      GNUNET_CORE_peer_request_connect_cancel (pos->connect_req);              
525    if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
526      GNUNET_SCHEDULER_cancel (sched,
527                               pos->hello_delay_task);
528    GNUNET_free_non_null (pos->hello);   
529    if (pos->filter != NULL)
530      GNUNET_CONTAINER_bloomfilter_free (peer->filter);
531    GNUNET_free (pos);
532 }
533
534
535 /**
536  * Setup bloom filter for the given peer entry.
537  *
538  * @param peer entry to initialize
539  */
540 static void
541 setup_filter (struct PeerList *peer)
542 {
543   /* 2^{-5} chance of not sending a HELLO to a peer is
544      acceptably small (if the filter is 50% full);
545      64 bytes of memory are small compared to the rest
546      of the data structure and would only really become
547      "useless" once a HELLO has been passed on to ~100
548      other peers, which is likely more than enough in
549      any case; hence 64, 5 as bloomfilter parameters. */
550   peer->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
551   peer->filter_expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
552   /* never send a peer its own HELLO */
553   GNUNET_CONTAINER_bloomfilter_add (peer->filter, &peer->id.hashPubKey);
554 }
555
556
557 /**
558  * Function to fill send buffer with HELLO.
559  *
560  * @param cls 'struct PeerList' of the target peer
561  * @param size number of bytes available in buf
562  * @param buf where the callee should write the message
563  * @return number of bytes written to buf
564  */
565 static size_t
566 hello_advertising_ready (void *cls,
567                          size_t size,
568                          void *buf);
569
570
571 /**
572  * Calculate when we would like to send the next HELLO to this
573  * peer and ask for it.
574  *
575  * @param cls for which peer to schedule the HELLO
576  * @param tc task context
577  */
578 static void
579 schedule_next_hello (void *cls,
580                      const struct GNUNET_SCHEDULER_TaskContext *tc)
581 {
582   struct PeerList *pl = cls;
583   struct PeerList *pos;
584   struct PeerList *next;
585   uint16_t next_want;
586   struct GNUNET_TIME_Relative next_adv;
587   struct GNUNET_TIME_Relative rst_time;
588   
589   pl->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
590   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
591     return; /* we're out of here */
592   next_want = 0;
593   next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
594   /* find applicable HELLOs */
595   next = peers;
596   while (NULL != (pos = next))
597     {
598       next = pos->next;
599       if (pos->hello == NULL)
600         continue;
601       rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
602       if (0 == rst_time.value)
603         {
604           /* time to discard... */
605           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
606           setup_filter (pos);
607         }
608       else
609         {
610           if (rst_time.value < next_adv.value)
611             next_want = GNUNET_HELLO_size (pos->hello);
612           next_adv = GNUNET_TIME_relative_min (rst_time,
613                                                next_adv);         
614         }
615       if (GNUNET_NO ==
616           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
617                                              &pl->id.hashPubKey))
618         break;
619     }
620   if (pos != NULL)  
621     next_adv = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
622   if (next_adv.value == 0)
623     {
624       /* now! */
625       pl->hello_req = GNUNET_CORE_notify_transmit_ready (handle, 0,
626                                                          next_adv,
627                                                          &pl->id,
628                                                          next_want,
629                                                          &hello_advertising_ready,
630                                                          pl);
631       return;
632     }
633   pl->hello_delay_task 
634     = GNUNET_SCHEDULER_add_delayed (sched,
635                                     next_adv,
636                                     &schedule_next_hello,
637                                     pl);
638 }
639
640
641 /**
642  * Cancel existing requests for sending HELLOs to this peer
643  * and recalculate when we should send HELLOs to it based
644  * on our current state (something changed!).
645  */
646 static void
647 reschedule_hellos (struct PeerList *peer)
648 {
649   if (peer->hello_req != NULL)
650     {
651       GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
652       peer->hello_req = NULL;
653     }
654    if (peer->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
655      {
656        GNUNET_SCHEDULER_cancel (sched,
657                                 peer->hello_delay_task);
658        peer->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
659      }
660    peer->hello_delay_task 
661      = GNUNET_SCHEDULER_add_now (sched,
662                                  &schedule_next_hello,
663                                  peer);
664 }
665
666
667 /**
668  * Method called whenever a peer connects.
669  *
670  * @param cls closure
671  * @param peer peer identity this notification is about
672  * @param latency reported latency of the connection with 'other'
673  * @param distance reported distance (DV) to 'other' 
674  */
675 static void 
676 connect_notify (void *cls,
677                 const struct
678                 GNUNET_PeerIdentity * peer,
679                 struct GNUNET_TIME_Relative latency,
680                 uint32_t distance)
681 {
682   struct PeerList *pos;
683
684 #if DEBUG_TOPOLOGY
685   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
686               "Core told us that we are connecting to `%s'\n",
687               GNUNET_i2s (peer));
688 #endif
689   connection_count++;
690   GNUNET_STATISTICS_set (stats,
691                          gettext_noop ("# peers connected"),
692                          connection_count,
693                          GNUNET_NO);
694   pos = find_peer (peer);
695   if (pos == NULL)    
696     {
697       pos = make_peer (peer, NULL, GNUNET_NO);
698       if (GNUNET_OK != is_connection_allowed (pos))
699         {
700           GNUNET_assert (pos->is_friend == GNUNET_NO);
701           pos->is_connected = GNUNET_YES;
702 #if DEBUG_TOPOLOGY
703           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
704                       "Connection to `%s' is forbidden, forcing disconnect!\n",
705                       GNUNET_i2s (peer));
706 #endif       
707           force_disconnect (pos);
708           return;
709         }
710     }
711   else
712     {
713       GNUNET_assert (GNUNET_NO == pos->is_connected);
714       pos->blacklisted_until.value = 0; /* remove blacklisting */
715     }
716   pos->is_connected = GNUNET_YES;
717   if (pos->is_friend)
718     {
719       if ( (friend_count == minimum_friend_count - 1) &&
720            (GNUNET_YES != friends_only) )       
721         whitelist_peers ();       
722       friend_count++;
723       GNUNET_STATISTICS_set (stats,
724                              gettext_noop ("# friends connected"),
725                              connection_count,
726                              GNUNET_NO);
727     }
728   reschedule_hellos (pos);
729 }
730
731
732 /**
733  * Disconnect from all non-friends (we're below quota).
734  */
735 static void
736 drop_non_friends ()
737 {
738   struct PeerList *pos;
739
740   pos = peers;
741   while (pos != NULL)
742     {
743       if ( (GNUNET_NO == pos->is_friend) &&
744            (GNUNET_YES == pos->is_connected) )
745         {
746 #if DEBUG_TOPOLOGY
747           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
748                       "Connection to `%s' is not from a friend, forcing disconnect!\n",
749                       GNUNET_i2s (&pos->id));
750 #endif       
751           force_disconnect (pos);
752         }
753       pos = pos->next;
754     }
755 }
756
757
758 /**
759  * Try to add more peers to our connection set.
760  */
761 static void
762 try_add_peers ()
763 {
764   struct PeerList *pos;
765
766   pos = peers;
767   while (pos != NULL)
768     {
769       if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
770            ( (GNUNET_YES == pos->is_friend) ||
771              (friend_count >= minimum_friend_count) ) &&
772            (GNUNET_YES != pos->is_connected) )
773         attempt_connect (pos);
774       pos = pos->next;
775     }
776 }
777
778
779 /**
780  * Method called whenever a peer disconnects.
781  *
782  * @param cls closure
783  * @param peer peer identity this notification is about
784  */
785 static void 
786 disconnect_notify (void *cls,
787                    const struct
788                    GNUNET_PeerIdentity * peer)
789 {
790   struct PeerList *pos;
791  
792 #if DEBUG_TOPOLOGY
793   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
794               "Core told us that we disconnected from `%s'\n",
795               GNUNET_i2s (peer));
796 #endif       
797   pos = find_peer (peer);
798   if (pos == NULL)
799     {
800       GNUNET_break (0);
801       return;
802     }
803   if (pos->is_connected != GNUNET_YES)
804     {
805       GNUNET_break (0);
806       return;
807     }
808   connection_count--;
809   GNUNET_STATISTICS_set (stats,
810                          gettext_noop ("# peers connected"),
811                          connection_count,
812                          GNUNET_NO);
813   if (pos->is_friend)
814     {
815       friend_count--; 
816       GNUNET_STATISTICS_set (stats,
817                              gettext_noop ("# friends connected"),
818                              connection_count,
819                              GNUNET_NO);
820     }
821   if ( (connection_count < target_connection_count) ||
822        (friend_count < minimum_friend_count) )
823     try_add_peers ();   
824   if (friend_count < minimum_friend_count)
825     {
826       /* disconnect from all non-friends */
827 #if DEBUG_TOPOLOGY
828       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
829                   "Not enough friendly connections, dropping all non-friend connections\n");
830 #endif       
831       drop_non_friends ();
832     }
833 }
834
835
836 /**
837  * Iterator called on each address.
838  *
839  * @param cls flag that we will set if we see any addresses
840  * @param tname name of the transport
841  * @param expiration when will the given address expire
842  * @param addr the address of the peer
843  * @param addrlen number of bytes in addr
844  * @return GNUNET_SYSERR always, to terminate iteration
845  */
846 static int
847 address_iterator (void *cls,
848                   const char *tname,
849                   struct GNUNET_TIME_Absolute expiration,
850                   const void *addr, size_t addrlen)
851 {
852   int *flag = cls;
853   *flag = GNUNET_YES;
854   return GNUNET_SYSERR;
855 }
856
857
858 /**
859  * We've gotten a HELLO from another peer.  Consider it for
860  * advertising.
861  */
862 static void
863 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
864 {
865   int have_address;
866   struct GNUNET_PeerIdentity pid;
867   struct PeerList *peer;
868   struct PeerList *pos;
869   uint16_t size;
870
871   GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
872   if (0 == memcmp (&pid,
873                    &my_identity,
874                    sizeof (struct GNUNET_PeerIdentity)))
875     return; /* that's me! */
876   have_address = GNUNET_NO;
877   GNUNET_HELLO_iterate_addresses (hello,
878                                   GNUNET_NO,
879                                   &address_iterator,
880                                   &have_address);
881   if (GNUNET_NO == have_address)
882     return; /* no point in advertising this one... */
883   peer = find_peer (&pid);
884   if (peer == NULL)
885     peer = make_peer (&pid, hello, GNUNET_NO);
886 #if DEBUG_TOPOLOGY
887   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888               "Found `%s' from peer `%s' for advertising\n",
889               "HELLO",
890               GNUNET_i2s (&pid));
891 #endif 
892   size = GNUNET_HELLO_size (hello);
893   GNUNET_free_non_null (peer->hello);
894   peer->hello = GNUNET_malloc (size);
895   memcpy (peer->hello, hello, size);
896   if (peer->filter != NULL)
897     GNUNET_CONTAINER_bloomfilter_free (peer->filter);
898   setup_filter (peer);
899   /* since we have a new HELLO to pick from, re-schedule all
900      HELLO requests that are not bound by the HELLO send rate! */
901   pos = peers;
902   while (NULL != pos)
903     {
904       if (pos != peer)  
905         {
906           if ( (pos->is_connected) &&
907                (GNUNET_TIME_absolute_get_remaining (pos->next_hello_allowed).value <= HELLO_ADVERTISEMENT_MIN_FREQUENCY.value) )
908             reschedule_hellos (pos);    
909         }
910       pos = pos->next;
911     }
912 }
913
914
915 /**
916  * Peerinfo calls this function to let us know about a possible peer
917  * that we might want to connect to.
918  */
919 static void
920 process_peer (void *cls,
921               const struct GNUNET_PeerIdentity *peer,
922               const struct GNUNET_HELLO_Message *hello,
923               uint32_t trust)
924 {
925   struct PeerList *pos;
926
927   GNUNET_assert (peer != NULL);
928   if (0 == memcmp (&my_identity,
929                    peer, sizeof (struct GNUNET_PeerIdentity)))
930     return;  /* that's me! */
931   if (hello == NULL)
932     {
933       /* free existing HELLO, if any */
934       if (NULL != (pos = find_peer (peer)))
935         {
936           GNUNET_free_non_null (pos->hello);
937           pos->hello = NULL;
938           if (pos->filter != NULL)
939             {
940               GNUNET_CONTAINER_bloomfilter_free (pos->filter);
941               pos->filter = NULL;
942             }
943           if ( (! pos->is_connected) &&
944                (! pos->is_friend) &&
945                (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
946             free_peer (pos);
947         }
948       return;
949     }
950   consider_for_advertising (hello);
951   pos = find_peer (peer);  
952   if (pos == NULL)
953     pos = make_peer (peer, hello, GNUNET_NO);
954   GNUNET_assert (NULL != pos);
955 #if DEBUG_TOPOLOGY
956   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
957               "Considering connecting to peer `%s'\n",
958               GNUNET_i2s (peer));
959 #endif 
960   if (GNUNET_YES == pos->is_connected)
961     {
962 #if DEBUG_TOPOLOGY
963       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
964                   "Already connected to peer `%s'\n",
965                   GNUNET_i2s (peer));
966 #endif 
967       return;
968     }
969   if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
970     {
971 #if DEBUG_TOPOLOGY
972       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
973                   "Already tried peer `%s' recently\n",
974                   GNUNET_i2s (peer));
975 #endif 
976       return; /* peer still blacklisted */
977     }
978   if ( (GNUNET_YES == pos->is_friend) ||
979        (GNUNET_YES != friends_only) ||    
980        (friend_count >= minimum_friend_count) )
981     attempt_connect (pos);
982 }
983
984
985 /**
986  * Discard peer entries for blacklisted peers
987  * where the blacklisting has expired.
988  */
989 static void
990 discard_old_blacklist_entries (void *cls,
991                                const struct GNUNET_SCHEDULER_TaskContext *tc)
992 {
993   struct PeerList *pos;
994   struct PeerList *next;
995
996   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
997     return;
998   next = peers;
999   while (NULL != (pos = next))
1000     {
1001       next = pos->next;
1002       if ( (GNUNET_NO == pos->is_friend) &&
1003            (GNUNET_NO == pos->is_connected) &&
1004            (GNUNET_NO == pos->is_blocked) &&
1005            (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
1006         free_peer (pos);
1007     }
1008   GNUNET_SCHEDULER_add_delayed (sched,
1009                                 BLACKLIST_AFTER_ATTEMPT,
1010                                 &discard_old_blacklist_entries,
1011                                 NULL);
1012 }
1013
1014
1015 /**
1016  * Function called after GNUNET_CORE_connect has succeeded
1017  * (or failed for good).
1018  *
1019  * @param cls closure
1020  * @param server handle to the server, NULL if we failed
1021  * @param my_id ID of this peer, NULL if we failed
1022  * @param publicKey public key of this peer, NULL if we failed
1023  */
1024 static void
1025 core_init (void *cls,
1026            struct GNUNET_CORE_Handle * server,
1027            const struct GNUNET_PeerIdentity *
1028            my_id,
1029            const struct
1030            GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
1031            publicKey)
1032 {
1033   if (server == NULL)
1034     {
1035       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1036                   _("Failed to connect to core service, can not manage topology!\n"));
1037       GNUNET_SCHEDULER_shutdown (sched);
1038       return;
1039     }
1040   handle = server;
1041   my_identity = *my_id;
1042 #if DEBUG_TOPOLOGY
1043   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1044               "I am peer `%s'\n",
1045               GNUNET_i2s (my_id));
1046 #endif  
1047   GNUNET_SCHEDULER_add_delayed (sched,
1048                                 BLACKLIST_AFTER_ATTEMPT,
1049                                 &discard_old_blacklist_entries,
1050                                 NULL);
1051   peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
1052                                             &process_peer,
1053                                             NULL);
1054 }
1055
1056
1057 /**
1058  * Read the friends file.
1059  */
1060 static void
1061 read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
1062 {
1063   char *fn;
1064   char *data;
1065   size_t pos;
1066   struct GNUNET_PeerIdentity pid;
1067   struct stat frstat;
1068   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
1069   unsigned int entries_found;
1070   struct PeerList *fl;
1071
1072   if (GNUNET_OK !=
1073       GNUNET_CONFIGURATION_get_value_filename (cfg,
1074                                                "TOPOLOGY",
1075                                                "FRIENDS",
1076                                                &fn))
1077     {
1078       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1079                   _("Option `%s' in section `%s' not specified!\n"),
1080                   "FRIENDS",
1081                   "TOPOLOGY");
1082       return;
1083     }
1084   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
1085     GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
1086         | GNUNET_DISK_PERM_USER_WRITE);
1087   if (0 != STAT (fn, &frstat))
1088     {
1089       if ((friends_only) || (minimum_friend_count > 0))
1090         {
1091           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1092                       _("Could not read friends list `%s'\n"), fn);
1093           GNUNET_free (fn);
1094           return;
1095         }
1096     }
1097   if (frstat.st_size == 0)
1098     {
1099       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1100                   _("Friends file `%s' is empty.\n"),
1101                   fn);
1102       GNUNET_free (fn);
1103       return;
1104     }
1105   data = GNUNET_malloc_large (frstat.st_size);
1106   if (frstat.st_size !=
1107       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
1108     {
1109       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1110                   _("Failed to read friends list from `%s'\n"), fn);
1111       GNUNET_free (fn);
1112       GNUNET_free (data);
1113       return;
1114     }
1115   entries_found = 0;
1116   pos = 0;
1117   while ((pos < frstat.st_size) && isspace (data[pos]))
1118     pos++;
1119   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
1120          (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
1121     {
1122       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
1123       if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
1124         {
1125           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1126                       _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
1127                       (unsigned long long) pos);
1128           pos++;
1129           while ((pos < frstat.st_size) && (!isspace (data[pos])))
1130             pos++;
1131           continue;
1132         }
1133       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
1134       if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
1135         {
1136           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1137                       _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
1138                       (unsigned long long) pos,
1139                       &enc);
1140         }
1141       else
1142         {
1143           if (0 != memcmp (&pid,
1144                            &my_identity,
1145                            sizeof (struct GNUNET_PeerIdentity)))
1146             {
1147               entries_found++;
1148               fl = make_peer (&pid,
1149                               NULL,
1150                               GNUNET_YES);
1151               GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1152                           _("Found friend `%s' in configuration\n"),
1153                           GNUNET_i2s (&fl->id));
1154             }
1155           else
1156             {
1157               GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1158                           _("Found myself `%s' in friend list (useless, ignored)\n"),
1159                           GNUNET_i2s (&pid));
1160             }
1161         }
1162       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
1163       while ((pos < frstat.st_size) && isspace (data[pos]))
1164         pos++;
1165     }
1166   GNUNET_free (data);
1167   GNUNET_free (fn);
1168   GNUNET_STATISTICS_update (stats,
1169                             gettext_noop ("# friends in configuration"),
1170                             entries_found,
1171                             GNUNET_NO);
1172   if ( (minimum_friend_count > entries_found) &&
1173        (friends_only == GNUNET_NO) )
1174     {
1175       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1176                   _("Fewer friends specified than required by minimum friend count. Will only connect to friends.\n"));
1177     }
1178   if ( (minimum_friend_count > target_connection_count) &&
1179        (friends_only == GNUNET_NO) )
1180     {
1181       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1182                   _("More friendly connections required than target total number of connections.\n"));
1183     }
1184 }
1185
1186
1187 /**
1188  * This function is called whenever an encrypted HELLO message is
1189  * received.
1190  *
1191  * @param cls closure
1192  * @param other the other peer involved (sender or receiver, NULL
1193  *        for loopback messages where we are both sender and receiver)
1194  * @param message the actual HELLO message
1195  * @param latency reported latency of the connection with 'other'
1196  * @param distance reported distance (DV) to 'other' 
1197  * @return GNUNET_OK to keep the connection open,
1198  *         GNUNET_SYSERR to close it (signal serious error)
1199  */
1200 static int
1201 handle_encrypted_hello (void *cls,
1202                         const struct GNUNET_PeerIdentity * other,
1203                         const struct GNUNET_MessageHeader *
1204                         message,
1205                         struct GNUNET_TIME_Relative latency,
1206                         uint32_t distance)
1207 {
1208 #if DEBUG_TOPOLOGY
1209   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1210               "Received encrypted `%s' from peer `%s'",
1211               "HELLO",
1212               GNUNET_i2s (other));
1213 #endif  
1214   GNUNET_STATISTICS_update (stats,
1215                             gettext_noop ("# HELLO messages received"),
1216                             1,
1217                             GNUNET_NO);
1218   if (transport != NULL)
1219     GNUNET_TRANSPORT_offer_hello (transport,
1220                                   message);
1221   return GNUNET_OK;
1222 }
1223
1224
1225 /**
1226  * Function to fill send buffer with HELLO.
1227  *
1228  * @param cls 'struct PeerList' of the target peer
1229  * @param size number of bytes available in buf
1230  * @param buf where the callee should write the message
1231  * @return number of bytes written to buf
1232  */
1233 static size_t
1234 hello_advertising_ready (void *cls,
1235                          size_t size,
1236                          void *buf)
1237 {
1238   struct PeerList *pl = cls;
1239   struct PeerList *pos; 
1240   struct PeerList *next;
1241   uint16_t want;
1242   size_t hs;
1243
1244   pl->hello_req = NULL;
1245 #if DEBUG_TOPOLOGY
1246   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1247               "Data solicited for `%s', considering sending `%s'",
1248               GNUNET_i2s (&pl->id),
1249               "HELLO");
1250 #endif  
1251   /* find applicable HELLOs */
1252   next = peers;
1253   while (NULL != (pos = next))
1254     {
1255       next = pos->next;
1256       if (pos->hello == NULL)
1257         continue;
1258       if (0 == GNUNET_TIME_absolute_get_remaining (pos->filter_expiration).value)
1259         {
1260           /* time to discard... */
1261           GNUNET_CONTAINER_bloomfilter_free (pos->filter);
1262           setup_filter (pos);
1263         }
1264       if (GNUNET_NO ==
1265           GNUNET_CONTAINER_bloomfilter_test (pos->filter,
1266                                              &pl->id.hashPubKey))
1267         break;
1268     }
1269   want = 0;
1270   if (pos != NULL)
1271     {
1272       hs = GNUNET_HELLO_size (pos->hello);
1273       if (hs < size)
1274         {
1275           want = hs;
1276           memcpy (buf, pos->hello, want);
1277           GNUNET_CONTAINER_bloomfilter_add (pos->filter,
1278                                             &pl->id.hashPubKey);
1279 #if DEBUG_TOPOLOGY
1280           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1281                       "Sending %u bytes of `%s's",
1282                       (unsigned int) want,
1283                       "HELLO");
1284 #endif  
1285           GNUNET_STATISTICS_update (stats,
1286                                     gettext_noop ("# HELLO messages gossipped"),
1287                                     1,
1288                                     GNUNET_NO);
1289         }
1290     }
1291   pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
1292   pl->hello_delay_task 
1293     = GNUNET_SCHEDULER_add_now (sched,
1294                                 &schedule_next_hello,
1295                                 pl);
1296   return want;
1297 }
1298
1299
1300 /**
1301  * Last task run during shutdown.  Disconnects us from
1302  * the transport and core.
1303  */
1304 static void
1305 cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1306 {
1307   struct DisconnectList *dl;
1308
1309   if (NULL != peerinfo_notify)
1310     {
1311       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
1312       peerinfo_notify = NULL;
1313     }
1314   GNUNET_TRANSPORT_disconnect (transport);
1315   transport = NULL;
1316   while (NULL != peers)
1317     free_peer (peers);     
1318   if (handle != NULL)
1319     {
1320       GNUNET_CORE_disconnect (handle);
1321       handle = NULL;
1322     }
1323   while (NULL != (dl = disconnect_head))
1324     {
1325       GNUNET_CONTAINER_DLL_remove (disconnect_head,
1326                                    disconnect_tail,
1327                                    dl);
1328       GNUNET_TRANSPORT_blacklist_cancel (dl->rh);
1329       GNUNET_free (dl);
1330     }
1331   if (stats != NULL)
1332     {
1333       GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
1334       stats = NULL;
1335     }
1336 }
1337
1338
1339 /**
1340  * Main function that will be run.
1341  *
1342  * @param cls closure
1343  * @param s the scheduler to use
1344  * @param args remaining command-line arguments
1345  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1346  * @param c configuration
1347  */
1348 static void
1349 run (void *cls,
1350      struct GNUNET_SCHEDULER_Handle * s,
1351      char *const *args,
1352      const char *cfgfile,
1353      const struct GNUNET_CONFIGURATION_Handle * c)
1354 {
1355   struct GNUNET_CORE_MessageHandler handlers[] =
1356     {
1357       { &handle_encrypted_hello, GNUNET_MESSAGE_TYPE_HELLO, 0},
1358       { NULL, 0, 0 }
1359     };
1360   unsigned long long opt;
1361
1362   sched = s;
1363   cfg = c;
1364   stats = GNUNET_STATISTICS_create (sched, "topology", cfg);
1365   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1366                                                       "TOPOLOGY",
1367                                                       "AUTOCONNECT");
1368   friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1369                                                        "TOPOLOGY",
1370                                                        "FRIENDS-ONLY");
1371   if (GNUNET_OK !=
1372       GNUNET_CONFIGURATION_get_value_number (cfg,
1373                                              "TOPOLOGY",
1374                                              "MINIMUM-FRIENDS",
1375                                              &opt))
1376     opt = 0;
1377   minimum_friend_count = (unsigned int) opt;
1378   if (GNUNET_OK !=
1379       GNUNET_CONFIGURATION_get_value_number (cfg,
1380                                              "TOPOLOGY",
1381                                              "TARGET-CONNECTION-COUNT",
1382                                              &opt))
1383     opt = 16;
1384   target_connection_count = (unsigned int) opt;
1385
1386   if ( (friends_only == GNUNET_YES) ||
1387        (minimum_friend_count > 0) )
1388     read_friends_file (cfg);
1389 #if DEBUG_TOPOLOGY
1390   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1391               "Topology would like %u connections with at least %u friends (%s)\n",
1392               target_connection_count,
1393               minimum_friend_count,
1394               autoconnect ? "autoconnect enabled" : "autoconnect disabled");
1395 #endif       
1396   transport = GNUNET_TRANSPORT_connect (sched,
1397                                         cfg,
1398                                         NULL,
1399                                         NULL,
1400                                         NULL,
1401                                         NULL);
1402   handle = GNUNET_CORE_connect (sched,
1403                                 cfg,
1404                                 GNUNET_TIME_UNIT_FOREVER_REL,
1405                                 NULL,
1406                                 &core_init,
1407                                 NULL,
1408                                 &connect_notify,
1409                                 &disconnect_notify,
1410                                 NULL, GNUNET_NO,
1411                                 NULL, GNUNET_NO,
1412                                 handlers);
1413   GNUNET_SCHEDULER_add_delayed (sched,
1414                                 GNUNET_TIME_UNIT_FOREVER_REL,
1415                                 &cleaning_task, NULL);
1416   if (NULL == transport)
1417     {
1418       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1419                   _("Failed to connect to `%s' service.\n"),
1420                   "transport");
1421       GNUNET_SCHEDULER_shutdown (sched);
1422       return;
1423     }
1424   if (NULL == handle)
1425     {
1426       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1427                   _("Failed to connect to `%s' service.\n"),
1428                   "core");
1429       GNUNET_SCHEDULER_shutdown (sched);
1430       return;
1431     }
1432 }
1433
1434
1435 /**
1436  * gnunet-daemon-topology command line options.
1437  */
1438 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1439   GNUNET_GETOPT_OPTION_END
1440 };
1441
1442
1443 /**
1444  * The main function for the topology daemon.
1445  *
1446  * @param argc number of arguments from the command line
1447  * @param argv command line arguments
1448  * @return 0 ok, 1 on error
1449  */
1450 int
1451 main (int argc, char *const *argv)
1452 {
1453   int ret;
1454
1455   ret = (GNUNET_OK ==
1456          GNUNET_PROGRAM_run (argc,
1457                              argv,
1458                              "topology",
1459                              _("GNUnet topology control (maintaining P2P mesh and F2F constraints)"),
1460                              options,
1461                              &run, NULL)) ? 0 : 1;
1462   return ret;
1463 }
1464
1465 /* end of gnunet-daemon-topology.c */