retry faster
[oweals/gnunet.git] / src / topology / gnunet-daemon-topology.c
index 5dedeb42a7a66f8cbb1296cab79a0a64898b63bf..08b1ba1c3c0effb852fa29cbd34b8880afc645ca 100644 (file)
@@ -4,7 +4,7 @@
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
 
      GNUnet is free software; you can redistribute it and/or modify
      it under the terms of the GNU General Public License as published
-     by the Free Software Foundation; either version 2, or (at your
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
 
 #include <stdlib.h>
 #include "platform.h"
 
 #include <stdlib.h>
 #include "platform.h"
+#include "gnunet_constants.h"
 #include "gnunet_core_service.h"
 #include "gnunet_protocols.h"
 #include "gnunet_peerinfo_service.h"
 #include "gnunet_core_service.h"
 #include "gnunet_protocols.h"
 #include "gnunet_peerinfo_service.h"
+#include "gnunet_statistics_service.h"
 #include "gnunet_transport_service.h"
 #include "gnunet_util_lib.h"
 
 
 #include "gnunet_transport_service.h"
 #include "gnunet_util_lib.h"
 
 
-#define DEBUG_TOPOLOGY GNUNET_NO
+#define DEBUG_TOPOLOGY GNUNET_YES
 
 /**
 
 /**
- * For how long do we blacklist a peer after a failed
- * connection attempt?
+ * For how long do we blacklist a peer after a failed connection
+ * attempt?
  */
  */
-#define BLACKLIST_AFTER_ATTEMPT GNUNET_TIME_UNIT_HOURS
+#define GREYLIST_AFTER_ATTEMPT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
 
 /**
 
 /**
- * For how long do we blacklist a friend after a failed
- * connection attempt?
+ * For how long do we blacklist a friend after a failed connection
+ * attempt?
  */
  */
-#define BLACKLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 15)
+#define GREYLIST_AFTER_ATTEMPT_FRIEND GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
 
 /**
 
 /**
- * How frequently are we allowed to ask PEERINFO for more
- * HELLO's to advertise (at most)?
+ * For how long do we blacklist anyone under any cirumstances after a failed connection
+ * attempt?
  */
  */
-#define MIN_HELLO_GATHER_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 27)
+#define GREYLIST_AFTER_ATTEMPT_MIN GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
+
+/**
+ * For how long do we blacklist anyone under any cirumstances after a failed connection
+ * attempt?
+ */
+#define GREYLIST_AFTER_ATTEMPT_MAX GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 1)
+
+/**
+ * How often do we at most advertise any HELLO to a peer?
+ */
+#define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
 
 /**
  * How often do we at most advertise the same HELLO to the same peer?
 
 /**
  * How often do we at most advertise the same HELLO to the same peer?
- * Also used to remove HELLOs of peers that PEERINFO no longer lists
- * from our cache.
  */
  */
-#define HELLO_ADVERTISEMENT_MIN_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 12)
+#define HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
 
 
 /**
 
 
 /**
- * List of neighbours, friends and blacklisted peers.
+ * Record for neighbours, friends and blacklisted peers.
  */
  */
-struct PeerList
+struct Peer
 {
 {
+  /**
+   * Which peer is this entry about?
+   */
+  struct GNUNET_PeerIdentity pid;
 
   /**
 
   /**
-   * This is a linked list.
+   * Our handle for the request to transmit HELLOs to this peer; NULL
+   * if no such request is pending.
    */
    */
-  struct PeerList *next;
+  struct GNUNET_CORE_TransmitHandle *hello_req;  
 
   /**
 
   /**
-   * Is this peer listed here because he is a friend?
+   * Our handle for the request to connect to this peer; NULL if no
+   * such request is pending.
    */
    */
-  int is_friend;
+  struct GNUNET_CORE_PeerRequestHandle *connect_req;  
 
   /**
 
   /**
-   * Are we connected to this peer right now?
+   * Pointer to the HELLO message of this peer; can be NULL.
    */
    */
-  int is_connected;
+  struct GNUNET_HELLO_Message *hello;
+
+  /**
+   * Bloom filter used to mark which peers already got the HELLO
+   * from this peer.
+   */
+  struct GNUNET_CONTAINER_BloomFilter *filter;
 
   /**
    * Until what time should we not try to connect again
    * to this peer?
    */
 
   /**
    * Until what time should we not try to connect again
    * to this peer?
    */
-  struct GNUNET_TIME_Absolute blacklisted_until;
+  struct GNUNET_TIME_Absolute greylisted_until;
 
   /**
 
   /**
-   * Last time we transmitted a HELLO to this peer?
+   * Next time we are allowed to transmit a HELLO to this peer?
    */
    */
-  struct GNUNET_TIME_Absolute last_hello_sent;
+  struct GNUNET_TIME_Absolute next_hello_allowed;
 
   /**
 
   /**
-   * ID of the peer.
+   * When should we reset the bloom filter of this entry?
    */
    */
-  struct GNUNET_PeerIdentity id;
-
-};
+  struct GNUNET_TIME_Absolute filter_expiration;
 
 
-
-/**
- * List of HELLOs we may consider for advertising.
- */
-struct HelloList
-{
   /**
   /**
-   * This is a linked list.
+   * ID of task we use to wait for the time to send the next HELLO
+   * to this peer.
    */
    */
-  struct HelloList *next;
+  GNUNET_SCHEDULER_TaskIdentifier hello_delay_task;
 
   /**
 
   /**
-   * Pointer to the HELLO message.  Memory allocated as part
-   * of the "struct HelloList" --- do not free!
+   * ID of task we use to clear peers from the greylist.
    */
    */
-  struct GNUNET_HELLO_Message *msg;
+  GNUNET_SCHEDULER_TaskIdentifier greylist_clean_task;
 
   /**
 
   /**
-   * Bloom filter used to mark which peers already got
-   * this HELLO.
+   * How often have we tried so far?
    */
    */
-  struct GNUNET_CONTAINER_BloomFilter *filter;
+  unsigned int connect_attempts;
 
   /**
 
   /**
-   * What peer is this HELLO for?
+   * Is this peer listed here because he is a friend?
    */
    */
-  struct GNUNET_PeerIdentity id;
+  int is_friend;
 
   /**
 
   /**
-   * When should we remove this entry from the linked list (either
-   * resetting the filter or possibly eliminating it for good because
-   * we no longer consider the peer to be participating in the
-   * network)?
+   * Are we connected to this peer right now?
    */
    */
-  struct GNUNET_TIME_Absolute expiration;
+  int is_connected;
+
 };
 
 
 /**
  * Our peerinfo notification context.  We use notification
 };
 
 
 /**
  * Our peerinfo notification context.  We use notification
- * to instantly learn about new peers as they are discovered
- * as well as periodic iteration to try peers again after
- * a while.
+ * to instantly learn about new peers as they are discovered.
  */
 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
 
  */
 static struct GNUNET_PEERINFO_NotifyContext *peerinfo_notify;
 
-/**
- * Linked list of HELLOs for advertising.
- */
-static struct HelloList *hellos;
-
-/**
- * Our scheduler.
- */
-static struct GNUNET_SCHEDULER_Handle * sched;
-
 /**
  * Our configuration.
  */
 /**
  * Our configuration.
  */
-static const struct GNUNET_CONFIGURATION_Handle * cfg;
+static const struct GNUNET_CONFIGURATION_Handle *cfg;
 
 /**
  * Handle to the core API.
 
 /**
  * Handle to the core API.
@@ -177,15 +177,21 @@ static struct GNUNET_TRANSPORT_Handle *transport;
 static struct GNUNET_PeerIdentity my_identity;
 
 /**
 static struct GNUNET_PeerIdentity my_identity;
 
 /**
- * Linked list of all of our friends and all of our current
- * neighbours.
+ * All of our friends, all of our current neighbours and all peers for
+ * which we have HELLOs.  So pretty much everyone.  Maps peer identities
+ * to 'struct Peer *' values.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *peers;
+
+/**
+ * Handle for reporting statistics.
  */
  */
-static struct PeerList *friends;
+static struct GNUNET_STATISTICS_Handle *stats;
 
 /**
 
 /**
- * Timestamp from the last time we tried to gather HELLOs.
+ * Blacklist (NULL if we have none).
  */
  */
-static struct GNUNET_TIME_Absolute last_hello_gather_time;
+static struct GNUNET_TRANSPORT_Blacklist *blacklist;
 
 /**
  * Flag to disallow non-friend connections (pure F2F mode).
 
 /**
  * Flag to disallow non-friend connections (pure F2F mode).
@@ -218,242 +224,463 @@ static unsigned int friend_count;
  */
 static int autoconnect;
 
  */
 static int autoconnect;
 
+
 /**
 /**
- * Non-NULL if we are currently having a request pending with
- * PEERINFO asking for HELLOs for advertising?
+ * Function that decides if a connection is acceptable or not.  
+ * If we have a blacklist, only friends are allowed, so the check
+ * is rather simple.
+ *
+ * @param cls closure
+ * @param pid peer to approve or disapproave
+ * @return GNUNET_OK if the connection is allowed
  */
  */
-static struct GNUNET_PEERINFO_IteratorContext *pitr;
+static int
+blacklist_check (void *cls,
+                const struct GNUNET_PeerIdentity *pid)
+{
+  struct Peer *pos;
+
+  pos = GNUNET_CONTAINER_multihashmap_get (peers, &pid->hashPubKey);
+  if ( (pos != NULL) &&
+       (pos->is_friend == GNUNET_YES) )
+    return GNUNET_OK;
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# peers blacklisted"),
+                           1,
+                           GNUNET_NO);
+  return GNUNET_SYSERR;
+}
+
 
 /**
 
 /**
- * Non-NULL if we are currently having a request pending with
- * PEERINFO looking for more peers to connect to.
+ * Whitelist all peers that we blacklisted; we've passed
+ * the minimum number of friends.
  */
  */
-static struct GNUNET_PEERINFO_IteratorContext *pitr_more;
+static void
+whitelist_peers ()
+{
+  if (blacklist != NULL)
+    {
+      GNUNET_TRANSPORT_blacklist_cancel (blacklist);
+      blacklist = NULL;
+    }
+}
 
 
 /**
 
 
 /**
- * Entry in linked list of active 'disconnect' requests that we have issued.
+ * Function called by core when our attempt to connect succeeded.
+ *
+ * @param cls the 'struct Peer' for which we issued the connect request
+ * @param success was the request transmitted
  */
  */
-struct DisconnectList
+static void
+connect_completed_callback (void *cls,
+                           int success)
 {
 {
-  /**
-   * This is a doubly-linked list.
-   */
-  struct DisconnectList *next;
+  struct Peer *pos = cls;
 
 
-  /**
-   * This is a doubly-linked list.
-   */
-  struct DisconnectList *prev;
-  
-  /**
-   * Our request handle.
-   */
-  struct GNUNET_CORE_InformationRequestContext *rh;
+  pos->connect_req = NULL;
+}
 
 
-  /**
-   * Peer we tried to disconnect.
-   */
-  struct GNUNET_PeerIdentity peer;
 
 
-};
+/**
+ * Check if an additional connection from the given peer is allowed.
+ * 
+ * @param peer connection to check
+ * @return GNUNET_OK if the connection is allowed
+ */
+static int
+is_connection_allowed (struct Peer *peer)
+{
+  if (0 == memcmp (&my_identity, 
+                  &peer->pid, 
+                  sizeof (struct GNUNET_PeerIdentity)))
+    return GNUNET_SYSERR;       /* disallow connections to self */
+  if (peer->is_friend)
+    return GNUNET_OK;
+  if (GNUNET_YES == friends_only)
+    {
+#if DEBUG_TOPOLOGY
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Determined that `%s' is not allowed to connect (not a friend)\n",
+                 GNUNET_i2s (&peer->pid));
+#endif       
+      return GNUNET_SYSERR;
+    }
+  if (friend_count >= minimum_friend_count)
+    return GNUNET_OK;
+#if DEBUG_TOPOLOGY
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
+             GNUNET_i2s (&peer->pid));
+#endif       
+  return GNUNET_SYSERR;
+}
 
 
 /**
 
 
 /**
- * Head of doubly-linked list of active 'disconnect' requests that we have issued.
+ * Free all resources associated with the given peer.
+ *
+ * @param cls closure (not used)
+ * @param pid identity of the peer
+ * @param value peer to free
+ * @return GNUNET_YES (always: continue to iterate)
  */
  */
-static struct DisconnectList *disconnect_head;
+static int
+free_peer (void *cls,
+          const GNUNET_HashCode *pid,
+          void *value)
+{
+  struct Peer *pos = value;
+
+  GNUNET_break (GNUNET_OK == 
+               GNUNET_CONTAINER_multihashmap_remove (peers,
+                                                     pid,
+                                                     pos));
+  if (pos->hello_req != NULL)
+    GNUNET_CORE_notify_transmit_ready_cancel (pos->hello_req);
+  if (pos->connect_req != NULL)
+    GNUNET_CORE_peer_request_connect_cancel (pos->connect_req);              
+  if (pos->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (pos->hello_delay_task);
+  if (pos->greylist_clean_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (pos->greylist_clean_task);
+  GNUNET_free_non_null (pos->hello);   
+  if (pos->filter != NULL)
+    GNUNET_CONTAINER_bloomfilter_free (pos->filter);
+  GNUNET_free (pos);
+  return GNUNET_YES;
+}
+
 
 /**
 
 /**
- * Head of doubly-linked list of active 'disconnect' requests that we have issued.
+ * Discard peer entries for greylisted peers
+ * where the greylisting has expired.
+ *
+ * @param cls 'struct Peer' to greylist
+ * @param tc scheduler context
  */
  */
-static struct DisconnectList *disconnect_tail;
+static void
+remove_from_greylist (void *cls,
+                     const struct GNUNET_SCHEDULER_TaskContext *tc);
 
 
 /**
 
 
 /**
- * Function called once our request to 'disconnect' a peer
- * has completed.
+ * Try to connect to the specified peer.
  *
  *
- * @param cls our 'struct DisconnectList'
- * @param peer NULL on error (then what?)
- * @param bpm_in set to the current bandwidth limit (receiving) for this peer
- * @param bpm_out set to the current bandwidth limit (sending) for this peer
- * @param latency current latency estimate, "FOREVER" if we have been
- *                disconnected
- * @param amount set to the amount that was actually reserved or unreserved
- * @param preference current traffic preference for the given peer
+ * @param pos peer to connect to
  */
 static void
  */
 static void
-disconnect_done (void *cls,
-                const struct
-                GNUNET_PeerIdentity * peer,
-                unsigned int bpm_in,
-                unsigned int bpm_out,
-                struct GNUNET_TIME_Relative
-                latency, int amount,
-                unsigned long long preference)
+attempt_connect (struct Peer *pos)
 {
 {
-  struct DisconnectList *dl = cls;
-
-  GNUNET_CONTAINER_DLL_remove (disconnect_head,
-                              disconnect_tail,
-                              dl);
-  GNUNET_free (dl);
+  struct GNUNET_TIME_Relative rem;
+  
+  if ( (connection_count >= target_connection_count) &&
+       (friend_count >= minimum_friend_count) )
+    return;
+  if (GNUNET_YES == pos->is_connected)
+    return;
+  if (GNUNET_OK != is_connection_allowed (pos))
+    return;
+  if (GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).rel_value > 0)
+    return;
+  if (GNUNET_YES == pos->is_friend)
+    rem = GREYLIST_AFTER_ATTEMPT_FRIEND;
+  else
+    rem = GREYLIST_AFTER_ATTEMPT;
+  rem = GNUNET_TIME_relative_multiply (rem, connection_count);
+  rem = GNUNET_TIME_relative_divide (rem, target_connection_count);
+  if (pos->connect_attempts > 30)
+    pos->connect_attempts = 30;
+  rem = GNUNET_TIME_relative_multiply (rem, 1 << (++pos->connect_attempts));
+  rem = GNUNET_TIME_relative_max (rem,
+                                 GREYLIST_AFTER_ATTEMPT_MIN);
+  rem = GNUNET_TIME_relative_min (rem,
+                                 GREYLIST_AFTER_ATTEMPT_MAX);
+  pos->greylisted_until = GNUNET_TIME_relative_to_absolute (rem);
+  if (pos->greylist_clean_task != GNUNET_SCHEDULER_NO_TASK)
+    GNUNET_SCHEDULER_cancel (pos->greylist_clean_task);
+  pos->greylist_clean_task 
+    = GNUNET_SCHEDULER_add_delayed (rem,
+                                   &remove_from_greylist,
+                                   pos);
+#if DEBUG_TOPOLOGY
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Asking  to connect to `%s'\n",
+             GNUNET_i2s (&pos->pid));
+#endif
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# connect requests issued to core"),
+                           1,
+                           GNUNET_NO);
+  pos->connect_req = GNUNET_CORE_peer_request_connect (handle,
+                                                      GNUNET_TIME_UNIT_MINUTES,
+                                                      &pos->pid,
+                                                      &connect_completed_callback,
+                                                      pos);
 }
 
 
 /**
 }
 
 
 /**
- * Force a disconnect from the specified peer.
+ * Discard peer entries for greylisted peers
+ * where the greylisting has expired.
+ *
+ * @param cls 'struct Peer' to greylist
+ * @param tc scheduler context
  */
 static void
  */
 static void
-force_disconnect (const struct GNUNET_PeerIdentity *peer)
+remove_from_greylist (void *cls,
+                     const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct DisconnectList *dl;
-
-  dl = GNUNET_malloc (sizeof (struct DisconnectList));
-  dl->peer = *peer;
-  GNUNET_CONTAINER_DLL_insert (disconnect_head,
-                              disconnect_tail,
-                              dl);
-  dl->rh = GNUNET_CORE_peer_get_info (sched, cfg,
-                                     peer,
-                                     GNUNET_TIME_UNIT_FOREVER_REL,
-                                     0,
-                                     0,
-                                     0,
-                                     &disconnect_done,
-                                     dl);
+  struct Peer *pos = cls;
+  struct GNUNET_TIME_Relative rem;
+
+  pos->greylist_clean_task = GNUNET_SCHEDULER_NO_TASK;
+  rem = GNUNET_TIME_absolute_get_remaining (pos->greylisted_until);
+  if (rem.rel_value == 0)
+    {
+      attempt_connect (pos);
+    }
+  else
+    {
+      pos->greylist_clean_task 
+       = GNUNET_SCHEDULER_add_delayed (rem,
+                                       &remove_from_greylist,
+                                       pos);
+    }
+  if ( (GNUNET_NO == pos->is_friend) &&
+       (GNUNET_NO == pos->is_connected) )
+    {
+      free_peer (NULL, &pos->pid.hashPubKey, pos);
+      return;
+    }
 }
 
 
 /**
 }
 
 
 /**
- * Function called by core when our attempt to connect
- * succeeded.  Does nothing.
+ * Create a new entry in the peer list.
+ *
+ * @param peer identity of the new entry
+ * @param hello hello message, can be NULL
+ * @param is_friend is the new entry for a friend?
+ * @return the new entry
  */
  */
-static size_t
-ready_callback (void *cls,
-               size_t size, void *buf)
+static struct Peer *
+make_peer (const struct
+          GNUNET_PeerIdentity * peer,
+          const struct GNUNET_HELLO_Message *hello,
+          int is_friend)
 {
 {
-  struct GNUNET_MessageHeader hdr;
-  if (buf == NULL)
+  struct Peer *ret;
+  
+  ret = GNUNET_malloc (sizeof (struct Peer));
+  ret->pid = *peer;
+  ret->is_friend = is_friend;
+  if (hello != NULL)
     {
     {
-#if DEBUG_TOPOLOGY
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Core told us that our attempt to connect failed.\n");
-#endif
-      return 0;
+      ret->hello = GNUNET_malloc (GNUNET_HELLO_size (hello));
+      memcpy (ret->hello, hello,
+             GNUNET_HELLO_size (hello));
     }
     }
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Sending dummy message to establish connection.\n");
-#endif
-  hdr.size = htons (sizeof (struct GNUNET_MessageHeader));
-  hdr.type = htons (GNUNET_MESSAGE_TYPE_TOPOLOGY_DUMMY);
-  memcpy (buf, &hdr, sizeof (struct GNUNET_MessageHeader));
-  return sizeof (struct GNUNET_MessageHeader);
+  GNUNET_break (GNUNET_OK ==
+               GNUNET_CONTAINER_multihashmap_put (peers,
+                                                  &peer->hashPubKey,
+                                                  ret,
+                                                  GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  return ret;
 }
 
 
 /**
 }
 
 
 /**
- * Try to connect to the specified peer.
+ * Setup bloom filter for the given peer entry.
  *
  *
- * @param peer who we should try to connect to
- * @param pos entry in our friend list; NULL if not in friend list yet
+ * @param peer entry to initialize
  */
 static void
  */
 static void
-attempt_connect (const struct GNUNET_PeerIdentity *peer,
-                struct PeerList *pos)
+setup_filter (struct Peer *peer)
 {
 {
-  if (pos == NULL)
-    {
-      pos = friends;
-      while (pos != NULL)
-       {
-         if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
-           break;
-         pos = pos->next;
-       }
-    }
-  if (pos == NULL)
-    {
-      pos = GNUNET_malloc (sizeof(struct PeerList));
-      pos->id = *peer;
-      pos->next = friends;
-      friends = pos;
-    }
-  if (GNUNET_YES == pos->is_friend)
-    pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT_FRIEND);
-  else
-    pos->blacklisted_until = GNUNET_TIME_relative_to_absolute (BLACKLIST_AFTER_ATTEMPT);
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Asking core to connect to `%s'\n",
-             GNUNET_i2s (peer));
-#endif
-  GNUNET_CORE_notify_transmit_ready (handle,
-                                    0 /* priority */,
-                                    GNUNET_TIME_UNIT_MINUTES,
-                                    peer,
-                                    sizeof(struct GNUNET_MessageHeader),
-                                    &ready_callback,
-                                    NULL);
+  /* 2^{-5} chance of not sending a HELLO to a peer is
+     acceptably small (if the filter is 50% full);
+     64 bytes of memory are small compared to the rest
+     of the data structure and would only really become
+     "useless" once a HELLO has been passed on to ~100
+     other peers, which is likely more than enough in
+     any case; hence 64, 5 as bloomfilter parameters. */
+  peer->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
+  peer->filter_expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_REPEAT_FREQUENCY);
+  /* never send a peer its own HELLO */
+  GNUNET_CONTAINER_bloomfilter_add (peer->filter, &peer->pid.hashPubKey);
 }
 
 
 /**
 }
 
 
 /**
- * Is this peer one of our friends?
+ * Function to fill send buffer with HELLO.
+ *
+ * @param cls 'struct Peer' of the target peer
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+hello_advertising_ready (void *cls,
+                        size_t size,
+                        void *buf);
+
+
+
+
+/**
+ * Closure for 'find_advertisable_hello'.
+ */
+struct FindAdvHelloContext {
+
+  /**
+   * Peer we want to advertise to.
+   */
+  struct Peer *peer;
+
+  /**
+   * Where to store the result (peer selected for advertising).
+   */
+  struct Peer *result;
+
+  /**
+   * Maximum HELLO size we can use right now.
+   */
+  size_t max_size;
+
+  struct GNUNET_TIME_Relative next_adv;
+};
+
+
+/**
+ * Find a peer that would be reasonable for advertising.
+ *
+ * @param cls closure
+ * @param pid identity of a peer
+ * @param value 'struct Peer*' for the peer we are considering 
+ * @return GNUNET_YES (continue iteration)
  */
 static int
  */
 static int
-is_friend (const struct GNUNET_PeerIdentity * peer)
+find_advertisable_hello (void *cls,
+                        const GNUNET_HashCode *pid,
+                        void *value)
 {
 {
-  struct PeerList *pos;
+  struct FindAdvHelloContext *fah = cls;
+  struct Peer *pos = value;
+  struct GNUNET_TIME_Relative rst_time;
+  size_t hs;
+
+  if (pos == fah->peer)
+    return GNUNET_YES;
+  if (pos->hello == NULL)
+    return GNUNET_YES;
+  rst_time = GNUNET_TIME_absolute_get_remaining (pos->filter_expiration);
+  if (0 == rst_time.rel_value)
+    {
+      /* time to discard... */
+      GNUNET_CONTAINER_bloomfilter_free (pos->filter);
+      setup_filter (pos);
+    }
+  fah->next_adv = GNUNET_TIME_relative_min (rst_time,
+                                           fah->next_adv);
+  hs = GNUNET_HELLO_size (pos->hello);
+  if (hs > fah->max_size)
+    return GNUNET_YES;
+  if (GNUNET_NO ==
+      GNUNET_CONTAINER_bloomfilter_test (pos->filter,
+                                        &fah->peer->pid.hashPubKey))
+    fah->result = pos;    
+  return GNUNET_YES;
+}
+
 
 
-  pos = friends;
-  while (pos != NULL)
+/**
+ * Calculate when we would like to send the next HELLO to this
+ * peer and ask for it.
+ *
+ * @param cls for which peer to schedule the HELLO
+ * @param tc task context
+ */
+static void
+schedule_next_hello (void *cls,
+                    const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct Peer *pl = cls;
+  struct FindAdvHelloContext fah;
+  size_t next_want;
+  struct GNUNET_TIME_Relative delay;
+  pl->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
+  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+    return; /* we're out of here */
+  if (pl->hello_req != NULL)
+    return; /* did not finish sending the previous one */
+  /* find applicable HELLOs */
+  fah.peer = pl;
+  fah.result = NULL;
+  fah.max_size = GNUNET_SERVER_MAX_MESSAGE_SIZE - 1;
+  fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                        &find_advertisable_hello,
+                                        &fah);
+  pl->hello_delay_task 
+    = GNUNET_SCHEDULER_add_delayed (fah.next_adv,
+                                   &schedule_next_hello,
+                                   pl);
+  if (fah.result == NULL)
+    return;   
+  next_want = GNUNET_HELLO_size (fah.result->hello);
+  delay = GNUNET_TIME_absolute_get_remaining (pl->next_hello_allowed);
+  if (delay.rel_value == 0)
     {
     {
-      if ( (GNUNET_YES == pos->is_friend) &&
-          (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
-       {
-#if DEBUG_TOPOLOGY
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Determined that `%s' is a friend\n",
-                     GNUNET_i2s (peer));
-#endif   
-         return GNUNET_YES;
-       }
-      pos = pos->next;
+      /* now! */
+      pl->hello_req = GNUNET_CORE_notify_transmit_ready (handle, 0,
+                                                        GNUNET_CONSTANTS_SERVICE_TIMEOUT,
+                                                        &pl->pid,
+                                                        next_want,
+                                                        &hello_advertising_ready,
+                                                        pl);
     }
     }
-  return GNUNET_NO;
 }
 
 
 /**
 }
 
 
 /**
- * Check if an additional connection from the given peer is allowed.
+ * Cancel existing requests for sending HELLOs to this peer
+ * and recalculate when we should send HELLOs to it based
+ * on our current state (something changed!).
+ *
+ * @param cls closure, 'struct Peer' to skip, or NULL
+ * @param pid identity of a peer
+ * @param value 'struct Peer*' for the peer
+ * @return GNUNET_YES (always)
  */
 static int
  */
 static int
-is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
+reschedule_hellos (void *cls,
+                  const GNUNET_HashCode *pid,
+                  void *value)
 {
 {
-  if (0 == memcmp (&my_identity, peer, sizeof (struct GNUNET_PeerIdentity)))
-    return GNUNET_SYSERR;       /* disallow connections to self */
-  if (is_friend (peer))
-    return GNUNET_OK;
-  if (GNUNET_YES == friends_only)
+  struct Peer *peer = value;
+  struct Peer *skip = cls;
+
+  if (skip == peer)
+    return GNUNET_YES;
+  if (! peer->is_connected) 
+    return GNUNET_YES;
+  if (peer->hello_req != NULL)
     {
     {
-#if DEBUG_TOPOLOGY
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Determined that `%s' is not allowed to connect (not a friend)\n",
-                 GNUNET_i2s (peer));
-#endif       
-      return GNUNET_SYSERR;
+      GNUNET_CORE_notify_transmit_ready_cancel (peer->hello_req);
+      peer->hello_req = NULL;
     }
     }
-  if (friend_count >= minimum_friend_count)
-    return GNUNET_OK;
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Determined that `%s' is not allowed to connect (not enough connected friends)\n",
-             GNUNET_i2s (peer));
-#endif       
-  return GNUNET_SYSERR;
+  if (peer->hello_delay_task != GNUNET_SCHEDULER_NO_TASK)
+    {
+      GNUNET_SCHEDULER_cancel (peer->hello_delay_task);
+      peer->hello_delay_task = GNUNET_SCHEDULER_NO_TASK;
+    }
+  peer->hello_delay_task 
+    = GNUNET_SCHEDULER_add_now (&schedule_next_hello,
+                               peer);
+  return GNUNET_YES;
 }
 
 
 }
 
 
@@ -462,73 +689,74 @@ is_connection_allowed (const struct GNUNET_PeerIdentity * peer)
  *
  * @param cls closure
  * @param peer peer identity this notification is about
  *
  * @param cls closure
  * @param peer peer identity this notification is about
+ * @param atsi performance data
  */
  */
-static void connect_notify (void *cls,
-                           const struct
-                           GNUNET_PeerIdentity * peer)
+static void 
+connect_notify (void *cls,
+               const struct
+               GNUNET_PeerIdentity * peer,
+               const struct GNUNET_TRANSPORT_ATS_Information *atsi)
 {
 {
-  struct PeerList *pos;
+  struct Peer *pos;
 
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core told us that we are connecting to `%s'\n",
              GNUNET_i2s (peer));
 
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core told us that we are connecting to `%s'\n",
              GNUNET_i2s (peer));
-#endif       
+#endif
+  if (0 == memcmp(&my_identity, peer, sizeof(struct GNUNET_PeerIdentity)))
+    return;
+
   connection_count++;
   connection_count++;
-  pos = friends;
-  while (pos != NULL)
+  GNUNET_STATISTICS_set (stats,
+                        gettext_noop ("# peers connected"),
+                        connection_count,
+                        GNUNET_NO);
+  pos = GNUNET_CONTAINER_multihashmap_get (peers, &peer->hashPubKey);
+  if (pos == NULL)    
     {
     {
-      if ( (GNUNET_YES == pos->is_friend) &&
-          (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity))) )
-       {
-         GNUNET_assert (GNUNET_NO == pos->is_connected);
-         pos->is_connected = GNUNET_YES;
-         pos->blacklisted_until.value = 0; /* remove blacklisting */
-         friend_count++;
-         return;
-       }
-      pos = pos->next;
+      pos = make_peer (peer, NULL, GNUNET_NO);
+      GNUNET_break (GNUNET_OK == is_connection_allowed (pos));
+    }
+  else
+    {
+      GNUNET_assert (GNUNET_NO == pos->is_connected);
+      pos->greylisted_until.abs_value = 0; /* remove greylisting */
     }
     }
-  pos = GNUNET_malloc (sizeof(struct PeerList));
-  pos->id = *peer;
   pos->is_connected = GNUNET_YES;
   pos->is_connected = GNUNET_YES;
-  pos->next = friends;
-  friends = pos;
-  if (GNUNET_OK != is_connection_allowed (peer))
+  pos->connect_attempts = 0; /* re-set back-off factor */
+  if (pos->is_friend)
     {
     {
-#if DEBUG_TOPOLOGY
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Connection to `%s' is forbidden, forcing disconnect!\n",
-                 GNUNET_i2s (peer));
-#endif       
-      force_disconnect (peer);
+      if ( (friend_count == minimum_friend_count - 1) &&
+          (GNUNET_YES != friends_only) )       
+       whitelist_peers ();       
+      friend_count++;
+      GNUNET_STATISTICS_set (stats,
+                            gettext_noop ("# friends connected"),
+                            friend_count,
+                            GNUNET_NO);
     }
     }
+  reschedule_hellos (NULL, &peer->hashPubKey, pos);
 }
 
 
 /**
 }
 
 
 /**
- * Disconnect from all non-friends (we're below quota).
+ * Try to add more peers to our connection set.
+ *
+ * @param cls closure, not used
+ * @param pid identity of a peer
+ * @param value 'struct Peer*' for the peer
+ * @return GNUNET_YES (continue to iterate)
  */
  */
-static void
-drop_non_friends ()
+static int
+try_add_peers (void *cls,
+              const GNUNET_HashCode *pid,
+              void *value)
 {
 {
-  struct PeerList *pos;
+  struct Peer *pos = value;
 
 
-  pos = friends;
-  while (pos != NULL)
-    {
-      if (GNUNET_NO == pos->is_friend)
-       {
-         GNUNET_assert (GNUNET_YES == pos->is_connected);
-#if DEBUG_TOPOLOGY
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Connection to `%s' is not from a friend, forcing disconnect!\n",
-                     GNUNET_i2s (&pos->id));
-#endif       
-         force_disconnect (&pos->id);
-       }
-      pos = pos->next;
-    }
+  attempt_connect (pos);
+  return GNUNET_YES;
 }
 
 
 }
 
 
@@ -538,101 +766,57 @@ drop_non_friends ()
  * @param cls closure
  * @param peer peer identity this notification is about
  */
  * @param cls closure
  * @param peer peer identity this notification is about
  */
-static void disconnect_notify (void *cls,
-                              const struct
-                              GNUNET_PeerIdentity * peer)
+static void 
+disconnect_notify (void *cls,
+                  const struct
+                  GNUNET_PeerIdentity * peer)
 {
 {
-  struct PeerList *pos;
-  struct PeerList *prev;
+  struct Peer *pos;
+
+  if (0 == memcmp(&my_identity, peer, sizeof(struct GNUNET_PeerIdentity)))
+    return;
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core told us that we disconnected from `%s'\n",
              GNUNET_i2s (peer));
 #endif       
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Core told us that we disconnected from `%s'\n",
              GNUNET_i2s (peer));
 #endif       
+  pos = GNUNET_CONTAINER_multihashmap_get (peers,
+                                          &peer->hashPubKey);
+  if (pos == NULL)
+    {
+      GNUNET_break (0);
+      return;
+    }
+  if (pos->is_connected != GNUNET_YES)
+    {
+      GNUNET_break (0);
+      return;
+    }
   connection_count--;
   connection_count--;
-  pos = friends;
-  prev = NULL;
-  while (pos != NULL)
+  GNUNET_STATISTICS_set (stats,
+                        gettext_noop ("# peers connected"),
+                        connection_count,
+                        GNUNET_NO);
+  if (pos->is_friend)
     {
     {
-      if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
-       {
-         GNUNET_assert (GNUNET_YES == pos->is_connected);
-         pos->is_connected = GNUNET_NO;
-         if (GNUNET_YES == pos->is_friend)
-           {
-             friend_count--;
-             if (friend_count < minimum_friend_count)
-               {
-                 /* disconnect from all non-friends */
-#if DEBUG_TOPOLOGY
-                 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                             "Not enough friendly connections, dropping all non-friend connections\n");
-#endif       
-                 drop_non_friends ();
-                 attempt_connect (peer, pos);
-               }
-           }
-         else
-           {
-             /* free entry */
-             if (prev == NULL)
-               friends = pos->next;
-             else
-               prev->next = pos->next;
-             GNUNET_free (pos);
-           }
-         return;
-       }
-      prev = pos;
-      pos = pos->next;
+      friend_count--; 
+      GNUNET_STATISTICS_set (stats,
+                            gettext_noop ("# friends connected"),
+                            friend_count,
+                            GNUNET_NO);
     }
     }
-  GNUNET_break (0);
-}
-
-
-/**
- * Find more peers that we should connect to and ask the
- * core to establish connections.
- */
-static void
-find_more_peers (void *cls,
-                const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-
-/**
- * Determine when we should try again to find more peers and
- * schedule the task.
- */
-static void
-schedule_peer_search ()
-{
-  struct GNUNET_TIME_Relative delay;
-
-  /* Typically, we try again every 15 minutes; the minimum period is
-     15s; if we are above the connection target, we reduce re-trying
-     by the square of how much we are above; so for example, with 200%
-     of the connection target we would only look for more peers once
-     every hour (after all, we're quite busy processing twice as many
-     connections as we intended to have); similarly, if we are at only
-     25% of our connectivity goal, we will try 16x as hard to connect
-     (so roughly once a minute, plus the 15s minimum delay */
-  delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
-                                        15 + 15 * 60 * connection_count * connection_count / target_connection_count / target_connection_count);
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Will try to find more peers in %llums\n",
-             (unsigned long long) delay.value);
-#endif 
-  GNUNET_SCHEDULER_add_delayed (sched,
-                               delay,
-                               &find_more_peers,
-                               NULL);
+  if ( (connection_count < target_connection_count) ||
+       (friend_count < minimum_friend_count) )
+    GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                          &try_add_peers,
+                                          NULL);
+  if ( (friend_count < minimum_friend_count) &&
+       (blacklist == NULL) )
+    blacklist = GNUNET_TRANSPORT_blacklist (cfg,
+                                           &blacklist_check, NULL);
 }
 
 
 }
 
 
-
-
 /**
  * Iterator called on each address.
  *
 /**
  * Iterator called on each address.
  *
@@ -647,7 +831,8 @@ static int
 address_iterator (void *cls,
                  const char *tname,
                  struct GNUNET_TIME_Absolute expiration,
 address_iterator (void *cls,
                  const char *tname,
                  struct GNUNET_TIME_Absolute expiration,
-                 const void *addr, size_t addrlen)
+                 const void *addr, 
+                 uint16_t addrlen)
 {
   int *flag = cls;
   *flag = GNUNET_YES;
 {
   int *flag = cls;
   *flag = GNUNET_YES;
@@ -656,17 +841,30 @@ address_iterator (void *cls,
 
 
 /**
 
 
 /**
- * We've gotten a HELLO from another peer.
- * Consider it for advertising.
+ * We've gotten a HELLO from another peer.  Consider it for
+ * advertising.
+ *
+ * @param hello the HELLO we got
  */
 static void
 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
 {
   int have_address;
   struct GNUNET_PeerIdentity pid;
  */
 static void
 consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
 {
   int have_address;
   struct GNUNET_PeerIdentity pid;
-  struct HelloList *pos;
+  struct GNUNET_TIME_Absolute dt;
+  struct GNUNET_HELLO_Message *nh;
+  struct Peer *peer;
   uint16_t size;
 
   uint16_t size;
 
+  if (GNUNET_OK != GNUNET_HELLO_get_id (hello, &pid))
+    {
+       GNUNET_break (0);
+       return;
+    }
+  if (0 == memcmp (&pid,
+                  &my_identity,
+                  sizeof (struct GNUNET_PeerIdentity)))
+    return; /* that's me! */
   have_address = GNUNET_NO;
   GNUNET_HELLO_iterate_addresses (hello,
                                  GNUNET_NO,
   have_address = GNUNET_NO;
   GNUNET_HELLO_iterate_addresses (hello,
                                  GNUNET_NO,
@@ -674,15 +872,19 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
                                  &have_address);
   if (GNUNET_NO == have_address)
     return; /* no point in advertising this one... */
                                  &have_address);
   if (GNUNET_NO == have_address)
     return; /* no point in advertising this one... */
-  GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid));
-  pos = hellos;
-  while (pos != NULL)
+  peer = GNUNET_CONTAINER_multihashmap_get (peers,
+                                           &pid.hashPubKey);
+  if (peer == NULL)
     {
     {
-      if (0 == memcmp (&pos->id,
-                      &pid,
-                      sizeof(struct GNUNET_PeerIdentity)))
-       return; /* duplicate, at least "mostly" */
-      pos = pos->next;
+      peer = make_peer (&pid, hello, GNUNET_NO);
+    }
+  else if (peer->hello != NULL)
+    {
+      dt = GNUNET_HELLO_equals (peer->hello,
+                               hello,
+                               GNUNET_TIME_absolute_get());
+      if (dt.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
+       return; /* nothing new here */
     }
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     }
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -690,219 +892,100 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello)
              "HELLO",
              GNUNET_i2s (&pid));
 #endif 
              "HELLO",
              GNUNET_i2s (&pid));
 #endif 
-  size = GNUNET_HELLO_size (hello);
-  pos = GNUNET_malloc (sizeof(struct HelloList) + size);
-  pos->msg = (struct GNUNET_HELLO_Message*) &pos[1];
-  memcpy (&pos->msg, hello, size);
-  pos->id = pid;
-  pos->expiration = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
-  /* 2^{-5} chance of not sending a HELLO to a peer is
-     acceptably small (if the filter is 50% full);
-     64 bytes of memory are small compared to the rest
-     of the data structure and would only really become
-     "useless" once a HELLO has been passed on to ~100
-     other peers, which is likely more than enough in
-     any case; hence 64, 5 as bloomfilter parameters. */
-  pos->filter = GNUNET_CONTAINER_bloomfilter_load (NULL, 64, 5);
-  /* never send a peer its own HELLO */
-  GNUNET_CONTAINER_bloomfilter_add (pos->filter, &pos->id.hashPubKey);
-  pos->next = hellos;
-  hellos = pos;
+  if (peer->hello != NULL)
+    {
+      nh = GNUNET_HELLO_merge (peer->hello,
+                              hello);
+      GNUNET_free (peer->hello);
+      peer->hello = nh;
+    }
+  else
+    {
+      size = GNUNET_HELLO_size (hello);
+      peer->hello = GNUNET_malloc (size);
+      memcpy (peer->hello, hello, size);
+    }
+  if (peer->filter != NULL)
+    GNUNET_CONTAINER_bloomfilter_free (peer->filter);
+  setup_filter (peer);
+  /* since we have a new HELLO to pick from, re-schedule all
+     HELLO requests that are not bound by the HELLO send rate! */
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                        &reschedule_hellos,
+                                        peer);
 }
 
 
 /**
 }
 
 
 /**
- * Peerinfo calls this function to let us know about a
- * possible peer that we might want to connect to.
+ * PEERINFO calls this function to let us know about a possible peer
+ * that we might want to connect to.
+ *
+ * @param cls closure (not used)
+ * @param peer potential peer to connect to
+ * @param hello HELLO for this peer (or NULL)
  */
 static void
 process_peer (void *cls,
              const struct GNUNET_PeerIdentity *peer,
  */
 static void
 process_peer (void *cls,
              const struct GNUNET_PeerIdentity *peer,
-             const struct GNUNET_HELLO_Message *hello,
-             uint32_t trust)
+             const struct GNUNET_HELLO_Message *hello)
 {
 {
-  struct PeerList *pos;
+  struct Peer *pos;
 
 
-  if (peer == NULL)
-    {
-      pitr_more = NULL;
-      /* last call, schedule 'find_more_peers' again... */
-      if (0 != (GNUNET_SCHEDULER_get_reason (sched) & GNUNET_SCHEDULER_REASON_SHUTDOWN))
-       {
-#if DEBUG_TOPOLOGY
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Received shutdown request, stopping search for peers to connect to.\n");
-#endif
-         return;
-       }
-      schedule_peer_search ();
-      return;
-    }
-  if (hello == NULL)
-    {
-      /* no HELLO known; can not connect, ignore! */
-      return;
-    }
+  GNUNET_assert (peer != NULL);
   if (0 == memcmp (&my_identity,
                    peer, sizeof (struct GNUNET_PeerIdentity)))
     return;  /* that's me! */
   if (0 == memcmp (&my_identity,
                    peer, sizeof (struct GNUNET_PeerIdentity)))
     return;  /* that's me! */
-
-  consider_for_advertising (hello);
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Considering connecting to peer `%s'\n",
-             GNUNET_i2s (peer));
-#endif 
-  pos = friends;
-  while (pos != NULL)
+  if (hello == NULL)
     {
     {
-      if (0 == memcmp (&pos->id, peer, sizeof (struct GNUNET_PeerIdentity)))
+      /* free existing HELLO, if any */
+      pos = GNUNET_CONTAINER_multihashmap_get (peers,
+                                              &peer->hashPubKey);
+      if (NULL != pos)
        {
        {
-         if (GNUNET_YES == pos->is_connected)
-           {
-#if DEBUG_TOPOLOGY
-             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                         "Already connected to peer `%s'\n",
-                         GNUNET_i2s (peer));
-#endif 
-             return;
-           }
-         if (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value > 0)
+         GNUNET_free_non_null (pos->hello);
+         pos->hello = NULL;
+         if (pos->filter != NULL)
            {
            {
-#if DEBUG_TOPOLOGY
-             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                         "Already tried peer `%s' recently\n",
-                         GNUNET_i2s (peer));
-#endif 
-             return; /* peer still blacklisted */
-           }
-         if (GNUNET_YES == pos->is_friend)
-           {
-             attempt_connect (peer, pos);
-             return;
+             GNUNET_CONTAINER_bloomfilter_free (pos->filter);
+             pos->filter = NULL;
            }
            }
+         if ( (! pos->is_connected) &&
+              (! pos->is_friend) &&
+              (0 == GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).rel_value) )
+           free_peer (NULL, &pos->pid.hashPubKey, pos);
        }
        }
-      pos = pos->next;
+      return;
     }
     }
-  if ( (GNUNET_YES == friends_only) ||    
-       (friend_count < minimum_friend_count) )
+  consider_for_advertising (hello);
+  pos = GNUNET_CONTAINER_multihashmap_get (peers,
+                                          &peer->hashPubKey);
+  if (pos == NULL)
+    pos = make_peer (peer, hello, GNUNET_NO);
+  GNUNET_assert (NULL != pos);
+  if (GNUNET_YES == pos->is_connected)
     {
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     {
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Peer `%s' is not a friend, and we currently only connect to friends\n",
+                 "Already connected to peer `%s'\n",
                  GNUNET_i2s (peer));
 #endif 
       return;
     }
                  GNUNET_i2s (peer));
 #endif 
       return;
     }
-  attempt_connect (peer, NULL);
-}
-
-
-/**
- * Try to add more friends to our connection set.
- */
-static void
-try_add_friends ()
-{
-  struct PeerList *pos;
-
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Considering all of our friends for new connections\n");
-#endif 
-  pos = friends;
-  while (pos != NULL)
-    {
-      if ( (GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value == 0) &&
-          (GNUNET_YES == pos->is_friend) &&
-          (GNUNET_YES != pos->is_connected) )
-       attempt_connect (&pos->id, pos);
-      pos = pos->next;
-    }
-}
-
-
-/**
- * Discard peer entries for blacklisted peers
- * where the blacklisting has expired.
- */
-static void
-discard_old_blacklist_entries ()
-{
-  struct PeerList *pos;
-  struct PeerList *next;
-  struct PeerList *prev;
-
-  next = friends;
-  prev = NULL;
-  while (NULL != (pos = next))
-    {
-      next = pos->next;
-      if ( (GNUNET_NO == pos->is_friend) &&
-          (GNUNET_NO == pos->is_connected) &&
-          (0 == GNUNET_TIME_absolute_get_remaining (pos->blacklisted_until).value) )
-       {
-         /* delete 'pos' from list */
-#if DEBUG_TOPOLOGY
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Deleting peer `%s' from our list (not connected, not a friend and blacklist expired)\n",
-                     GNUNET_i2s (&pos->id));
-#endif         
-         if (prev == NULL)
-           friends = next;
-         else
-           prev->next = next;
-         GNUNET_free (pos);
-       }
-      else
-       {
-         prev = pos;
-       }
-    }
-}
-
-
-/**
- * Find more peers that we should connect to and ask the
- * core to establish connections.
- */
-static void
-find_more_peers (void *cls,
-                const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+  if (GNUNET_TIME_absolute_get_remaining (pos->greylisted_until).rel_value > 0)
     {
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     {
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Received shutdown request, stopping search for peers to connect to.\n");
-#endif
-      return;
-    }
-  discard_old_blacklist_entries ();
-  if (connection_count <= target_connection_count)
-    {
-      schedule_peer_search ();
-      return;
-    }
-  if ( (GNUNET_YES == friends_only) ||
-       (friend_count < minimum_friend_count) )
-    {
-      try_add_friends ();
-      schedule_peer_search ();
-      return;
+                 "Already tried peer `%s' recently\n",
+                 GNUNET_i2s (peer));
+#endif 
+      return; /* peer still greylisted */
     }
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     }
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Got sufficient (%u/%u, %u friends) number of connections, won't try to create more.\n",
-             connection_count,
-             target_connection_count,
-             friend_count);
-#endif         
-  pitr_more = GNUNET_PEERINFO_iterate (cfg,
-                                      sched,
-                                      NULL,
-                                      0, GNUNET_TIME_UNIT_FOREVER_REL,
-                                      &process_peer, NULL);
+             "Considering connecting to peer `%s'\n",
+             GNUNET_i2s (peer));
+#endif 
+  attempt_connect (pos);
 }
 
 
 }
 
 
@@ -928,7 +1011,7 @@ core_init (void *cls,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to core service, can not manage topology!\n"));
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to core service, can not manage topology!\n"));
-      GNUNET_SCHEDULER_shutdown (sched);
+      GNUNET_SCHEDULER_shutdown ();
       return;
     }
   handle = server;
       return;
     }
   handle = server;
@@ -938,22 +1021,11 @@ core_init (void *cls,
              "I am peer `%s'\n",
              GNUNET_i2s (my_id));
 #endif         
              "I am peer `%s'\n",
              GNUNET_i2s (my_id));
 #endif         
-  if (autoconnect)
-    GNUNET_SCHEDULER_add_delayed (sched,
-                                 GNUNET_TIME_UNIT_SECONDS /* give core time to tell us about existing connections */,
-                                 &find_more_peers,
-                                 NULL);
+  peerinfo_notify = GNUNET_PEERINFO_notify (cfg, &process_peer,
+                                           NULL);
 }
 
 
 }
 
 
-/**
- * gnunet-daemon-topology command line options.
- */
-static struct GNUNET_GETOPT_CommandLineOption options[] = {
-  GNUNET_GETOPT_OPTION_END
-};
-
-
 /**
  * Read the friends file.
  */
 /**
  * Read the friends file.
  */
@@ -963,11 +1035,11 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
   char *fn;
   char *data;
   size_t pos;
   char *fn;
   char *data;
   size_t pos;
-  GNUNET_HashCode hc;
+  struct GNUNET_PeerIdentity pid;
   struct stat frstat;
   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
   unsigned int entries_found;
   struct stat frstat;
   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
   unsigned int entries_found;
-  struct PeerList *fl;
+  struct Peer *fl;
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
 
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_filename (cfg,
@@ -982,17 +1054,16 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
       return;
     }
   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
       return;
     }
   if (GNUNET_OK != GNUNET_DISK_file_test (fn))
-    GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ
-        | GNUNET_DISK_PERM_USER_WRITE);
+    GNUNET_DISK_fn_write (fn, NULL, 0, 
+                         GNUNET_DISK_PERM_USER_READ
+                         | GNUNET_DISK_PERM_USER_WRITE);
   if (0 != STAT (fn, &frstat))
     {
       if ((friends_only) || (minimum_friend_count > 0))
   if (0 != STAT (fn, &frstat))
     {
       if ((friends_only) || (minimum_friend_count > 0))
-        {
-          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                     _("Could not read friends list `%s'\n"), fn);
-         GNUNET_free (fn);
-          return;
-        }
+       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                   _("Could not read friends list `%s'\n"), fn);
+      GNUNET_free (fn);
+      return;
     }
   if (frstat.st_size == 0)
     {
     }
   if (frstat.st_size == 0)
     {
@@ -1003,6 +1074,13 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
       return;
     }
   data = GNUNET_malloc_large (frstat.st_size);
       return;
     }
   data = GNUNET_malloc_large (frstat.st_size);
+  if (data == NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 _("Failed to read friends list from `%s': out of memory\n"), fn);
+      GNUNET_free (fn);
+      return;
+    }
   if (frstat.st_size !=
       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
     {
   if (frstat.st_size !=
       GNUNET_DISK_fn_read (fn, data, frstat.st_size))
     {
@@ -1014,24 +1092,24 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
     }
   entries_found = 0;
   pos = 0;
     }
   entries_found = 0;
   pos = 0;
-  while ((pos < frstat.st_size) && isspace (data[pos]))
+  while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
     pos++;
   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
         (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
     {
       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
     pos++;
   while ((frstat.st_size >= sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)) &&
         (pos <= frstat.st_size - sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded)))
     {
       memcpy (&enc, &data[pos], sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded));
-      if (!isspace (enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
+      if (!isspace ( (unsigned char) enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1]))
        {
          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                      _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
                      (unsigned long long) pos);
          pos++;
        {
          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                      _("Syntax error in topology specification at offset %llu, skipping bytes.\n"),
                      (unsigned long long) pos);
          pos++;
-         while ((pos < frstat.st_size) && (!isspace (data[pos])))
+         while ((pos < frstat.st_size) && (!isspace ( (unsigned char) data[pos])))
            pos++;
          continue;
        }
       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
            pos++;
          continue;
        }
       enc.encoding[sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1] = '\0';
-      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &hc))
+      if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string ((char *) &enc, &pid.hashPubKey))
        {
          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                      _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
        {
          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                      _("Syntax error in topology specification at offset %llu, skipping bytes `%s'.\n"),
@@ -1040,24 +1118,35 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
        }
       else
        {
        }
       else
        {
-         entries_found++;
-         fl = GNUNET_malloc (sizeof(struct PeerList));
-         fl->is_friend = GNUNET_YES;
-         fl->id.hashPubKey = hc;
-         fl->next = friends;
-         friends = fl;
-#if DEBUG_TOPOLOGY
-         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                     "Found friend `%s' in configuration\n",
-                     GNUNET_i2s (&fl->id));
-#endif       
+         if (0 != memcmp (&pid,
+                          &my_identity,
+                          sizeof (struct GNUNET_PeerIdentity)))
+           {
+             entries_found++;
+             fl = make_peer (&pid,
+                             NULL,
+                             GNUNET_YES);
+             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                         _("Found friend `%s' in configuration\n"),
+                         GNUNET_i2s (&fl->pid));
+           }
+         else
+           {
+             GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                         _("Found myself `%s' in friend list (useless, ignored)\n"),
+                         GNUNET_i2s (&pid));
+           }
        }
       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
        }
       pos = pos + sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded);
-      while ((pos < frstat.st_size) && isspace (data[pos]))
+      while ((pos < frstat.st_size) && isspace ( (unsigned char) data[pos]))
        pos++;
     }
   GNUNET_free (data);
   GNUNET_free (fn);
        pos++;
     }
   GNUNET_free (data);
   GNUNET_free (fn);
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# friends in configuration"),
+                           entries_found,
+                           GNUNET_NO);
   if ( (minimum_friend_count > entries_found) &&
        (friends_only == GNUNET_NO) )
     {
   if ( (minimum_friend_count > entries_found) &&
        (friends_only == GNUNET_NO) )
     {
@@ -1081,6 +1170,7 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg)
  * @param other the other peer involved (sender or receiver, NULL
  *        for loopback messages where we are both sender and receiver)
  * @param message the actual HELLO message
  * @param other the other peer involved (sender or receiver, NULL
  *        for loopback messages where we are both sender and receiver)
  * @param message the actual HELLO message
+ * @param atsi performance data
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
  * @return GNUNET_OK to keep the connection open,
  *         GNUNET_SYSERR to close it (signal serious error)
  */
@@ -1088,14 +1178,46 @@ static int
 handle_encrypted_hello (void *cls,
                        const struct GNUNET_PeerIdentity * other,
                        const struct GNUNET_MessageHeader *
 handle_encrypted_hello (void *cls,
                        const struct GNUNET_PeerIdentity * other,
                        const struct GNUNET_MessageHeader *
-                       message)
+                       message,
+                       const struct GNUNET_TRANSPORT_ATS_Information *atsi)
 {
 {
+  struct Peer *peer;
+  struct GNUNET_PeerIdentity pid;
+
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received encrypted `%s' from peer `%s'",
              "HELLO",
              GNUNET_i2s (other));
 #endif         
 #if DEBUG_TOPOLOGY
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "Received encrypted `%s' from peer `%s'",
              "HELLO",
              GNUNET_i2s (other));
 #endif         
+  if (GNUNET_OK !=
+      GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message*) message,
+                          &pid))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  GNUNET_STATISTICS_update (stats,
+                           gettext_noop ("# HELLO messages received"),
+                           1,
+                           GNUNET_NO);
+  peer = GNUNET_CONTAINER_multihashmap_get (peers,
+                                           &pid.hashPubKey);
+  if (peer == NULL)
+    {
+      if ( (GNUNET_YES == friends_only) ||
+          (friend_count < minimum_friend_count) )
+       return GNUNET_OK;      
+    }
+  else
+    {
+      if ( (GNUNET_YES != peer->is_friend) &&
+          (GNUNET_YES == friends_only) )
+       return GNUNET_OK;
+      if ( (GNUNET_YES != peer->is_friend) &&
+          (friend_count < minimum_friend_count) )
+       return GNUNET_OK;      
+    }
   if (transport != NULL)
     GNUNET_TRANSPORT_offer_hello (transport,
                                  message);
   if (transport != NULL)
     GNUNET_TRANSPORT_offer_hello (transport,
                                  message);
@@ -1103,192 +1225,91 @@ handle_encrypted_hello (void *cls,
 }
 
 
 }
 
 
-/**
- * Peerinfo calls this function to let us know about a
- * possible peer that we might want to connect to.
- *
- * @param cls unused
- * @param peer NULL for the end of the list, otherwise a peer identity
- * @param hello a HELLO for a peer, or NULL
- * @param trust how much do we trust the given peer?
- */
-static void
-gather_hello_callback (void *cls,
-                      const struct GNUNET_PeerIdentity *peer,
-                      const struct GNUNET_HELLO_Message *hello,
-                      uint32_t trust)
-{
-  if (peer == NULL)
-    {
-      pitr = NULL;
-      return;
-    }
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Received `%s' for peer `%s'",
-             "HELLO",
-             GNUNET_i2s (peer));
-#endif         
-  if (hello != NULL)
-    consider_for_advertising (hello);
-}
-
-
-// FIXME: this no longer works (no solicitation!)
 /**
  * Function to fill send buffer with HELLO.
  *
 /**
  * Function to fill send buffer with HELLO.
  *
- * @param cls unused
- * @param receiver the receiver of the message
- * @param position is the reference to the
- *        first unused position in the buffer where GNUnet is building
- *        the message
- * @param padding is the number of bytes left in that buffer.
- * @return the number of bytes written to
- *   that buffer (must be a positive number).
+ * @param cls 'struct Peer' of the target peer
+ * @param size number of bytes available in buf
+ * @param buf where the callee should write the message
+ * @return number of bytes written to buf
  */
  */
-/* static */ unsigned int
-hello_advertising (void *cls,
-                  const struct GNUNET_PeerIdentity *
-                  receiver,
-                  void *position, 
-                  size_t padding)
+static size_t
+hello_advertising_ready (void *cls,
+                        size_t size,
+                        void *buf)
 {
 {
-  struct PeerList *pl;
-  struct HelloList *pos;
-  struct HelloList *prev;
-  struct HelloList *next;
-  uint16_t size;
+  struct Peer *pl = cls;
+  struct FindAdvHelloContext fah;
+  size_t want;
 
 
-#if DEBUG_TOPOLOGY
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Data solicited for `%s', considering sending `%s's",
-             GNUNET_i2s (receiver),
-             "HELLO");
-#endif         
-  pl = friends;
-  while (pl != NULL)
-    {
-      if (0 == memcmp (&pl->id, receiver, sizeof (struct GNUNET_PeerIdentity)))
-       break;
-      pl = pl->next;
-    }
-  if (pl == NULL)
-    {
-      GNUNET_break (0);
-      return 0;
-    }
+  pl->hello_req = NULL;
   /* find applicable HELLOs */
   /* find applicable HELLOs */
-  prev = NULL;
-  next = hellos;
-  while (NULL != (pos = next))
+  fah.peer = pl;
+  fah.result = NULL;
+  fah.max_size = size;
+  fah.next_adv = GNUNET_TIME_UNIT_FOREVER_REL;
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                        &find_advertisable_hello,
+                                        &fah);
+  want = 0;
+  if (fah.result != NULL)
     {
     {
-      next = pos->next;
-      if (GNUNET_NO ==
-         GNUNET_CONTAINER_bloomfilter_test (pos->filter,
-                                            &receiver->hashPubKey))
-       break;
-      if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value)
-       {
-         /* time to discard... */
-         if (prev != NULL)
-           prev->next = next;
-         else
-           hellos = next;
-         GNUNET_CONTAINER_bloomfilter_free (pos->filter);
-         GNUNET_free (pos);
-       }
-      else
-       {
-         prev = pos;
-       }
-    }
-  if (pos != NULL)
-    {
-      size = GNUNET_HELLO_size (pos->msg);
-      if (size < padding)
-       {
-         memcpy (position, pos->msg, size);
-         GNUNET_CONTAINER_bloomfilter_add (pos->filter,
-                                           &receiver->hashPubKey);
-       }
-      else
-       {
-         size = 0;
-       }
+      want = GNUNET_HELLO_size (fah.result->hello);
+      GNUNET_assert (want <= size);
+      memcpy (buf, fah.result->hello, want);
+      GNUNET_CONTAINER_bloomfilter_add (fah.result->filter,
+                                       &pl->pid.hashPubKey);
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
 #if DEBUG_TOPOLOGY
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Sending %u bytes of  `%s's",
-                 (unsigned int) size,
-                 "HELLO");
-#endif         
-      return size;
-    }
-  if ( (NULL == pitr) &&
-       (GNUNET_TIME_absolute_get_duration (last_hello_gather_time).value >
-       MIN_HELLO_GATHER_DELAY.value) )
-    {
-#if DEBUG_TOPOLOGY
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                 "Have no `%s's, trying to get some from `%s' for next time",
+                 "Sending `%s' with %u bytes",
                  "HELLO",
                  "HELLO",
-                 "PEERINFO");
+                 (unsigned int) want);
 #endif         
 #endif         
-      last_hello_gather_time = GNUNET_TIME_absolute_get();
-      pitr = GNUNET_PEERINFO_iterate (cfg,
-                                     sched,
-                                     NULL,
-                                     0, GNUNET_TIME_UNIT_FOREVER_REL,
-                                     &gather_hello_callback, NULL);
+      GNUNET_STATISTICS_update (stats,
+                               gettext_noop ("# HELLO messages gossipped"),
+                               1,
+                               GNUNET_NO);    
     }
     }
-  return 0;
+  pl->next_hello_allowed = GNUNET_TIME_relative_to_absolute (HELLO_ADVERTISEMENT_MIN_FREQUENCY);
+  pl->hello_delay_task 
+    = GNUNET_SCHEDULER_add_now (&schedule_next_hello,
+                               pl);
+  return want;
 }
 
 
 /**
  * Last task run during shutdown.  Disconnects us from
  * the transport and core.
 }
 
 
 /**
  * Last task run during shutdown.  Disconnects us from
  * the transport and core.
+ *
+ * @param cls unused, NULL
+ * @param tc scheduler context
  */
 static void
  */
 static void
-cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+cleaning_task (void *cls, 
+              const struct GNUNET_SCHEDULER_TaskContext *tc)
 {
 {
-  struct PeerList *pl;
-  struct DisconnectList *dl;
-
   if (NULL != peerinfo_notify)
     {
       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
       peerinfo_notify = NULL;
     }
   if (NULL != peerinfo_notify)
     {
       GNUNET_PEERINFO_notify_cancel (peerinfo_notify);
       peerinfo_notify = NULL;
     }
-  if (NULL != pitr)
-    {
-      GNUNET_PEERINFO_iterate_cancel (pitr);
-      pitr = NULL;
-    }
-  if (NULL != pitr_more)
-    {
-      GNUNET_PEERINFO_iterate_cancel (pitr_more);
-      pitr_more = NULL;
-    }
   GNUNET_TRANSPORT_disconnect (transport);
   transport = NULL;
   GNUNET_TRANSPORT_disconnect (transport);
   transport = NULL;
+  GNUNET_CONTAINER_multihashmap_iterate (peers,
+                                        &free_peer,
+                                        NULL);
+  GNUNET_CONTAINER_multihashmap_destroy (peers);
   if (handle != NULL)
     {
       GNUNET_CORE_disconnect (handle);
       handle = NULL;
     }
   if (handle != NULL)
     {
       GNUNET_CORE_disconnect (handle);
       handle = NULL;
     }
-  while (NULL != (pl = friends))
-    {
-      friends = pl->next;
-      GNUNET_free (pl);
-    }  
-  while (NULL != (dl = disconnect_head))
+  whitelist_peers ();
+  if (stats != NULL)
     {
     {
-      GNUNET_CONTAINER_DLL_remove (disconnect_head,
-                                  disconnect_tail,
-                                  dl);
-      GNUNET_CORE_peer_get_info_cancel (dl->rh);
-      GNUNET_free (dl);
+      GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
+      stats = NULL;
     }
 }
 
     }
 }
 
@@ -1297,14 +1318,12 @@ cleaning_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * Main function that will be run.
  *
  * @param cls closure
  * Main function that will be run.
  *
  * @param cls closure
- * @param s the scheduler to use
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
  * @param c configuration
  */
 static void
 run (void *cls,
  * @param args remaining command-line arguments
  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
  * @param c configuration
  */
 static void
 run (void *cls,
-     struct GNUNET_SCHEDULER_Handle * s,
      char *const *args,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle * c)
      char *const *args,
      const char *cfgfile,
      const struct GNUNET_CONFIGURATION_Handle * c)
@@ -1316,8 +1335,8 @@ run (void *cls,
     };
   unsigned long long opt;
 
     };
   unsigned long long opt;
 
-  sched = s;
   cfg = c;
   cfg = c;
+  stats = GNUNET_STATISTICS_create ("topology", cfg);
   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
                                                      "TOPOLOGY",
                                                      "AUTOCONNECT");
   autoconnect = GNUNET_CONFIGURATION_get_value_yesno (cfg,
                                                      "TOPOLOGY",
                                                      "AUTOCONNECT");
@@ -1338,6 +1357,7 @@ run (void *cls,
                                             &opt))
     opt = 16;
   target_connection_count = (unsigned int) opt;
                                             &opt))
     opt = 16;
   target_connection_count = (unsigned int) opt;
+  peers = GNUNET_CONTAINER_multihashmap_create (target_connection_count * 2);
 
   if ( (friends_only == GNUNET_YES) ||
        (minimum_friend_count > 0) )
 
   if ( (friends_only == GNUNET_YES) ||
        (minimum_friend_count > 0) )
@@ -1349,31 +1369,33 @@ run (void *cls,
              minimum_friend_count,
              autoconnect ? "autoconnect enabled" : "autoconnect disabled");
 #endif       
              minimum_friend_count,
              autoconnect ? "autoconnect enabled" : "autoconnect disabled");
 #endif       
-  transport = GNUNET_TRANSPORT_connect (sched,
-                                       cfg,
+  if (friend_count < minimum_friend_count) 
+    blacklist = GNUNET_TRANSPORT_blacklist (cfg,
+                                           &blacklist_check, NULL);
+  transport = GNUNET_TRANSPORT_connect (cfg,
+                                       NULL,
                                        NULL,
                                        NULL,
                                        NULL,
                                        NULL);
                                        NULL,
                                        NULL,
                                        NULL,
                                        NULL);
-  handle = GNUNET_CORE_connect (sched,
-                               cfg,
-                               GNUNET_TIME_UNIT_FOREVER_REL,
+  handle = GNUNET_CORE_connect (cfg,
+                               1,
                                NULL,
                                &core_init,
                                &connect_notify,
                                NULL,
                                &core_init,
                                &connect_notify,
-                               &disconnect_notify,
+                               &disconnect_notify, 
+                               NULL,
                                NULL, GNUNET_NO,
                                NULL, GNUNET_NO,
                                handlers);
                                NULL, GNUNET_NO,
                                NULL, GNUNET_NO,
                                handlers);
-  GNUNET_SCHEDULER_add_delayed (sched,
-                                GNUNET_TIME_UNIT_FOREVER_REL,
+  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
                                 &cleaning_task, NULL);
   if (NULL == transport)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to `%s' service.\n"),
                  "transport");
                                 &cleaning_task, NULL);
   if (NULL == transport)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to `%s' service.\n"),
                  "transport");
-      GNUNET_SCHEDULER_shutdown (sched);
+      GNUNET_SCHEDULER_shutdown ();
       return;
     }
   if (NULL == handle)
       return;
     }
   if (NULL == handle)
@@ -1381,12 +1403,9 @@ run (void *cls,
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to `%s' service.\n"),
                  "core");
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  _("Failed to connect to `%s' service.\n"),
                  "core");
-      GNUNET_SCHEDULER_shutdown (sched);
+      GNUNET_SCHEDULER_shutdown ();
       return;
     }
       return;
     }
-  peerinfo_notify = GNUNET_PEERINFO_notify (cfg, sched,
-                                           &process_peer,
-                                           NULL);
 }
 
 
 }
 
 
@@ -1400,6 +1419,9 @@ run (void *cls,
 int
 main (int argc, char *const *argv)
 {
 int
 main (int argc, char *const *argv)
 {
+  static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+    GNUNET_GETOPT_OPTION_END
+  };
   int ret;
 
   ret = (GNUNET_OK ==
   int ret;
 
   ret = (GNUNET_OK ==