starting point for reading topology from file
[oweals/gnunet.git] / src / testing / testing_group.c
index 2369738b81cbc70790716625dc4313dbd55abde6..f3eeb906940b6fa9876c0a47e47d4e76adc77d39 100644 (file)
@@ -229,6 +229,21 @@ struct CreateTopologyContext
   void *cls;
 };
 
+enum States
+{
+  /** Waiting to read number of peers */
+  NUM_PEERS,
+
+  /** Should find next peer index */
+  PEER_INDEX,
+
+  /** Should find colon */
+  COLON,
+
+  /** Should read other peer index, space, or endline */
+  OTHER_PEER_INDEX
+};
+
 #if OLD
 struct PeerConnection
 {
@@ -601,6 +616,19 @@ struct ConnectContext
   struct ConnectTopologyContext *ct_ctx;
 };
 
+struct UnblacklistContext
+{
+  /**
+   * The peergroup
+   */
+  struct GNUNET_TESTING_PeerGroup *pg;
+
+  /**
+   * uid of the first peer
+   */
+  uint32_t first_uid;
+};
+
 /**
  * Convert unique ID to hash code.
  *
@@ -700,6 +728,11 @@ GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
        */
     "NONE",
 
+      /**
+       * Read the topology from a file.
+       */
+    "FROM_FILE",
+
     NULL
   };
 
@@ -842,10 +875,11 @@ update_config (void *cls,
                            "/tmp/test-service-%s-%u", section, ctx->upnum++);
           value = uval;
         }
-      else if (GNUNET_YES ==
+      else if ((GNUNET_YES ==
                GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
                                                       per_host_variable,
-                                                      &num_per_host))
+                                                      &num_per_host)) && (num_per_host > 0))
+
         {
           GNUNET_snprintf (uval,
                            sizeof (uval),
@@ -1346,7 +1380,22 @@ create_scale_free (struct GNUNET_TESTING_PeerGroup *pg,
 
 /**
  * Create a topology given a peer group (set of running peers)
- * and a connection processor.
+ * and a connection processor.  Creates a small world topology
+ * according to the rewired ring construction.  The basic
+ * behavior is that a ring topology is created, but with some
+ * probability instead of connecting a peer to the next
+ * neighbor in the ring a connection will be created to a peer
+ * selected uniformly at random.   We use the TESTING
+ * PERCENTAGE option to specify what number of
+ * connections each peer should have.  Default is 2,
+ * which makes the ring, any given number is multiplied by
+ * the log of the network size; i.e. a PERCENTAGE of 2 makes
+ * each peer have on average 2logn connections.  The additional
+ * connections are made at increasing distance around the ring
+ * from the original peer, or to random peers based on the re-
+ * wiring probability. The TESTING
+ * PROBABILITY option is used as the probability that a given
+ * connection is rewired.
  *
  * @param pg the peergroup to create the topology on
  * @param proc the connection processor to call to actually set
@@ -1363,7 +1412,7 @@ create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
   int nodeToConnect;
   unsigned int natLog;
   unsigned int randomPeer;
-  double random, logNModifier, percentage;
+  double random, logNModifier, probability;
   unsigned int smallWorldConnections;
   int connsPerPeer;
   char *p_string;
@@ -1375,7 +1424,7 @@ create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
   logNModifier = 0.5;           /* FIXME: default value? */
   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
                                                           "TESTING",
-                                                          "LOGNMODIFIER",
+                                                          "PERCENTAGE",
                                                           &p_string))
     {
       if (sscanf (p_string, "%lf", &logNModifier) != 1)
@@ -1385,13 +1434,13 @@ create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
                     p_string, "LOGNMODIFIER", "TESTING");
       GNUNET_free (p_string);
     }
-  percentage = 0.5;             /* FIXME: default percentage? */
+  probability = 0.5;             /* FIXME: default percentage? */
   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
                                                           "TESTING",
-                                                          "PERCENTAGE",
+                                                          "PROBABILITY",
                                                           &p_string))
     {
-      if (sscanf (p_string, "%lf", &percentage) != 1)
+      if (sscanf (p_string, "%lf", &probability) != 1)
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                     _
                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
@@ -1404,6 +1453,8 @@ create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
   if (connsPerPeer % 2 == 1)
     connsPerPeer += 1;
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."), connsPerPeer);
+
   smallWorldConnections = 0;
   connect_attempts = 0;
   for (i = 0; i < pg->total; i++)
@@ -1430,7 +1481,7 @@ create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
             ((double)
              GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
                                        UINT64_MAX) / ((double) UINT64_MAX));
-          if (random < percentage)
+          if (random < probability)
             {
               /* Connect to uniformly selected random peer */
               randomPeer =
@@ -1489,14 +1540,14 @@ create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg,
   nat_percentage = 0.6;         /* FIXME: default percentage? */
   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
                                                           "TESTING",
-                                                          "NATPERCENTAGE",
+                                                          "PERCENTAGE",
                                                           &p_string))
     {
       if (sscanf (p_string, "%lf", &nat_percentage) != 1)
         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                     _
                     ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
-                    p_string, "NATPERCENTAGE", "TESTING");
+                    p_string, "PERCENTAGE", "TESTING");
       GNUNET_free (p_string);
     }
 
@@ -1885,6 +1936,63 @@ create_clique (struct GNUNET_TESTING_PeerGroup *pg,
   return connect_attempts;
 }
 
+/**
+ * Iterator over hash map entries.
+ *
+ * @param cls closure the peer group
+ * @param key the key stored in the hashmap is the
+ *            index of the peer to connect to
+ * @param value value in the hash map, handle to the peer daemon
+ * @return GNUNET_YES if we should continue to
+ *         iterate,
+ *         GNUNET_NO if not.
+ */
+static int
+unblacklist_iterator (void *cls,
+                      const GNUNET_HashCode * key,
+                      void *value)
+{
+  struct UnblacklistContext *un_ctx = cls;
+  uint32_t second_pos;
+
+  uid_from_hash (key, &second_pos);
+
+  unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
+
+  return GNUNET_YES;
+}
+
+/**
+ * Create a blacklist topology based on the allowed topology
+ * which disallows any connections not in the allowed topology
+ * at the transport level.
+ *
+ * @param pg the peergroup to create the topology on
+ * @param proc the connection processor to call to allow
+ *        up connections between two peers
+ *
+ * @return the number of connections that were set up
+ *
+ */
+static unsigned int
+copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
+             GNUNET_TESTING_ConnectionProcessor proc)
+{
+  struct UnblacklistContext un_ctx;
+  unsigned int count;
+  unsigned int total;
+
+  un_ctx.pg = pg;
+  total = 0;
+  for (count = 0; count < pg->total - 1; count++)
+    {
+      un_ctx.first_uid = count;
+      total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, &unblacklist_iterator, &un_ctx);
+    }
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total);
+  return total;
+}
+
 /**
  * Create a topology given a peer group (set of running peers)
  * and a connection processor.
@@ -1918,6 +2026,145 @@ create_line (struct GNUNET_TESTING_PeerGroup *pg,
   return connect_attempts;
 }
 
+/**
+ * Create a topology given a peer group (set of running peers)
+ * and a connection processor.
+ *
+ * @param pg the peergroup to create the topology on
+ * @param filename the file to read topology information from
+ * @param proc the connection processor to call to actually set
+ *        up connections between two peers
+ *
+ * @return the number of connections that were set up
+ *
+ */
+static unsigned int
+create_from_file (struct GNUNET_TESTING_PeerGroup *pg,
+                  char *filename,
+                  GNUNET_TESTING_ConnectionProcessor proc)
+{
+  int connect_attempts;
+  unsigned int first_peer_index;
+  unsigned int second_peer_index;
+  connect_attempts = 0;
+  struct stat frstat;
+  int count;
+  char *data;
+  char *buf;
+  unsigned int total_peers;
+
+  enum States curr_state;
+
+  if (GNUNET_OK != GNUNET_DISK_file_test (filename))
+      GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ
+        | GNUNET_DISK_PERM_USER_WRITE);
+
+  if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Could not open file specified for topology!");
+      return connect_attempts;
+    }
+
+  data = GNUNET_malloc_large (frstat.st_size);
+  GNUNET_assert(data != NULL);
+  if (frstat.st_size !=
+      GNUNET_DISK_fn_read (filename, data, frstat.st_size))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Could not read file %s specified for host list, ending test!", filename);
+      GNUNET_free (data);
+      return connect_attempts;
+    }
+
+  buf = data;
+  count = 0;
+  /* First line should contain a single integer, specifying the number of peers */
+  /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
+  curr_state = NUM_PEERS;
+  while (count < frstat.st_size - 1)
+    {
+      if ((buf[count] == '\n') || (buf[count] == ' '))
+      {
+        count++;
+        continue;
+      }
+
+      switch (curr_state)
+      {
+        case NUM_PEERS:
+          if (1 != sscanf(&buf[count], "%u", &total_peers))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read number of peers from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read %u total peers in topology\n", total_peers);
+          curr_state = PEER_INDEX;
+          while((buf[count] != '\n') && (count < frstat.st_size - 1))
+            count++;
+          count++;
+          break;
+        case PEER_INDEX:
+          if (1 != sscanf(&buf[count], "%u", &first_peer_index))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read peer index from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read next peer index %u\n", first_peer_index);
+          while((buf[count] != ':') && (count < frstat.st_size - 1))
+            count++;
+          count++;
+          curr_state = OTHER_PEER_INDEX;
+          break;
+        case COLON:
+          if (1 == sscanf(&buf[count], ":"))
+            curr_state = OTHER_PEER_INDEX;
+          count++;
+          break;
+        case OTHER_PEER_INDEX:
+          if (1 != sscanf(&buf[count], "%u", &second_peer_index))
+            {
+              GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to peer index from topology file!\n");
+              GNUNET_free_non_null(data);
+              return connect_attempts;
+            }
+          GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read second peer index %u\n", second_peer_index);
+          while((buf[count] != '\n') && (buf[count] != ' ') && (count < frstat.st_size - 1))
+            count++;
+          if (buf[count] == '\n')
+          {
+            curr_state = PEER_INDEX;
+          }
+          else if (buf[count] != ' ')
+          {
+            curr_state = OTHER_PEER_INDEX;
+          }
+          count++;
+          curr_state = OTHER_PEER_INDEX;
+          break;
+        default:
+          GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Found bad data in topology file while in state %d!\n", curr_state);
+          GNUNET_break(0);
+          return connect_attempts;
+      }
+
+    }
+#if 0
+  /* Connect each peer to the next highest numbered peer */
+  for (count = 0; count < pg->total - 1; count++)
+    {
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  "Connecting peer %d to peer %d\n", first_peer_index, second_peer_index);
+#endif
+      connect_attempts += proc (pg, first_peer_index, second_peer_index);
+    }
+#endif
+  return connect_attempts;
+}
+
 /**
  * Create a topology given a peer group (set of running peers)
  * and a connection processor.
@@ -2013,13 +2260,13 @@ static int
 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
 {
   struct BlacklistContext *blacklist_ctx = cls;
-  //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);
+  GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
   fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
            blacklist_ctx->transport, (char *) &peer_enc);
 
@@ -2195,6 +2442,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
   unsigned int i;
   char *pos;
   char *temp_transports;
+  int entry_count;
 
   procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
@@ -2216,7 +2464,7 @@ create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
             {
               temp_transports[i] = '\0';
               blacklist_ctx.transport = pos;
-              GNUNET_CONTAINER_multihashmap_iterate (pg->
+              entry_count = GNUNET_CONTAINER_multihashmap_iterate (pg->
                                                      peers
                                                      [pg_iter].blacklisted_peers,
                                                      &blacklist_file_iterator,
@@ -2504,6 +2752,9 @@ copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
  * of each peer in the peer group
  *
  * @param pg the peer group we are dealing with
+ * @param notify_callback callback to notify when finished
+ * @param notify_cls closure for notify callback
+ *
  * @return the number of connections that will be attempted
  */
 static int
@@ -2598,6 +2849,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
   int ret;
   unsigned int num_connections;
   int unblacklisted_connections;
+  char *filename;
 
   switch (topology)
     {
@@ -2661,6 +2913,21 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
 #endif
       num_connections = create_line (pg, &add_allowed_connections);
       break;
+    case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
+#if VERBOSE_TESTING
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                  _("Creating topology from file!\n"));
+#endif
+      if (GNUNET_OK ==
+          GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing", "topology_file",
+                                                 &filename))
+        num_connections = create_from_file (pg, filename, &add_allowed_connections);
+      else
+      {
+        GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!");
+        num_connections = 0;
+      }
+      break;
     case GNUNET_TESTING_TOPOLOGY_NONE:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2696,8 +2963,7 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
     }
 
   /* Use the create clique method to initially set all connections as blacklisted. */
-  if (restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE)
-    create_clique (pg, &blacklist_connections);
+  create_clique (pg, &blacklist_connections);
 
   unblacklisted_connections = 0;
   /* Un-blacklist connections as per the topology specified */
@@ -2775,17 +3041,23 @@ GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
       unblacklisted_connections = create_line (pg, &unblacklist_connections);
       break;
     case GNUNET_TESTING_TOPOLOGY_NONE:
+    case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
 #if VERBOSE_TESTING
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   _
                   ("Creating no blacklist topology (all peers can connect at transport level)\n"));
 #endif
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                _
+                ("Creating blacklist topology from allowed\n"));
+    unblacklisted_connections = copy_allowed (pg, &unblacklist_connections);
     default:
       break;
     }
 
   if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
     {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'", restrict_transports);
       ret = create_and_copy_blacklist_files (pg, restrict_transports);
       if (ret != GNUNET_OK)
         {
@@ -3631,7 +3903,10 @@ stats_check_existing (struct GNUNET_TESTING_PeerGroup *pg,
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg, "statistics",
                                              "port", &port))
-    return GNUNET_NO;
+    {
+      GNUNET_free(unix_domain_socket);
+      return GNUNET_NO;
+    }
 
   if (specific_peer->daemon->hostname != NULL)
     GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
@@ -3905,6 +4180,7 @@ internal_hostkey_callback (void *cls,
  *
  * @param cls closure
  * @param id identifier for the daemon, NULL on error
+ * @param cfg config
  * @param d handle for the daemon
  * @param emsg error message (NULL on success)
  */
@@ -4100,7 +4376,8 @@ GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg)
  * @param cb_cls closure for cb
  * @param connect_callback function to call each time two hosts are connected
  * @param connect_callback_cls closure for connect_callback
- * @param hostnames linked list of hosts to use to start peers on (NULL to run on localhost only)
+ * @param hostnames linked list of host structs to use to start peers on
+ *                  (NULL to run on localhost only)
  *
  * @return NULL on error, otherwise handle to control peer group
  */