changes to testing... create hostkey using peerinfo before starting peers, better...
authorNathan S. Evans <evans@in.tum.de>
Sun, 9 May 2010 15:55:32 +0000 (15:55 +0000)
committerNathan S. Evans <evans@in.tum.de>
Sun, 9 May 2010 15:55:32 +0000 (15:55 +0000)
src/testing/test_testing.c
src/testing/test_testing_connect.c
src/testing/test_testing_data_topology_clique.conf
src/testing/test_testing_data_topology_ring.conf
src/testing/test_testing_group.c
src/testing/test_testing_topology.c
src/testing/testing.c
src/testing/testing_group.c

index 925579a71784bf327300fc2f4731eb36a47c83db..b33f3ee1b33cd4733782ee06075d1fa61c7e03f3 100644 (file)
@@ -33,14 +33,16 @@ end_cb (void *cls, const char *emsg)
 {
   if (emsg != NULL)
     {
-      fprintf (stderr, "Error terminaing daemon: `%s'\n",
-              emsg);
-      return;
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Ending with error: %s\n", emsg);
+      ok = 1;
     }
+  else
+    {
 #if VERBOSE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon terminated, will now exit.\n");
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Daemon terminated, will now exit.\n");
 #endif
-  ok = 0;
+      ok = 0;
+    }
 }
 
 static void
@@ -54,7 +56,7 @@ my_cb (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Daemon `%s' started, will now stop it.\n", GNUNET_i2s (id));
 #endif
-  GNUNET_TESTING_daemon_stop (d, &end_cb, NULL);
+  GNUNET_TESTING_daemon_stop (d, &end_cb, NULL, GNUNET_YES);
 }
 
 
@@ -70,7 +72,7 @@ run (void *cls,
 #if VERBOSE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting daemon.\n");
 #endif
-  d = GNUNET_TESTING_daemon_start (sched, cfg, NULL, &my_cb, NULL);
+  d = GNUNET_TESTING_daemon_start (sched, cfg, NULL, NULL, NULL, &my_cb, NULL);
   GNUNET_assert (d != NULL);
 }
 
index 60721298e2196b40c2cfb25a562a3964dce6a3ae..a55207cec4d0ac98961520b3c455462406ec1b30 100644 (file)
@@ -49,26 +49,34 @@ static struct GNUNET_SCHEDULER_Handle *sched;
 static void
 end2_cb (void *cls, const char *emsg)
 {
-  GNUNET_assert (emsg == NULL);
+
+  if (emsg != NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Ending with error: %s\n", emsg);
+      ok = 1;
+    }
+  else
+    {
 #if VERBOSE
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Both daemons terminated, will now exit.\n");
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Both daemons terminated, will now exit.\n");
 #endif
-  ok = 0;
+      ok = 0;
+    }
 }
 
 static void
 end1_cb (void *cls, const char *emsg)
 {
   GNUNET_assert (emsg == NULL);
-  GNUNET_TESTING_daemon_stop (d2, &end2_cb, NULL);
+  GNUNET_TESTING_daemon_stop (d2, &end2_cb, NULL, GNUNET_YES);
   d2 = NULL;
 }
 
 static void
 finish_testing(void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
 {
-  GNUNET_TESTING_daemon_stop (d1, &end1_cb, NULL);
+  GNUNET_TESTING_daemon_stop (d1, &end1_cb, NULL, GNUNET_YES);
   d1 = NULL;
 }
 
@@ -113,7 +121,7 @@ my_cb1 (void *cls,
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Daemon `%s' started.\n", GNUNET_i2s (id));
 #endif
-  d2 = GNUNET_TESTING_daemon_start (sched, c2, NULL, &my_cb2, NULL);
+  d2 = GNUNET_TESTING_daemon_start (sched, c2, NULL, NULL, NULL, &my_cb2, NULL);
   GNUNET_assert (d2 != NULL);
 
 }
@@ -134,7 +142,7 @@ run (void *cls,
   GNUNET_CONFIGURATION_parse (c1, "test_testing_connect_peer1.conf");
   c2 = GNUNET_CONFIGURATION_create ();
   GNUNET_CONFIGURATION_parse (c2, "test_testing_connect_peer2.conf");
-  d1 = GNUNET_TESTING_daemon_start (sched, c1, NULL, &my_cb1, NULL);
+  d1 = GNUNET_TESTING_daemon_start (sched, c1, NULL, NULL, NULL, &my_cb1, NULL);
   GNUNET_assert (d1 != NULL);
 }
 
index 11a017c877daea6fab6b47afa79ecdf51f32ae7f..689a45d7408e726c3e0358e49fcece3405331e6b 100644 (file)
@@ -40,7 +40,7 @@ PORT = 2570
 #DEBUG = YES
 
 [testing]
-NUM_PEERS = 3
+NUM_PEERS = 4
 WEAKRANDOM = YES
 TOPOLOGY = 0
 F2F = YES
index 41493887cab4e0e0bbe58c0925191029d467f6d2..480eb639274ccab49f5e70a12b995453a3278c67 100644 (file)
@@ -34,3 +34,4 @@ NUM_PEERS = 5
 WEAKRANDOM = YES
 TOPOLOGY = 3
 F2F = YES
+BLACKLISTING = YES
index d9d22dd0224351c5a70d9db025384b509b5b2821..8458bfbd631e6394a81e756e5d16be1aaa8a1b2e 100644 (file)
@@ -80,6 +80,7 @@ run (void *cls,
   peers_left = NUM_PEERS;
   pg = GNUNET_TESTING_daemons_start (sched, cfg,
                                      peers_left,
+                                     NULL, NULL,
                                      &my_cb, NULL, NULL, NULL, NULL);
   GNUNET_assert (pg != NULL);
 }
index 784c850f35639a2458a7678bc1689b4e8e07fb52..62ffb9a76fe26430b6fa85e8f6e980414d8125d9 100644 (file)
@@ -25,7 +25,7 @@
 #include "gnunet_testing_lib.h"
 #include "gnunet_core_service.h"
 
-#define VERBOSE GNUNET_YES
+#define VERBOSE GNUNET_NO
 
 /**
  * How long until we fail the whole testcase?
@@ -83,6 +83,14 @@ static int transmit_ready_called;
 
 static enum GNUNET_TESTING_Topology topology;
 
+static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* Don't do any blacklisting */
+
+static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
+
+static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL;
+
+static double connect_topology_option_modifier = 0.0;
+
 static char *test_directory;
 
 #define MTYPE 12345
@@ -509,20 +517,13 @@ topology_callback (void *cls,
     }
 }
 
-
 static void
-create_topology ()
+connect_topology ()
 {
   expected_connections = -1;
   if ((pg != NULL) && (peers_left == 0))
     {
-      /* create_topology will read the topology information from
-         the config already contained in the peer group, so should
-         we have create_topology called from start peers?  I think
-         maybe this way is best so that the client can know both
-         when peers are started, and when they are connected.
-       */
-      expected_connections = GNUNET_TESTING_create_topology (pg, topology);
+      expected_connections = GNUNET_TESTING_connect_topology (pg, connection_topology, connect_topology_option, connect_topology_option_modifier);
 #if VERBOSE
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Have %d expected connections\n", expected_connections);
@@ -532,21 +533,52 @@ create_topology ()
   GNUNET_SCHEDULER_cancel (sched, die_task);
   if (expected_connections == GNUNET_SYSERR)
     {
+      die_task = GNUNET_SCHEDULER_add_now (sched,
+                                           &end_badly, "from connect topology (bad return)");
+    }
+
+  die_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                           TEST_TIMEOUT,
+                                           &end_badly, "from connect topology (timeout)");
+}
+
+static void
+create_topology ()
+{
+  peers_left = num_peers; /* Reset counter */
+  if (GNUNET_TESTING_create_topology (pg, topology, blacklist_topology) != GNUNET_SYSERR)
+    {
+#if VERBOSE
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Topology set up, now starting peers!\n");
+#endif
+      GNUNET_TESTING_daemons_continue_startup(pg);
+    }
+  else
+    {
+      GNUNET_SCHEDULER_cancel (sched, die_task);
       die_task = GNUNET_SCHEDULER_add_now (sched,
                                            &end_badly, "from create topology (bad return)");
     }
+  GNUNET_SCHEDULER_cancel (sched, die_task);
   die_task = GNUNET_SCHEDULER_add_delayed (sched,
                                            TEST_TIMEOUT,
-                                           &end_badly, "from create topology (timeout)");
+                                           &end_badly, "from continue startup (timeout)");
 }
 
 
 static void
-my_cb (void *cls,
+peers_started_callback (void *cls,
        const struct GNUNET_PeerIdentity *id,
        const struct GNUNET_CONFIGURATION_Handle *cfg,
        struct GNUNET_TESTING_Daemon *d, const char *emsg)
 {
+  if (emsg != NULL)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Failed to start daemon with error: `%s'\n",
+                  emsg);
+      return;
+    }
   GNUNET_assert (id != NULL);
 #if VERBOSE
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
@@ -566,12 +598,54 @@ my_cb (void *cls,
       die_task = GNUNET_SCHEDULER_add_delayed (sched,
                                                GNUNET_TIME_relative_multiply
                                                (GNUNET_TIME_UNIT_MINUTES, 5),
-                                               &end_badly, "from my_cb");
-      create_topology ();
+                                               &end_badly, "from peers_started_callback");
+      connect_topology ();
       ok = 0;
     }
 }
 
+/**
+ * Callback indicating that the hostkey was created for a peer.
+ *
+ * @param cls NULL
+ * @param id the peer identity
+ * @param d the daemon handle (pretty useless at this point, remove?)
+ * @param emsg non-null on failure
+ */
+void hostkey_callback (void *cls,
+                       const struct GNUNET_PeerIdentity *id,
+                       struct GNUNET_TESTING_Daemon *d,
+                       const char *emsg)
+{
+  if (emsg != NULL)
+    {
+      GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Hostkey callback received error: %s\n", emsg);
+    }
+
+#if VERBOSE
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Hostkey created for peer `%s'\n",
+                GNUNET_i2s(id));
+#endif
+    peers_left--;
+    if (peers_left == 0)
+      {
+#if VERBOSE
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "All %d hostkeys created, now creating topology!\n",
+                    num_peers);
+#endif
+        GNUNET_SCHEDULER_cancel (sched, die_task);
+        /* Set up task in case topology creation doesn't finish
+         * within a reasonable amount of time */
+        die_task = GNUNET_SCHEDULER_add_delayed (sched,
+                                                 GNUNET_TIME_relative_multiply
+                                                 (GNUNET_TIME_UNIT_MINUTES, 5),
+                                                 &end_badly, "from hostkey_callback");
+        GNUNET_SCHEDULER_add_now(sched, &create_topology, NULL);
+        ok = 0;
+      }
+}
 
 static void
 run (void *cls,
@@ -580,6 +654,10 @@ run (void *cls,
      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
 {
   unsigned long long topology_num;
+  unsigned long long connect_topology_num;
+  unsigned long long blacklist_topology_num;
+  unsigned long long connect_topology_option_num;
+  char *connect_topology_option_modifier_string;
   sched = s;
   ok = 1;
 
@@ -605,6 +683,36 @@ run (void *cls,
                                              &topology_num))
     topology = topology_num;
 
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology",
+                                             &connect_topology_num))
+    connection_topology = connect_topology_num;
+
+  if (GNUNET_YES ==
+        GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology_option",
+                                               &connect_topology_option_num))
+    connect_topology_option = connect_topology_option_num;
+
+  if (GNUNET_YES ==
+        GNUNET_CONFIGURATION_get_value_string (cfg, "testing", "connect_topology_option_modifier",
+                                               &connect_topology_option_modifier_string))
+    {
+      if (sscanf(connect_topology_option_modifier_string, "%lf", &connect_topology_option_modifier) != 1)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+        _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
+        connect_topology_option_modifier_string,
+        "connect_topology_option_modifier",
+        "TESTING");
+        GNUNET_free (connect_topology_option_modifier_string);
+      }
+    }
+
+  if (GNUNET_YES ==
+      GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "blacklist_topology",
+                                             &blacklist_topology_num))
+    blacklist_topology = blacklist_topology_num;
+
   if (GNUNET_SYSERR ==
       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
                                              &num_peers))
@@ -621,7 +729,7 @@ run (void *cls,
                                            &end_badly, "didn't start all daemons in reasonable amount of time!!!");
 
   pg = GNUNET_TESTING_daemons_start (sched, cfg,
-                                     peers_left, &my_cb, NULL,
+                                     peers_left, &hostkey_callback, NULL, &peers_started_callback, NULL,
                                      &topology_callback, NULL, NULL);
 
 }
index fcc8c97678022ed723a120b1e39fbeb5c10bf4ff..65441c1882ab337402221c0c4b5180a3eeb9237e 100644 (file)
@@ -37,7 +37,7 @@
 #include "gnunet_transport_service.h"
 #include "gnunet_hello_lib.h"
 
-#define DEBUG_TESTING GNUNET_NO
+#define DEBUG_TESTING GNUNET_YES
 #define DEBUG_TESTING_RECONNECT GNUNET_YES
 
 /**
@@ -50,7 +50,7 @@
  * How many times are we willing to try to wait for "scp" or
  * "gnunet-service-arm" to complete (waitpid) before giving up?
  */
-#define MAX_EXEC_WAIT_RUNS 50
+#define MAX_EXEC_WAIT_RUNS 250
 
 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
 
@@ -126,7 +126,7 @@ testing_init (void *cls,
     {
       d->server = NULL;
       if (GNUNET_YES == d->dead)
-        GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
+        GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES);
       else if (NULL != cb)
         cb (d->cb_cls, NULL, d->cfg, d,
             _("Failed to connect to core service\n"));
@@ -141,7 +141,7 @@ testing_init (void *cls,
   d->server = server;
   d->running = GNUNET_YES;
   if (GNUNET_YES == d->dead)
-    GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
+    GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES);
   else if (NULL != cb)
     cb (d->cb_cls, my_identity, d->cfg, d, NULL);
 #if DEBUG_TESTING
@@ -155,7 +155,7 @@ testing_init (void *cls,
   if (d->th == NULL)
     {
       if (GNUNET_YES == d->dead)
-        GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls);
+        GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls, GNUNET_YES);
       else if (NULL != d->cb)
         d->cb (d->cb_cls, &d->id, d->cfg, d,
             _("Failed to connect to transport service!\n"));
@@ -180,6 +180,9 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
   enum GNUNET_OS_ProcessStatusType type;
   unsigned long code;
   char *dst;
+  int bytes_read;
+  static char hostkeybuf[105];
+  static const char temphostkey[104];
 
 #if DEBUG_TESTING
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -227,6 +230,187 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
       d->phase = SP_COPIED;
       /* fall-through */
     case SP_COPIED:
+      /* Start create hostkey process */
+      d->pipe_stdout = GNUNET_DISK_pipe(GNUNET_NO);
+      if (d->pipe_stdout == NULL)
+        {
+          cb = d->cb;
+          d->cb = NULL;
+          if (NULL != cb)
+            cb (d->cb_cls,
+                NULL,
+                d->cfg,
+                d,
+                (NULL == d->hostname)
+                ? _("Failed to create pipe for `gnunet-peerinfo' process.\n")
+                : _("Failed to create pipe for `ssh' process.\n"));
+          return;
+        }
+      if (NULL == d->hostname)
+        {
+#if DEBUG_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Starting `%s', with command `%s %s %s %s'.\n",
+                      "gnunet-peerinfo", "gnunet-peerinfo", "-c", d->cfgfile,
+                      "-sq");
+#endif
+          d->pid = GNUNET_OS_start_process (NULL, d->pipe_stdout, "gnunet-peerinfo",
+                                            "gnunet-peerinfo",
+                                            "-c", d->cfgfile,
+                                            "-sq", NULL);
+          GNUNET_DISK_pipe_close_end(d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
+        }
+      else
+        {
+          if (d->username != NULL)
+            GNUNET_asprintf (&dst, "%s@%s", d->username, d->hostname);
+          else
+            dst = GNUNET_strdup (d->hostname);
+
+#if DEBUG_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      "Starting `%s', with command `%s %s %s %s %s %s'.\n",
+                      "gnunet-peerinfo", "ssh", dst, "gnunet-peerinfo", "-c", d->cfgfile,
+                      "-sq");
+#endif
+          d->pid = GNUNET_OS_start_process (NULL, d->pipe_stdout, "ssh",
+                                            "ssh",
+                                            dst,
+                                            "gnunet-peerinfo",
+                                            "-c", d->cfgfile, "-sq", NULL);
+          GNUNET_DISK_pipe_close_end(d->pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
+          GNUNET_free (dst);
+        }
+      if (-1 == d->pid)
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                      _("Could not start `%s' process to create hostkey.\n"),
+                      (NULL == d->hostname) ? "gnunet-peerinfo" : "ssh");
+          cb = d->cb;
+          d->cb = NULL;
+          if (NULL != cb)
+            cb (d->cb_cls,
+                NULL,
+                d->cfg,
+                d,
+                (NULL == d->hostname)
+                ? _("Failed to start `gnunet-peerinfo' process.\n")
+                : _("Failed to start `ssh' process.\n"));
+          GNUNET_DISK_pipe_close(d->pipe_stdout);
+          return;
+        }
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Started `%s', waiting for hostkey.\n",
+                  "gnunet-peerinfo");
+#endif
+      d->phase = SP_HOSTKEY_CREATE;
+      d->wait_runs = 0;
+      d->task
+        = GNUNET_SCHEDULER_add_delayed (d->sched,
+                                        GNUNET_CONSTANTS_EXEC_WAIT,
+                                        &start_fsm, d);
+      break;
+    case SP_HOSTKEY_CREATE:
+
+      bytes_read = GNUNET_DISK_file_read(GNUNET_DISK_pipe_handle(d->pipe_stdout, GNUNET_DISK_PIPE_END_READ), &hostkeybuf, sizeof(hostkeybuf));
+      if (bytes_read == 104) /* Success, we have read in the hostkey */
+        {
+          if (hostkeybuf[103] == '\n')
+            hostkeybuf[103] = '\0';
+          else
+            GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Malformed output from gnunet-peerinfo!\n");
+          memcpy(&temphostkey, &hostkeybuf, bytes_read);
+
+          if (GNUNET_OK != GNUNET_CRYPTO_hash_from_string (&temphostkey[0],
+                                                           &d->id.hashPubKey))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to convert string to peer identity!\n");
+            }
+          else
+            {
+              GNUNET_DISK_pipe_close(d->pipe_stdout);
+              d->pipe_stdout = NULL;
+            }
+        }
+
+      if (GNUNET_OK != GNUNET_OS_process_status (d->pid, &type, &code))
+        {
+          d->wait_runs++;
+          if (d->wait_runs > MAX_EXEC_WAIT_RUNS)
+            {
+              cb = d->cb;
+              d->cb = NULL;
+              if (NULL != cb)
+                cb (d->cb_cls,
+                    NULL,
+                    d->cfg,
+                    d,
+                    (NULL == d->hostname)
+                    ? _("`gnunet-peerinfo' does not seem to terminate.\n")
+                    : _("`ssh' does not seem to terminate.\n"));
+
+              GNUNET_DISK_pipe_close(d->pipe_stdout);
+              return;
+            }
+          /* wait some more */
+          d->task
+            = GNUNET_SCHEDULER_add_delayed (d->sched,
+                                            GNUNET_CONSTANTS_EXEC_WAIT,
+                                            &start_fsm, d);
+          return;
+        }
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Successfully got hostkey!\n");
+#endif
+      if (d->pipe_stdout != NULL)
+        {
+          cb = d->cb;
+          d->cb = NULL;
+          if (NULL != cb)
+            cb (d->cb_cls,
+                NULL,
+                d->cfg,
+                d,
+                _("`Failed to get hostkey!\n"));
+          GNUNET_DISK_pipe_close(d->pipe_stdout);
+          return;
+        }
+
+      if (d->hostkey_callback != NULL)
+        {
+          d->hostkey_callback(d->hostkey_cls, &d->id, d, NULL);
+          d->phase = SP_HOSTKEY_CREATED;
+        }
+      else
+        {
+          d->phase = SP_TOPOLOGY_SETUP;
+        }
+
+      /* Fall through */
+    case SP_HOSTKEY_CREATED:
+      /* wait for topology finished */
+      d->wait_runs++;
+      if ((GNUNET_YES == d->dead) || (d->wait_runs > MAX_EXEC_WAIT_RUNS))
+        {
+          cb = d->cb;
+          d->cb = NULL;
+          if (NULL != cb)
+            cb (d->cb_cls,
+                NULL,
+                d->cfg,
+                d,
+                _("`Failed while waiting for topology setup!\n"));
+          return;
+        }
+
+      d->task
+        = GNUNET_SCHEDULER_add_delayed (d->sched,
+                                        GNUNET_CONSTANTS_EXEC_WAIT,
+                                        &start_fsm, d);
+      break;
+    case SP_TOPOLOGY_SETUP:
       /* start GNUnet on remote host */
       if (NULL == d->hostname)
         {
@@ -283,6 +467,7 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
                 (NULL == d->hostname)
                 ? _("Failed to start `gnunet-arm' process.\n")
                 : _("Failed to start `ssh' process.\n"));
+          return;
         }
 #if DEBUG_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -452,6 +637,19 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
     }
 }
 
+/**
+ * Continues GNUnet daemon startup when user wanted to be notified
+ * once a hostkey was generated (for creating friends files, blacklists,
+ * etc.).
+ *
+ * @param daemon the daemon to finish starting
+ */
+void
+GNUNET_TESTING_daemon_continue_startup(struct GNUNET_TESTING_Daemon *daemon)
+{
+  GNUNET_assert(daemon->phase == SP_HOSTKEY_CREATED);
+  daemon->phase = SP_TOPOLOGY_SETUP;
+}
 
 /**
  * Starts a GNUnet daemon.  GNUnet must be installed on the target
@@ -463,6 +661,10 @@ start_fsm (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
  * @param cfg configuration to use
  * @param hostname name of the machine where to run GNUnet
  *        (use NULL for localhost).
+ * @param hostkey_callback function to call once the hostkey has been
+ *        generated for this peer, but it hasn't yet been started
+ *        (NULL to start immediately, otherwise waits on GNUNET_TESTING_daemon_continue_start)
+ * @param hostkey_cls closure for hostkey callback
  * @param cb function to call with the result
  * @param cb_cls closure for cb
  * @return handle to the daemon (actual start will be completed asynchronously)
@@ -471,6 +673,8 @@ struct GNUNET_TESTING_Daemon *
 GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
                              const struct GNUNET_CONFIGURATION_Handle *cfg,
                              const char *hostname,
+                             GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
+                             void *hostkey_cls,
                              GNUNET_TESTING_NotifyDaemonRunning cb,
                              void *cb_cls)
 {
@@ -493,6 +697,8 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
       GNUNET_free (ret);
       return NULL;
     }
+  ret->hostkey_callback = hostkey_callback;
+  ret->hostkey_cls = hostkey_cls;
   ret->cb = cb;
   ret->cb_cls = cb_cls;
   ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
@@ -573,27 +779,140 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched,
 }
 
 
+/**
+ * Restart (stop and start) a GNUnet daemon.
+ *
+ * @param d the daemon that should be restarted
+ * @param cb function called once the daemon is (re)started
+ * @param cb_cls closure for cb
+ */
+void
+GNUNET_TESTING_daemon_restart (struct GNUNET_TESTING_Daemon *d,
+                               GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls)
+{
+  char *arg;
+  char *del_arg;
+
+  del_arg = NULL;
+  if (NULL != d->cb)
+    {
+      d->dead = GNUNET_YES;
+      return;
+    }
+
+  d->cb = cb;
+  d->cb_cls = cb_cls;
+
+  if (d->phase == SP_CONFIG_UPDATE)
+    {
+      GNUNET_SCHEDULER_cancel (d->sched, d->task);
+      d->phase = SP_START_DONE;
+    }
+  if (d->server != NULL)
+    {
+      GNUNET_CORE_disconnect (d->server);
+      d->server = NULL;
+    }
+
+  if (d->th != NULL)
+    {
+      GNUNET_TRANSPORT_get_hello_cancel(d->th, &process_hello, d);
+      GNUNET_TRANSPORT_disconnect(d->th);
+      d->th = NULL;
+    }
+  /* state clean up and notifications */
+  GNUNET_free_non_null(d->hello);
+
+#if DEBUG_TESTING
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
+#endif
+
+   d->phase = SP_START_ARMING;
+
+    /* Check if this is a local or remote process */
+  if (NULL != d->hostname)
+    {
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Stopping gnunet-arm with config `%s' on host `%s'.\n", d->cfgfile, d->hostname);
+#endif
+
+      if (d->username != NULL)
+        GNUNET_asprintf (&arg, "%s@%s", d->username, d->hostname);
+      else
+        arg = GNUNET_strdup (d->hostname);
+
+      d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
+                                        arg, "gnunet-arm",
+#if DEBUG_TESTING
+                                        "-L", "DEBUG",
+#endif
+                                        "-c", d->cfgfile, "-e", "-r", NULL);
+      /* Use -r to restart arm and all services */
+
+      GNUNET_free (arg);
+    }
+  else
+    {
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
+#endif
+      d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
+                                        "gnunet-arm",
+#if DEBUG_TESTING
+                                        "-L", "DEBUG",
+#endif
+                                        "-c", d->cfgfile, "-e", "-r", NULL);
+    }
+
+    GNUNET_free_non_null(del_arg);
+    d->wait_runs = 0;
+    d->task
+      = GNUNET_SCHEDULER_add_delayed (d->sched,
+                                      GNUNET_CONSTANTS_EXEC_WAIT,
+                                      &start_fsm, d);
+
+}
+
+
 /**
  * Stops a GNUnet daemon.
  *
  * @param d the daemon that should be stopped
  * @param cb function called once the daemon was stopped
  * @param cb_cls closure for cb
+ * @param delete_files GNUNET_YES to remove files, GNUNET_NO
+ *        to leave them (i.e. for restarting at a later time,
+ *        or logfile inspection once finished)
  */
 void
 GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
-                            GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
+                            GNUNET_TESTING_NotifyCompletion cb, void *cb_cls,
+                            int delete_files)
 {
   char *arg;
-
+  char *del_arg;
   d->dead_cb = cb;
   d->dead_cb_cls = cb_cls;
 
   if (NULL != d->cb)
     {
+#if DEBUG_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 _("Setting d->dead on peer `%4s'\n"), GNUNET_i2s (&d->id));
+#endif
       d->dead = GNUNET_YES;
       return;
     }
+
+  del_arg = NULL;
+  if (delete_files == GNUNET_YES)
+    {
+      GNUNET_asprintf(&del_arg, "-d");
+    }
+
   if (d->phase == SP_CONFIG_UPDATE)
     {
       GNUNET_SCHEDULER_cancel (d->sched, d->task);
@@ -608,10 +927,9 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
 #if DEBUG_TESTING
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               _("Terminating peer `%4s'\n"), GNUNET_i2s (&d->id));
-  /* sleep(15); Manual check for running */
 #endif
 
-  d->phase = SP_SHUTDOWN_START;
+   d->phase = SP_SHUTDOWN_START;
 
   /* Check if this is a local or remote process */
   if (NULL != d->hostname)
@@ -627,29 +945,30 @@ GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d,
         arg = GNUNET_strdup (d->hostname);
 
       d->pid = GNUNET_OS_start_process (NULL, NULL, "ssh", "ssh",
-                                              arg, "gnunet-arm",
+                                        arg, "gnunet-arm",
 #if DEBUG_TESTING
-                                              "-L", "DEBUG",
+                                        "-L", "DEBUG",
 #endif
-                                              "-c", d->cfgfile, "-e", "-d", "-q", NULL);
+                                        "-c", d->cfgfile, "-e", "-q", del_arg, NULL);
       /* Use -e to end arm, and -d to remove temp files */
 
       GNUNET_free (arg);
     }
   else
-  {
+    {
 #if DEBUG_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Stopping gnunet-arm with config `%s' locally.\n", d->cfgfile);
 #endif
-    d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
-                                            "gnunet-arm",
+      d->pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-arm",
+                                        "gnunet-arm",
 #if DEBUG_TESTING
-                                            "-L", "DEBUG",
+                                        "-L", "DEBUG",
 #endif
-                                            "-c", d->cfgfile, "-e", "-d", "-q", NULL);
-  }
+                                        "-c", d->cfgfile, "-e", "-q", del_arg, NULL);
+    }
 
+  GNUNET_free_non_null(del_arg);
   d->wait_runs = 0;
   d->task
     = GNUNET_SCHEDULER_add_delayed (d->sched,
index e617bc113faaf4ed4aee6836c0e941807e6cd792..e9bc05a7401e311b731bd5d12af207e7ae889d1e 100644 (file)
 
 #define CONNECT_ATTEMPTS 8
 
+/**
+ * Prototype of a function called whenever two peers would be connected
+ * in a certain topology.
+ */
+typedef int (*GNUNET_TESTING_ConnectionProcessor)
+(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second);
+
+struct RestartContext
+{
+  /**
+   * The group of peers being restarted
+   */
+  struct GNUNET_TESTING_PeerGroup *peer_group;
+
+  /**
+   * How many peers have been restarted thus far
+   */
+  unsigned int peers_restarted;
+
+  /**
+   * How many peers got an error when restarting
+   */
+  unsigned int peers_restart_failed;
+
+  /**
+   * The function to call once all peers have been restarted
+   */
+  GNUNET_TESTING_NotifyCompletion callback;
+
+  /**
+   * Closure for callback function
+   */
+  void *callback_cls;
+
+};
+
+struct CreateTopologyContext
+{
+
+  /**
+   * Function to call with number of connections
+   */
+  GNUNET_TESTING_NotifyConnections cont;
+
+  /**
+   * Closure for connection notification
+   */
+  void *cls;
+};
+
+#if OLD
 struct PeerConnection
 {
   /*
@@ -63,6 +114,7 @@ struct PeerConnection
   struct GNUNET_TESTING_Daemon *daemon;
 
 };
+#endif
 
 /**
  * Data we keep per peer.
@@ -83,9 +135,33 @@ struct PeerData
   struct GNUNET_TESTING_Daemon *daemon;
 
   /**
-   * Linked list of peer connections (simply indexes of PeerGroup)
+   * The peergroup this peer belongs to.
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * Linked list of peer connections (pointers)
+   */
+  //struct PeerConnection *connected_peers;
+  /**
+   * Hash map of allowed peer connections (F2F created topology)
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
+
+  /**
+   * Hash map of blacklisted peers
    */
-  struct PeerConnection *connected_peers;
+  struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
+
+  /**
+   * Hash map of peer connections
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
+
+  /**
+   * Temporary hash map of peer connections
+   */
+  struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
 
   /**
    * Total number of connections this peer has
@@ -165,6 +241,31 @@ struct GNUNET_TESTING_PeerGroup
 
 };
 
+/**
+ * Convert unique ID to hash code.
+ *
+ * @param uid unique ID to convert
+ * @param hash set to uid (extended with zeros)
+ */
+static void
+hash_from_uid (uint32_t uid,
+               GNUNET_HashCode *hash)
+{
+  memset (hash, 0, sizeof(GNUNET_HashCode));
+  *((uint32_t*)hash) = uid;
+}
+
+/**
+ * Convert hash code to unique ID.
+ *
+ * @param uid unique ID to convert
+ * @param hash set to uid (extended with zeros)
+ */
+static void
+uid_from_hash (const GNUNET_HashCode *hash, uint32_t *uid)
+{
+  memcpy (uid, hash, sizeof(uint32_t));
+}
 
 struct UpdateContext
 {
@@ -279,41 +380,107 @@ make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, con
   return uc.ret;
 }
 
+
 /*
- * Add entries to the peers connected list
+ * Add entries to the peers connect list
  *
  * @param pg the peer group we are working with
  * @param first index of the first peer
  * @param second index of the second peer
  *
  * @return the number of connections added (can be 0, 1 or 2)
+ *         technically should only be 0 or 2, but the small price
+ *         of iterating over the lists (hashmaps in the future)
+ *         for being sure doesn't bother me!
+ *
+ */
+static int
+add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
+{
+  int added;
+  int add_first;
+  int add_second;
+
+  GNUNET_HashCode hash_first;
+  GNUNET_HashCode hash_second;
+
+  hash_from_uid(first, &hash_first);
+  hash_from_uid(second, &hash_second);
+
+  add_first = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second))
+    {
+      add_first = GNUNET_YES;
+    }
+
+  add_second = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first))
+    {
+      add_second = GNUNET_YES;
+    }
+
+  added = 0;
+  if (add_first)
+    {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+      added++;
+    }
+
+  if (add_second)
+    {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+      added++;
+    }
+
+  return added;
+}
+
+
+/*
+ * Add entries to the peers allowed connections list
  *
- * FIXME: add both, or only add one?
- *      - if both are added, then we have to keep track
- *        when connecting so we don't double connect
- *      - if only one is added, we need to iterate over
- *        both lists to find out if connection already exists
- *      - having both allows the whitelisting/friend file
- *        creation to be easier
+ * @param pg the peer group we are working with
+ * @param first index of the first peer
+ * @param second index of the second peer
  *
- *      -- For now, add both, we have to iterate over each to
- *         check for duplicates anyways, so we'll take the performance
- *         hit assuming we don't have __too__ many connections
+ * @return the number of connections added (can be 0, 1 or 2)
+ *         technically should only be 0 or 2, but the small price
+ *         of iterating over the lists (hashmaps in the future)
+ *         for being sure doesn't bother me!
  *
  */
 static int
-add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
+add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
 {
   int added;
+#if OLD
   struct PeerConnection *first_iter;
   struct PeerConnection *second_iter;
-  int add_first;
-  int add_second;
   struct PeerConnection *new_first;
   struct PeerConnection *new_second;
+#endif
+  int add_first;
+  int add_second;
+
+  GNUNET_HashCode hash_first;
+  GNUNET_HashCode hash_second;
+
+  hash_from_uid(first, &hash_first);
+  hash_from_uid(second, &hash_second);
+
+  add_first = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second))
+    {
+      add_first = GNUNET_YES;
+    }
 
+  add_second = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first))
+    {
+      add_second = GNUNET_YES;
+    }
+#if OLD
   first_iter = pg->peers[first].connected_peers;
-  add_first = GNUNET_YES;
   while (first_iter != NULL)
     {
       if (first_iter->daemon == pg->peers[second].daemon)
@@ -329,31 +496,129 @@ add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigne
         add_second = GNUNET_NO;
       second_iter = second_iter->next;
     }
+#endif
 
   added = 0;
   if (add_first)
     {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+#if OLD
       new_first = GNUNET_malloc(sizeof(struct PeerConnection));
       new_first->daemon = pg->peers[second].daemon;
       new_first->next = pg->peers[first].connected_peers;
       pg->peers[first].connected_peers = new_first;
       pg->peers[first].num_connections++;
+#endif
       added++;
     }
 
   if (add_second)
     {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+#if OLD
       new_second = GNUNET_malloc(sizeof(struct PeerConnection));
       new_second->daemon = pg->peers[first].daemon;
       new_second->next = pg->peers[second].connected_peers;
       pg->peers[second].connected_peers = new_second;
       pg->peers[first].num_connections++;
+#endif
+      added++;
+    }
+
+  return added;
+}
+
+/*
+ * Add entries to the peers blacklisted list
+ *
+ * @param pg the peer group we are working with
+ * @param first index of the first peer
+ * @param second index of the second peer
+ *
+ * @return the number of connections added (can be 0, 1 or 2)
+ *
+ */
+static int
+blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
+{
+  int added;
+  int add_first;
+  int add_second;
+  GNUNET_HashCode hash_first;
+  GNUNET_HashCode hash_second;
+
+  hash_from_uid(first, &hash_first);
+  hash_from_uid(second, &hash_second);
+
+  add_first = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second))
+    {
+      add_first = GNUNET_YES;
+    }
+
+  add_second = GNUNET_NO;
+  if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first))
+    {
+      add_second = GNUNET_YES;
+    }
+
+  added = 0;
+  if (add_first)
+    {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+      added++;
+    }
+
+  if (add_second)
+    {
+      GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
       added++;
     }
 
   return added;
 }
 
+/*
+ * Remove entries from the peers blacklisted list
+ *
+ * @param pg the peer group we are working with
+ * @param first index of the first peer
+ * @param second index of the second peer
+ *
+ * @return the number of connections removed (can be 0, 1 or 2)
+ *
+ */
+static int
+unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
+{
+  int removed;
+  int remove_first;
+  int remove_second;
+  GNUNET_HashCode hash_first;
+  GNUNET_HashCode hash_second;
+
+  hash_from_uid(first, &hash_first);
+  hash_from_uid(second, &hash_second);
+
+  remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second);
+  remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first);
+
+  removed = 0;
+  if (remove_first)
+    {
+      GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon));
+      removed++;
+    }
+
+  if (remove_second)
+    {
+      GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon));
+      removed++;
+    }
+
+  return removed;
+}
+
 /**
  * Scale free network construction as described in:
  *
@@ -369,7 +634,7 @@ add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigne
  * @return the number of connections created
  */
 static int
-create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
+create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
 
   unsigned int total_connections;
@@ -382,7 +647,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
   GNUNET_assert(pg->total > 1);
 
   /* Add a connection between the first two nodes */
-  total_connections = add_connections(pg, 0, 1);
+  total_connections = proc(pg, 0, 1);
 
   for (outer_count = 1; outer_count < pg->total; outer_count++)
     {
@@ -404,7 +669,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
                           "Connecting peer %d to peer %d\n",
                           outer_count, i);
 #endif
-              total_connections += add_connections(pg, outer_count, i);
+              total_connections += proc(pg, outer_count, i);
             }
         }
     }
@@ -413,7 +678,7 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
 }
 
 int
-create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
+create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i, j;
   int nodeToConnect;
@@ -502,7 +767,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
                                                          pg->total);
                 }
               smallWorldConnections +=
-                add_connections (pg, i, randomPeer);
+                proc (pg, i, randomPeer);
             }
           else
             {
@@ -512,7 +777,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
                   nodeToConnect = nodeToConnect - pg->total;
                 }
               connect_attempts +=
-                add_connections (pg, i, nodeToConnect);
+                proc (pg, i, nodeToConnect);
             }
         }
 
@@ -525,7 +790,7 @@ create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
 
 
 static int
-create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
+create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int outer_count, inner_count;
   unsigned int cutoff;
@@ -566,7 +831,7 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
                           "Connecting peer %d to peer %d\n",
                           outer_count, inner_count);
 #endif
-              connect_attempts += add_connections(pg, outer_count, inner_count);
+              connect_attempts += proc(pg, outer_count, inner_count);
             }
         }
     }
@@ -578,7 +843,7 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
 
 
 static int
-create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
+create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i, j, k;
   unsigned int square;
@@ -662,7 +927,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
       else
         nodeToConnect = i - cols + 1;
 
-      connect_attempts += add_connections (pg, i, nodeToConnect);
+      connect_attempts += proc (pg, i, nodeToConnect);
 
       if (i < cols)
         nodeToConnect = (rows * cols) - cols + i;
@@ -670,7 +935,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
         nodeToConnect = i - cols;
 
       if (nodeToConnect < pg->total)
-        connect_attempts += add_connections (pg, i, nodeToConnect);
+        connect_attempts += proc (pg, i, nodeToConnect);
     }
   natLog = log (pg->total);
 #if VERBOSE_TESTING > 2
@@ -703,7 +968,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
                                                              (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
                   /* If random < probability, then connect the two nodes */
                   if (random < probability)
-                    smallWorldConnections += add_connections (pg, j, k);
+                    smallWorldConnections += proc (pg, j, k);
 
                 }
             }
@@ -721,7 +986,7 @@ create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
 
 
 static int
-create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
+create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   double temp_rand;
   unsigned int outer_count;
@@ -759,7 +1024,7 @@ create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
 #endif
           if (temp_rand < probability)
             {
-              connect_attempts += add_connections (pg, outer_count, inner_count);
+              connect_attempts += proc (pg, outer_count, inner_count);
             }
         }
     }
@@ -768,7 +1033,7 @@ create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
 }
 
 static int
-create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
+create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int i;
   unsigned int square;
@@ -820,7 +1085,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
                       "Connecting peer %d to peer %d\n",
                       i, nodeToConnect);
 #endif
-      connect_attempts += add_connections(pg, i, nodeToConnect);
+      connect_attempts += proc(pg, i, nodeToConnect);
 
       /* Second connect to the node immediately above */
       if (i < cols)
@@ -835,7 +1100,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
                       "Connecting peer %d to peer %d\n",
                       i, nodeToConnect);
 #endif
-          connect_attempts += add_connections(pg, i, nodeToConnect);
+          connect_attempts += proc(pg, i, nodeToConnect);
         }
 
     }
@@ -846,7 +1111,7 @@ create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
 
 
 static int
-create_clique (struct GNUNET_TESTING_PeerGroup *pg)
+create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int outer_count;
   unsigned int inner_count;
@@ -864,7 +1129,7 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg)
                       "Connecting peer %d to peer %d\n",
                       outer_count, inner_count);
 #endif
-          connect_attempts += add_connections(pg, outer_count, inner_count);
+          connect_attempts += proc(pg, outer_count, inner_count);
         }
     }
 
@@ -873,7 +1138,7 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg)
 
 
 static int
-create_ring (struct GNUNET_TESTING_PeerGroup *pg)
+create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
 {
   unsigned int count;
   int connect_attempts;
@@ -888,16 +1153,77 @@ create_ring (struct GNUNET_TESTING_PeerGroup *pg)
                       "Connecting peer %d to peer %d\n",
                       count, count + 1);
 #endif
-      connect_attempts += add_connections(pg, count, count + 1);
+      connect_attempts += proc(pg, count, count + 1);
     }
 
   /* Connect the last peer to the first peer */
-  connect_attempts += add_connections(pg, pg->total - 1, 0);
+  connect_attempts += proc(pg, pg->total - 1, 0);
 
   return connect_attempts;
 }
 
 
+/**
+ * Iterator for writing friends of a peer to a file.
+ *
+ * @param cls closure, an open writable file handle
+ * @param key the key the daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that needs to be written.
+ *
+ * @return GNUNET_YES to continue iteration
+ *
+ * TODO: Could replace friend_file_iterator and blacklist_file_iterator
+ *       with a single file_iterator that takes a closure which contains
+ *       the prefix to write before the peer.  Then this could be used
+ *       for blacklisting multiple transports and writing the friend
+ *       file.  I'm sure *someone* will complain loudly about other
+ *       things that negate these functions even existing so no point in
+ *       "fixing" now.
+ */
+static int
+friend_file_iterator (void *cls,
+                  const GNUNET_HashCode * key,
+                  void *value)
+{
+  FILE *temp_friend_handle = cls;
+  struct GNUNET_TESTING_Daemon *peer = value;
+  struct GNUNET_PeerIdentity *temppeer;
+  struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
+
+  temppeer = &peer->id;
+  GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
+  fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
+
+  return GNUNET_YES;
+}
+
+
+/**
+ * Iterator for writing blacklist data to appropriate files.
+ *
+ * @param cls closure, an open writable file handle
+ * @param key the key the daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that needs to be written.
+ *
+ * @return GNUNET_YES to continue iteration
+ */
+static int
+blacklist_file_iterator (void *cls,
+                         const GNUNET_HashCode * key,
+                         void *value)
+{
+  FILE *temp_blacklist_handle = cls;
+  struct GNUNET_TESTING_Daemon *peer = value;
+  struct GNUNET_PeerIdentity *temppeer;
+  struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
+
+  temppeer = &peer->id;
+  GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
+  fprintf(temp_blacklist_handle, "tcp:%s\n", (char *)&peer_enc);
+
+  return GNUNET_YES;
+}
+
 /*
  * Create the friend files based on the PeerConnection's
  * of each peer in the peer group, and copy the files
@@ -910,12 +1236,9 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
 {
   FILE *temp_friend_handle;
   unsigned int pg_iter;
-  struct PeerConnection *connection_iter;
-  struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
   char *temp_service_path;
   pid_t *pidarr;
   char *arg;
-  struct GNUNET_PeerIdentity *temppeer;
   char * mytemp;
   enum GNUNET_OS_ProcessStatusType type;
   unsigned long return_code;
@@ -927,16 +1250,10 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
     {
       mytemp = GNUNET_DISK_mktemp("friends");
+      GNUNET_assert(mytemp != NULL);
       temp_friend_handle = fopen (mytemp, "wt");
-      connection_iter = pg->peers[pg_iter].connected_peers;
-      while (connection_iter != NULL)
-        {
-          temppeer = &connection_iter->daemon->id;
-          GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
-          fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
-          connection_iter = connection_iter->next;
-        }
-
+      GNUNET_assert(temp_friend_handle != NULL);
+      GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle);
       fclose(temp_friend_handle);
 
       if (GNUNET_OK !=
@@ -1029,29 +1346,153 @@ create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
   return ret;
 }
 
-/**
- * Internal notification of a connection, kept so that we can ensure some connections
- * happen instead of flooding all testing daemons with requests to connect.
+
+/*
+ * Create the blacklist files based on the PeerConnection's
+ * of each peer in the peer group, and copy the files
+ * to the appropriate place.
+ *
+ * @param pg the peer group we are dealing with
  */
-static void internal_connect_notify (void *cls,
-                                     const struct GNUNET_PeerIdentity *first,
-                                     const struct GNUNET_PeerIdentity *second,
-                                     const struct GNUNET_CONFIGURATION_Handle *first_cfg,
-                                     const struct GNUNET_CONFIGURATION_Handle *second_cfg,
-                                     struct GNUNET_TESTING_Daemon *first_daemon,
-                                     struct GNUNET_TESTING_Daemon *second_daemon,
-                                     const char *emsg)
+static int
+create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg)
 {
-  struct GNUNET_TESTING_PeerGroup *pg = cls;
-  outstanding_connects--;
+  FILE *temp_friend_handle;
+  unsigned int pg_iter;
+  char *temp_service_path;
+  pid_t *pidarr;
+  char *arg;
+  char *mytemp;
+  enum GNUNET_OS_ProcessStatusType type;
+  unsigned long return_code;
+  int count;
+  int ret;
+  int max_wait = 10;
 
-  pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
+  pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      mytemp = GNUNET_DISK_mktemp("blacklist");
+      GNUNET_assert(mytemp != NULL);
+      temp_friend_handle = fopen (mytemp, "wt");
+      GNUNET_assert(temp_friend_handle != NULL);
+      GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, temp_friend_handle);
+      fclose(temp_friend_handle);
 
-}
+      if (GNUNET_OK !=
+          GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
+                      "SERVICEHOME",
+                      "PATHS");
+          if (UNLINK (mytemp) != 0)
+            GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
+          GNUNET_free (mytemp);
+          break;
+        }
 
-static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
-  struct ConnectContext *connect_context = cls;
+      if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
+        {
+          GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
+          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
+                                         "mv", mytemp, arg, NULL);
+#if VERBOSE_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("Copying file with command cp %s %s\n"), mytemp, arg);
+#endif
+
+          GNUNET_free(arg);
+        }
+      else /* Remote, scp the file to the correct place */
+        {
+          if (NULL != pg->peers[pg_iter].daemon->username)
+            GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
+          else
+            GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
+          pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
+                                         "scp", mytemp, arg, NULL);
+
+#if VERBOSE_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("Copying file with command scp %s %s\n"), mytemp, arg);
+#endif
+          GNUNET_free(arg);
+        }
+      GNUNET_free (temp_service_path);
+      GNUNET_free (mytemp);
+    }
+
+  count = 0;
+  ret = GNUNET_SYSERR;
+  while ((count < max_wait) && (ret != GNUNET_OK))
+    {
+      ret = GNUNET_OK;
+      for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+        {
+#if VERBOSE_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("Checking copy status of file %d\n"), pg_iter);
+#endif
+          if (pidarr[pg_iter] != 0) /* Check for already completed! */
+            {
+              if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
+                {
+                  ret = GNUNET_SYSERR;
+                }
+              else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
+                {
+                  ret = GNUNET_SYSERR;
+                }
+              else
+                {
+                  pidarr[pg_iter] = 0;
+#if VERBOSE_TESTING
+            GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("File %d copied\n"), pg_iter);
+#endif
+                }
+            }
+        }
+      count++;
+      if (ret == GNUNET_SYSERR)
+        {
+          sleep(1);
+        }
+    }
+
+#if VERBOSE_TESTING
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                _("Finished copying all blacklist files!\n"));
+#endif
+  GNUNET_free(pidarr);
+  return ret;
+}
+
+
+/**
+ * Internal notification of a connection, kept so that we can ensure some connections
+ * happen instead of flooding all testing daemons with requests to connect.
+ */
+static void internal_connect_notify (void *cls,
+                                     const struct GNUNET_PeerIdentity *first,
+                                     const struct GNUNET_PeerIdentity *second,
+                                     const struct GNUNET_CONFIGURATION_Handle *first_cfg,
+                                     const struct GNUNET_CONFIGURATION_Handle *second_cfg,
+                                     struct GNUNET_TESTING_Daemon *first_daemon,
+                                     struct GNUNET_TESTING_Daemon *second_daemon,
+                                     const char *emsg)
+{
+  struct GNUNET_TESTING_PeerGroup *pg = cls;
+  outstanding_connects--;
+
+  pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
+
+}
+
+static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+  struct ConnectContext *connect_context = cls;
 
   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
     return;
@@ -1081,54 +1522,154 @@ static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContex
     }
 }
 
+/**
+ * Iterator for actually scheduling connections to be created
+ * between two peers.
+ *
+ * @param cls closure, a GNUNET_TESTING_Daemon
+ * @param key the key the second Daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that the first is to connect to
+ *
+ * @return GNUNET_YES to continue iteration
+ */
+static int
+connect_iterator (void *cls,
+                  const GNUNET_HashCode * key,
+                  void *value)
+{
+  struct PeerData *first = cls;
+  struct GNUNET_TESTING_Daemon *second = value;
+  struct ConnectContext *connect_context;
+
+  connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
+  connect_context->pg = first->pg;
+  connect_context->first = first->daemon;
+  connect_context->second = second;
+  GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
+
+  return GNUNET_YES;
+}
+
+/**
+ * Iterator for copying all entries in the allowed hashmap to the
+ * connect hashmap.
+ *
+ * @param cls closure, a GNUNET_TESTING_Daemon
+ * @param key the key the second Daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that the first is to connect to
+ *
+ * @return GNUNET_YES to continue iteration
+ */
+static int
+copy_topology_iterator (void *cls,
+                  const GNUNET_HashCode * key,
+                  void *value)
+{
+  struct PeerData *first = cls;
+
+  GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+
+  return GNUNET_YES;
+}
+
+/**
+ * Make the peers to connect the same as those that are allowed to be
+ * connected.
+ *
+ * @param pg the peer group
+ */
+static int
+copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
+{
+  unsigned int pg_iter;
+  int ret;
+  int total;
+
+  total = 0;
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &copy_topology_iterator, &pg->peers[pg_iter]);
+      if (GNUNET_SYSERR == ret)
+        return GNUNET_SYSERR;
+
+      total = total + ret;
+    }
+
+  return total;
+}
+
+
 /*
  * Connect the topology as specified by the PeerConnection's
  * of each peer in the peer group
  *
  * @param pg the peer group we are dealing with
+ *
+ * @return the number of connections that will be attempted
  */
-static void
+static int
 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
 {
   unsigned int pg_iter;
+  int ret;
+  int total;
+#if OLD
   struct PeerConnection *connection_iter;
   struct ConnectContext *connect_context;
+#endif
 
+  total = 0;
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
     {
-      connection_iter = pg->peers[pg_iter].connected_peers;
+      ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
+      if (GNUNET_SYSERR == ret)
+        return GNUNET_SYSERR;
+
+      total = total + ret;
+
+#if OLD
+      connection_iter = ;
       while (connection_iter != NULL)
         {
           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
           connect_context->pg = pg;
-          connect_context->first = pg->peers[pg_iter].daemon;
+          connect_context->first = ;
           connect_context->second = connection_iter->daemon;
           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
           connection_iter = connection_iter->next;
         }
+#endif
     }
+  return total;
 }
 
 
 /*
- * Takes a peer group and attempts to create a topology based on the
- * one specified in the configuration file.  Returns the number of connections
- * that will attempt to be created, but this will happen asynchronously(?) so
- * the caller will have to keep track (via the callback) of whether or not
- * the connection actually happened.
+ * Takes a peer group and creates a topology based on the
+ * one specified.  Creates a topology means generates friend
+ * files for the peers so they can only connect to those allowed
+ * by the topology.  This will only have an effect once peers
+ * are started if the FRIENDS_ONLY option is set in the base
+ * config.  Also takes an optional restrict topology which
+ * disallows direct TCP connections UNLESS they are specified in
+ * the restricted topology.
  *
  * @param pg the peer group struct representing the running peers
  * @param topology which topology to connect the peers in
+ * @param restrict_topology allow only direct TCP connections in this topology
+ *                          use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
  *
- * @return the number of connections should be created by the topology, so the
- * caller knows how many to wait for (if it so chooses)
- *
+ * @return the maximum number of connections were all allowed peers
+ *         connected to each other
  */
 int
-GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET_TESTING_Topology topology)
+GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
+                                enum GNUNET_TESTING_Topology topology,
+                                enum GNUNET_TESTING_Topology restrict_topology)
 {
   int ret;
   int num_connections;
+  int unblacklisted_connections;
 
   GNUNET_assert (pg->notify_connection != NULL);
   ret = GNUNET_OK;
@@ -1140,56 +1681,56 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating clique topology\n"));
 #endif
-      num_connections = create_clique (pg);
+      num_connections = create_clique (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating small world (ring) topology\n"));
 #endif
-      num_connections = create_small_world_ring (pg);
+      num_connections = create_small_world_ring (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating small world (2d-torus) topology\n"));
 #endif
-      num_connections = create_small_world (pg);
+      num_connections = create_small_world (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_RING:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating ring topology\n"));
 #endif
-      num_connections = create_ring (pg);
+      num_connections = create_ring (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating 2d torus topology\n"));
 #endif
-      num_connections = create_2d_torus (pg);
+      num_connections = create_2d_torus (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating Erdos-Renyi topology\n"));
 #endif
-      num_connections = create_erdos_renyi (pg);
+      num_connections = create_erdos_renyi (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_INTERNAT:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating InterNAT topology\n"));
 #endif
-      num_connections = create_nated_internet (pg);
+      num_connections = create_nated_internet (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _("Creating Scale Free topology\n"));
 #endif
-      num_connections = create_scale_free (pg);
+      num_connections = create_scale_free (pg, &add_allowed_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_NONE:
       num_connections = 0;
@@ -1202,10 +1743,11 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET
     return GNUNET_SYSERR;
 
   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
-    ret = create_and_copy_friend_files(pg);
-  if (ret == GNUNET_OK)
-    connect_topology(pg);
-  else
+    {
+      ret = create_and_copy_friend_files(pg);
+    }
+
+  if (ret != GNUNET_OK)
     {
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -1213,10 +1755,419 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET
 #endif
       return GNUNET_SYSERR;
     }
+  else
+    {
+#if VERBOSE_TESTING
+          GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                      _("Friend files created/copied successfully!\n"));
+#endif
+    }
+
+  /**
+   * Use the create clique method to initially set all connections
+   * as blacklisted.
+   */
+  create_clique (pg, &blacklist_connections);
+  unblacklisted_connections = 0;
+  /**
+   * Un-blacklist connections as per the topology specified
+   */
+  switch (restrict_topology)
+    {
+    case GNUNET_TESTING_TOPOLOGY_CLIQUE:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but clique topology\n"));
+#endif
+      unblacklisted_connections = create_clique (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but small world (ring) topology\n"));
+#endif
+      unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but small world (2d-torus) topology\n"));
+#endif
+      unblacklisted_connections = create_small_world (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_RING:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but ring topology\n"));
+#endif
+      unblacklisted_connections = create_ring (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but 2d torus topology\n"));
+#endif
+      unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but Erdos-Renyi topology\n"));
+#endif
+      unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_INTERNAT:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but InterNAT topology\n"));
+#endif
+      unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Blacklisting all but Scale Free topology\n"));
+#endif
+      unblacklisted_connections = create_scale_free (pg, &unblacklist_connections);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_NONE:
+      /* Fall through */
+    default:
+      break;
+    }
+
+  if (unblacklisted_connections > 0)
+  {
+    ret = create_and_copy_blacklist_files(pg);
+    if (ret != GNUNET_OK)
+      {
+#if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Failed during blacklist file copying!\n"));
+#endif
+        return GNUNET_SYSERR;
+      }
+    else
+      {
+#if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Blacklist files created/copied successfully!\n"));
+#endif
+      }
+  }
+
 
   return num_connections;
 }
 
+struct RandomContext
+{
+  /**
+   * The peergroup
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * uid of the first peer
+   */
+  uint32_t first_uid;
+
+  /**
+   * Peer data for first peer.
+   */
+  struct PeerData *first;
+
+  /**
+   * Random percentage to use
+   */
+  double percentage;
+};
+
+struct MinimumContext
+{
+  /**
+   * The peergroup
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * uid of the first peer
+   */
+  uint32_t first_uid;
+
+  /**
+   * Peer data for first peer.
+   */
+  struct PeerData *first;
+
+  /**
+   * Number of conns per peer
+   */
+  unsigned int num_to_add;
+};
+
+/**
+ * Iterator for choosing random peers to connect.
+ *
+ * @param cls closure, a RandomContext
+ * @param key the key the second Daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that the first is to connect to
+ *
+ * @return GNUNET_YES to continue iteration
+ */
+static int
+random_connect_iterator (void *cls,
+                  const GNUNET_HashCode * key,
+                  void *value)
+{
+  struct RandomContext *random_ctx = cls;
+  double random_number;
+  uint32_t second_pos;
+  GNUNET_HashCode first_hash;
+  random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
+                   (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
+  if (random_number < random_ctx->percentage)
+  {
+    GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+  }
+  /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
+  uid_from_hash(key, &second_pos);
+  hash_from_uid(random_ctx->first_uid, &first_hash);
+  GNUNET_assert(random_ctx->pg->total > second_pos);
+  GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon));
+
+  return GNUNET_YES;
+}
+
+/**
+ * Iterator for adding at least X peers to a peers connection set.
+ *
+ * @param cls closure, MinimumContext
+ * @param key the key the second Daemon was stored under
+ * @param value the GNUNET_TESTING_Daemon that the first is to connect to
+ *
+ * @return GNUNET_YES to continue iteration
+ */
+static int
+minimum_connect_iterator (void *cls,
+                  const GNUNET_HashCode * key,
+                  void *value)
+{
+  struct MinimumContext *min_ctx = cls;
+  uint32_t second_pos;
+  GNUNET_HashCode first_hash;
+
+  if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
+  {
+    GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+    /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
+    uid_from_hash(key, &second_pos);
+    hash_from_uid(min_ctx->first_uid, &first_hash);
+    GNUNET_assert(min_ctx->pg->total > second_pos);
+    GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon));
+    return GNUNET_YES;
+  }
+  else
+    return GNUNET_NO; /* We can stop iterating, we have enough peers! */
+
+
+}
+
+/**
+ * From the set of connections possible, choose percentage percent of connections
+ * to actually connect.
+ *
+ * @param pg the peergroup we are dealing with
+ * @param percentage what percent of total connections to make
+ */
+void
+choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage)
+{
+  struct RandomContext random_ctx;
+  uint32_t pg_iter;
+
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      random_ctx.first_uid = pg_iter;
+      random_ctx.first = &pg->peers[pg_iter];
+      random_ctx.percentage = percentage;
+      pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
+      GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx);
+      /* Now remove the old connections */
+      GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
+      /* And replace with the random set */
+      pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
+    }
+}
+
+/**
+ * From the set of connections possible, choose at least num connections per
+ * peer.
+ *
+ * @param pg the peergroup we are dealing with
+ * @param num how many connections at least should each peer have (if possible)?
+ */
+void
+choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
+{
+  struct MinimumContext minimum_ctx;
+  uint32_t pg_iter;
+
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
+    }
+
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      minimum_ctx.first_uid = pg_iter;
+      minimum_ctx.first = &pg->peers[pg_iter];
+      minimum_ctx.num_to_add = num;
+      pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
+      GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &minimum_connect_iterator, &minimum_ctx);
+    }
+
+  for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
+    {
+      /* Remove the "old" connections */
+      GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
+      /* And replace with the working set */
+      pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
+    }
+
+}
+
+
+/*
+ * @param pg the peer group struct representing the running peers
+ * @param topology which topology to connect the peers in
+ * @param options options for connecting the topology
+ * @param option_modifier modifier for options that take a parameter
+ *
+ * There are many ways to connect peers that are supported by this function.
+ * To connect peers in the same topology that was created via the
+ * GNUNET_TESTING_create_topology, the topology variable must be set to
+ * GNUNET_TESTING_TOPOLOGY_NONE.  If the topology variable is specified,
+ * a new instance of that topology will be generated and attempted to be
+ * connected.  This could result in some connections being impossible,
+ * because some topologies are non-deterministic.
+ *
+ */
+int
+GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
+                                 enum GNUNET_TESTING_Topology topology,
+                                 enum GNUNET_TESTING_TopologyOption options,
+                                 double option_modifier)
+{
+  int num_connections;
+
+  switch (topology)
+      {
+      case GNUNET_TESTING_TOPOLOGY_CLIQUE:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating clique topology\n"));
+  #endif
+        num_connections = create_clique (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating small world (ring) topology\n"));
+  #endif
+        num_connections = create_small_world_ring (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating small world (2d-torus) topology\n"));
+  #endif
+        num_connections = create_small_world (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_RING:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating ring topology\n"));
+  #endif
+        num_connections = create_ring (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating 2d torus topology\n"));
+  #endif
+        num_connections = create_2d_torus (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating Erdos-Renyi topology\n"));
+  #endif
+        num_connections = create_erdos_renyi (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_INTERNAT:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating InterNAT topology\n"));
+  #endif
+        num_connections = create_nated_internet (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
+  #if VERBOSE_TESTING
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    _("Creating Scale Free topology\n"));
+  #endif
+        num_connections = create_scale_free (pg, &add_actual_connections);
+        break;
+      case GNUNET_TESTING_TOPOLOGY_NONE:
+        num_connections = copy_allowed_topology(pg);
+        break;
+      default:
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unknown topology specification, can't connect peers!\n");
+        return GNUNET_SYSERR;
+      }
+
+  switch (options)
+    {
+    case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM: /* Create a random subset of total connections based on parameter */
+      choose_random_connections(pg, option_modifier);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM: /* Create at least X connections per peer (if possible!) */
+      choose_minimum(pg, (unsigned int)option_modifier);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_OPTION_DFS: /* Choose a random starting point, randomly walk graph, try to get each peer X connections */
+      //choose_dfs(pg, (int)option_modifier);
+      break;
+    case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
+      /* Fall through */
+    case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
+      /* Fall through */
+    default:
+      break;
+    }
+
+  return connect_topology(pg);
+}
+
+/**
+ * Function which continues a peer group starting up
+ * after successfully generating hostkeys for each peer.
+ *
+ * @param pg the peer group to continue starting
+ *
+ */
+void
+GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
+{
+  unsigned int i;
+
+  for (i = 0; i < pg->total; i++)
+    {
+      GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
+    }
+}
+
 /**
  * Start count gnunetd processes with the same set of transports and
  * applications.  The port numbers (any option called "PORT") will be
@@ -1226,6 +2177,11 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET
  * @param sched scheduler to use
  * @param cfg configuration template to use
  * @param total number of daemons to start
+ * @param hostkey_callback function to call on each peers hostkey generation
+ *        if NULL, peers will be started by this call, if non-null,
+ *        GNUNET_TESTING_daemons_continue_startup must be called after
+ *        successful hostkey generation
+ * @param hostkey_cls closure for hostkey callback
  * @param cb function to call on each daemon that was started
  * @param cb_cls closure for cb
  * @param connect_callback function to call each time two hosts are connected
@@ -1238,6 +2194,8 @@ struct GNUNET_TESTING_PeerGroup *
 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
                               const struct GNUNET_CONFIGURATION_Handle *cfg,
                               unsigned int total,
+                              GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
+                              void *hostkey_cls,
                               GNUNET_TESTING_NotifyDaemonRunning cb,
                               void *cb_cls,
                               GNUNET_TESTING_NotifyConnection
@@ -1366,13 +2324,20 @@ GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
                                              "SERVICEHOME", newservicehome);
       GNUNET_free (newservicehome);
       pg->peers[off].cfg = pcfg;
+      pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total);
+      pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total);
+      pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total);
+      pg->peers[off].pg = pg;
       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
                                                            pcfg,
                                                            hostname,
+                                                           hostkey_callback,
+                                                           hostkey_cls,
                                                            cb, cb_cls);
       if (NULL == pg->peers[off].daemon)
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                     _("Could not start peer number %u!\n"), off);
+
     }
   return pg;
 }
@@ -1390,6 +2355,72 @@ GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int pos
     return NULL;
 }
 
+/**
+ * Prototype of a function that will be called when a
+ * particular operation was completed the testing library.
+ *
+ * @param cls closure
+ * @param emsg NULL on success
+ */
+void restart_callback (void *cls,
+                       const struct GNUNET_PeerIdentity *id,
+                       const struct GNUNET_CONFIGURATION_Handle *cfg,
+                       struct GNUNET_TESTING_Daemon *d,
+                       const char *emsg)
+{
+  struct RestartContext *restart_context = cls;
+
+  if (emsg == NULL)
+    {
+      restart_context->peers_restarted++;
+    }
+  else
+    {
+      restart_context->peers_restart_failed++;
+    }
+
+  if (restart_context->peers_restarted == restart_context->peer_group->total)
+    {
+      restart_context->callback(restart_context->callback_cls, NULL);
+      GNUNET_free(restart_context);
+    }
+  else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total)
+    {
+      restart_context->callback(restart_context->callback_cls, "Failed to restart peers!");
+      GNUNET_free(restart_context);
+    }
+
+}
+
+/**
+ * Restart all peers in the given group.
+ *
+ * @param pg the handle to the peer group
+ * @param timeout how long to wait on failure
+ * @param callback function to call on completion (or failure)
+ * @param callback_cls closure for the callback function
+ */
+void
+GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls)
+{
+  struct RestartContext *restart_context;
+  unsigned int off;
+
+  if (pg->total > 0)
+    {
+      restart_context = GNUNET_malloc(sizeof(struct RestartContext));
+      restart_context->peer_group = pg;
+      restart_context->peers_restarted = 0;
+      restart_context->callback = callback;
+      restart_context->callback_cls = callback_cls;
+
+      for (off = 0; off < pg->total; off++)
+        {
+          GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context);
+        }
+    }
+}
+
 /**
  * Shutdown all peers started in the given group.
  *
@@ -1399,8 +2430,6 @@ void
 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
 {
   unsigned int off;
-  struct PeerConnection *pos;
-  struct PeerConnection *next;
 
   for (off = 0; off < pg->total; off++)
     {
@@ -1410,17 +2439,13 @@ GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
          as well... */
 
       if (NULL != pg->peers[off].daemon)
-        GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
+        GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL, GNUNET_YES);
       if (NULL != pg->peers[off].cfg)
         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
 
-      pos = pg->peers[off].connected_peers;
-      while (pos != NULL)
-        {
-          next = pos->next;
-          GNUNET_free(pos);
-          pos = next;
-        }
+      GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers);
+      GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers);
+      GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers);
 
     }
   GNUNET_free (pg->peers);