2 This file is part of GNUnet
3 (C) 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testing/testing_group.c
23 * @brief convenience API for writing testcases for GNUnet
24 * @author Nathan Evans
25 * @author Christian Grothoff
29 #include "gnunet_arm_service.h"
30 #include "gnunet_testing_lib.h"
31 #include "gnunet_core_service.h"
33 #define VERBOSE_TESTING GNUNET_NO
35 #define VERBOSE_TOPOLOGY GNUNET_YES
37 #define DEBUG_CHURN GNUNET_NO
40 * Lowest port used for GNUnet testing. Should be high enough to not
41 * conflict with other applications running on the hosts but be low
42 * enough to not conflict with client-ports (typically starting around
45 #define LOW_PORT 10000
48 * Highest port used for GNUnet testing. Should be low enough to not
49 * conflict with the port range for "local" ports (client apps; see
50 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
52 #define HIGH_PORT 56000
54 #define MAX_OUTSTANDING_CONNECTIONS 40
56 #define MAX_CONCURRENT_HOSTKEYS 10
58 #define MAX_CONCURRENT_STARTING 10
60 #define MAX_CONCURRENT_SHUTDOWN 10
62 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 200)
64 #define CONNECT_ATTEMPTS 21
67 * Prototype of a function called whenever two peers would be connected
68 * in a certain topology.
70 typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor) (struct
71 GNUNET_TESTING_PeerGroup
80 * Context for handling churning a peer group
85 * Callback used to notify of churning finished
87 GNUNET_TESTING_NotifyCompletion cb;
90 * Closure for callback
95 * Number of peers that still need to be started
97 unsigned int num_to_start;
100 * Number of peers that still need to be stopped
102 unsigned int num_to_stop;
105 * Number of peers that failed to start
107 unsigned int num_failed_start;
110 * Number of peers that failed to stop
112 unsigned int num_failed_stop;
115 struct RestartContext
118 * The group of peers being restarted
120 struct GNUNET_TESTING_PeerGroup *peer_group;
123 * How many peers have been restarted thus far
125 unsigned int peers_restarted;
128 * How many peers got an error when restarting
130 unsigned int peers_restart_failed;
133 * The function to call once all peers have been restarted
135 GNUNET_TESTING_NotifyCompletion callback;
138 * Closure for callback function
145 struct ShutdownContext
148 * Total peers to wait for
150 unsigned int total_peers;
153 * Number of peers successfully shut down
155 unsigned int peers_down;
158 * Number of peers failed to shut down
160 unsigned int peers_failed;
163 * Number of peers we have started shutting
164 * down. If too many, wait on them.
166 unsigned int outstanding;
169 * Timeout for shutdown.
171 struct GNUNET_TIME_Relative timeout;
174 * Callback to call when all peers either
175 * shutdown or failed to shutdown
177 GNUNET_TESTING_NotifyCompletion cb;
186 * Individual shutdown context for a particular peer.
188 struct PeerShutdownContext
191 * Pointer to the high level shutdown context.
193 struct ShutdownContext *shutdown_ctx;
196 * The daemon handle for the peer to shut down.
198 struct GNUNET_TESTING_Daemon *daemon;
202 * Individual shutdown context for a particular peer.
204 struct PeerRestartContext
207 * Pointer to the high level restart context.
209 struct ChurnRestartContext *churn_restart_ctx;
212 * The daemon handle for the peer to shut down.
214 struct GNUNET_TESTING_Daemon *daemon;
218 struct CreateTopologyContext
222 * Function to call with number of connections
224 GNUNET_TESTING_NotifyConnections cont;
227 * Closure for connection notification
234 /** Waiting to read number of peers */
237 /** Should find next peer index */
240 /** Should find colon */
243 /** Should read other peer index, space, or endline */
248 struct PeerConnection
253 struct PeerConnection *next;
256 * Pointer to daemon handle
258 struct GNUNET_TESTING_Daemon *daemon;
263 struct InternalStartContext
266 * Pointer to peerdata
268 struct PeerData *peer;
271 * Timeout for peer startup
273 struct GNUNET_TIME_Relative timeout;
276 * Client callback for hostkey notification
278 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
281 * Closure for hostkey_callback
286 * Client callback for peer start notification
288 GNUNET_TESTING_NotifyDaemonRunning start_cb;
296 * Hostname, where to start the peer
298 const char *hostname;
301 * Username to use when connecting to the
304 const char *username;
307 * Pointer to starting memory location of a hostkey
312 * Port to use for ssh.
318 struct ChurnRestartContext
321 * Number of restarts currently in flight.
323 unsigned int outstanding;
326 * Handle to the underlying churn context.
328 struct ChurnContext *churn_ctx;
331 * How long to allow the operation to take.
333 struct GNUNET_TIME_Relative timeout;
337 * Data we keep per peer.
342 * (Initial) configuration of the host.
343 * (initial because clients could change
344 * it and we would not know about those
347 struct GNUNET_CONFIGURATION_Handle *cfg;
350 * Handle for controlling the daemon.
352 struct GNUNET_TESTING_Daemon *daemon;
355 * The peergroup this peer belongs to.
357 struct GNUNET_TESTING_PeerGroup *pg;
360 * Hash map of allowed peer connections (F2F created topology)
362 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
365 * Hash map of blacklisted peers
367 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
370 * Hash map of peer connections
372 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
375 * Temporary hash map of peer connections
377 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
380 * Temporary variable for topology creation, should be reset before
381 * creating any topology so the count is valid once finished.
386 * Context to keep track of peers being started, to
387 * stagger hostkey generation and peer startup.
389 struct InternalStartContext internal_context;
394 * Linked list of per-host data.
404 * SSH username to use when connecting to this host.
409 * SSH port to use when connecting to this host.
414 * Lowest port that we have not yet used
420 struct TopologyIterateContext
423 * Callback for notifying of two connected peers.
425 GNUNET_TESTING_NotifyTopology topology_cb;
428 * Closure for topology_cb
433 * Number of peers currently connected to.
435 unsigned int connected;
438 * Number of peers we have finished iterating.
440 unsigned int completed;
443 * Number of peers total.
448 struct StatsIterateContext
451 * Continuation to call once all stats information has been retrieved.
453 GNUNET_STATISTICS_Callback cont;
456 * Proc function to call on each value received.
458 GNUNET_TESTING_STATISTICS_Iterator proc;
461 * Closure for topology_cb
466 * Number of peers currently connected to.
468 unsigned int connected;
471 * Number of peers we have finished iterating.
473 unsigned int completed;
476 * Number of peers total.
484 struct GNUNET_TESTING_Daemon *daemon;
487 struct StatsCoreContext
490 struct GNUNET_TESTING_Daemon *daemon;
492 * Handle to the statistics service.
494 struct GNUNET_STATISTICS_Handle *stats_handle;
497 * Handle for getting statistics.
499 struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
503 * Handle to a group of GNUnet peers.
505 struct GNUNET_TESTING_PeerGroup
508 * Configuration template.
510 const struct GNUNET_CONFIGURATION_Handle *cfg;
513 * Function to call on each started daemon.
515 //GNUNET_TESTING_NotifyDaemonRunning cb;
523 * Function to call on each topology connection created
525 GNUNET_TESTING_NotifyConnection notify_connection;
528 * Callback for notify_connection
530 void *notify_connection_cls;
533 * Array of information about hosts.
535 struct HostData *hosts;
538 * Number of hosts (size of HostData)
540 unsigned int num_hosts;
543 * Array of "total" peers.
545 struct PeerData *peers;
548 * Number of peers in this group.
553 * At what time should we fail the peer startup process?
555 struct GNUNET_TIME_Absolute max_timeout;
558 * How many peers are being started right now?
560 unsigned int starting;
563 * How many peers have already been started?
565 unsigned int started;
570 struct GNUNET_CONFIGURATION_Handle *ret;
571 const struct GNUNET_CONFIGURATION_Handle *orig;
572 const char *hostname;
578 struct ConnectTopologyContext
581 * How many connections are left to create.
583 unsigned int remaining_connections;
586 * Handle to group of peers.
588 struct GNUNET_TESTING_PeerGroup *pg;
591 * Temp value set for each iteration.
593 struct PeerData *first;
596 * Notification that all peers are connected.
598 GNUNET_TESTING_NotifyCompletion notify_connections_done;
601 * Closure for notify.
606 struct ConnectContext
609 * Peer to connect second to.
611 struct GNUNET_TESTING_Daemon *first;
614 * Peer to connect first to.
616 struct GNUNET_TESTING_Daemon *second;
619 * Higher level topology connection context.
621 struct ConnectTopologyContext *ct_ctx;
624 struct UnblacklistContext
629 struct GNUNET_TESTING_PeerGroup *pg;
632 * uid of the first peer
642 struct GNUNET_TESTING_PeerGroup *pg;
645 * uid of the first peer
650 * Peer data for first peer.
652 struct PeerData *first;
655 * Random percentage to use
660 struct MinimumContext
665 struct GNUNET_TESTING_PeerGroup *pg;
668 * uid of the first peer
673 * Peer data for first peer.
675 struct PeerData *first;
678 * Number of conns per peer
680 unsigned int num_to_add;
683 * Permuted array of all possible connections. Only add the Nth
684 * peer if it's in the Nth position.
686 unsigned int *pg_array;
689 * What number is the current element we are iterating over?
691 unsigned int current;
699 struct GNUNET_TESTING_PeerGroup *pg;
702 * uid of the first peer
707 * uid of the second peer
712 * Peer data for first peer.
714 struct PeerData *first;
717 * Which peer has been chosen as the one to add?
722 * What number is the current element we are iterating over?
724 unsigned int current;
728 * Convert unique ID to hash code.
730 * @param uid unique ID to convert
731 * @param hash set to uid (extended with zeros)
734 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
736 memset (hash, 0, sizeof (GNUNET_HashCode));
737 *((uint32_t *) hash) = uid;
741 * Convert hash code to unique ID.
743 * @param uid unique ID to convert
744 * @param hash set to uid (extended with zeros)
747 uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
749 memcpy (uid, hash, sizeof (uint32_t));
753 * Number of connects we are waiting on, allows us to rate limit
756 static int outstanding_connects;
759 * Get a topology from a string input.
761 * @param topology where to write the retrieved topology
762 * @param topology_string The string to attempt to
763 * get a configuration value from
764 * @return GNUNET_YES if topology string matched a
765 * known topology, GNUNET_NO if not
768 GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
769 const char *topology_string)
772 * Strings representing topologies in enum
774 static const char *topology_strings[] = {
776 * A clique (everyone connected to everyone else).
781 * Small-world network (2d torus plus random links).
786 * Small-world network (ring plus random links).
806 * Certain percentage of peers are unable to communicate directly
807 * replicating NAT conditions
812 * Scale free topology.
817 * Straight line topology.
822 * All peers are disconnected.
827 * Read the topology from a file.
835 if (topology_string == NULL)
837 while (topology_strings[curr] != NULL)
839 if (strcasecmp (topology_strings[curr], topology_string) == 0)
846 *topology = GNUNET_TESTING_TOPOLOGY_NONE;
852 * Get connect topology option from string input.
854 * @param topology_option where to write the retrieved topology
855 * @param topology_string The string to attempt to
856 * get a configuration value from
857 * @return GNUNET_YES if string matched a known
858 * topology option, GNUNET_NO if not
861 GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption
863 const char *topology_string)
866 * Options for connecting a topology as strings.
868 static const char *topology_option_strings[] = {
870 * Try to connect all peers specified in the topology.
875 * Choose a random subset of connections to create.
877 "CONNECT_RANDOM_SUBSET",
880 * Create at least X connections for each peer.
885 * Using a depth first search, create one connection
886 * per peer. If any are missed (graph disconnected)
887 * start over at those peers until all have at least one
893 * Find the N closest peers to each allowed peer in the
894 * topology and make sure a connection to those peers
895 * exists in the connect topology.
900 * No options specified.
908 if (topology_string == NULL)
910 while (NULL != topology_option_strings[curr])
912 if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
914 *topology_option = curr;
919 *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
924 * Function to iterate over options. Copies
925 * the options to the target configuration,
926 * updating PORT values as needed.
929 * @param section name of the section
930 * @param option name of the option
931 * @param value value of the option
934 update_config (void *cls,
935 const char *section, const char *option, const char *value)
937 struct UpdateContext *ctx = cls;
941 char *single_variable;
942 char *per_host_variable;
943 unsigned long long num_per_host;
945 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
947 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
950 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
953 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
957 GNUNET_free (single_variable);
960 if (0 == strcmp (option, "UNIXPATH"))
962 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
963 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
965 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
968 GNUNET_snprintf (uval,
970 "/tmp/test-service-%s-%u", section, ctx->upnum++);
973 else if ((GNUNET_YES ==
974 GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
976 &num_per_host)) && (num_per_host > 0))
979 GNUNET_snprintf (uval,
981 "/tmp/test-service-%s-%u",
982 section, ctx->fdnum % num_per_host);
985 GNUNET_free (single_variable);
986 GNUNET_free (per_host_variable);
990 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
992 value = ctx->hostname;
995 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
1000 * Create a new configuration using the given configuration
1001 * as a template; however, each PORT in the existing cfg
1002 * must be renumbered by incrementing "*port". If we run
1003 * out of "*port" numbers, return NULL.
1005 * @param cfg template configuration
1006 * @param port port numbers to use, update to reflect
1007 * port numbers that were used
1008 * @param upnum number to make unix domain socket names unique
1009 * @param hostname hostname of the controlling host, to allow control connections from
1010 * @param fdnum number used to offset the unix domain socket for grouped processes
1011 * (such as statistics or peerinfo, which can be shared among others)
1013 * @return new configuration, NULL on error
1015 static struct GNUNET_CONFIGURATION_Handle *
1016 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
1018 uint32_t * upnum, const char *hostname, uint32_t * fdnum)
1020 struct UpdateContext uc;
1023 char *allowed_hosts;
1029 uc.ret = GNUNET_CONFIGURATION_create ();
1030 uc.hostname = hostname;
1033 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1034 if (uc.nport >= HIGH_PORT)
1037 GNUNET_CONFIGURATION_destroy (uc.ret);
1041 if (GNUNET_CONFIGURATION_get_value_string
1042 (cfg, "testing", "control_host", &control_host) == GNUNET_OK)
1044 if (hostname != NULL)
1045 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host,
1048 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host);
1050 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM",
1052 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport",
1053 "ACCEPT_FROM", allowed_hosts);
1054 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "ACCEPT_FROM",
1056 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics",
1057 "ACCEPT_FROM", allowed_hosts);
1058 GNUNET_free_non_null (control_host);
1059 GNUNET_free (allowed_hosts);
1063 /* arm needs to know to allow connections from the host on which it is running,
1064 * otherwise gnunet-arm is unable to connect to it in some instances */
1065 if (hostname != NULL)
1067 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname);
1068 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1069 "BINDTO", hostname);
1070 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1071 "BINDTO", hostname);
1072 GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM",
1074 GNUNET_free (allowed_hosts);
1078 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1079 "BINDTO", "127.0.0.1");
1080 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1081 "BINDTO", "127.0.0.1");
1084 *port = (uint16_t) uc.nport;
1093 * Add entries to the peers connect list
1095 * @param pg the peer group we are working with
1096 * @param first index of the first peer
1097 * @param second index of the second peer
1099 * @return the number of connections added
1100 * technically should only be 0 or 2
1104 add_actual_connections (struct GNUNET_TESTING_PeerGroup *pg,
1105 unsigned int first, unsigned int second)
1111 GNUNET_HashCode hash_first;
1112 GNUNET_HashCode hash_second;
1114 hash_from_uid (first, &hash_first);
1115 hash_from_uid (second, &hash_second);
1117 add_first = GNUNET_NO;
1119 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].connect_peers,
1122 add_first = GNUNET_YES;
1125 add_second = GNUNET_NO;
1127 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].connect_peers,
1130 add_second = GNUNET_YES;
1136 GNUNET_assert (GNUNET_OK ==
1137 GNUNET_CONTAINER_multihashmap_put (pg->
1139 [first].connect_peers,
1142 peers[second].daemon,
1143 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1144 pg->peers[first].num_connections++;
1150 GNUNET_assert (GNUNET_OK ==
1151 GNUNET_CONTAINER_multihashmap_put (pg->
1153 [second].connect_peers,
1156 peers[first].daemon,
1157 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1158 pg->peers[second].num_connections++;
1167 * Add entries to the peers allowed connections list
1169 * @param pg the peer group we are working with
1170 * @param first index of the first peer
1171 * @param second index of the second peer
1173 * @return the number of connections added (can be 0, 1 or 2)
1174 * technically should only be 0 or 2, but the small price
1175 * of iterating over the lists (hashmaps in the future)
1176 * for being sure doesn't bother me!
1180 add_allowed_connections (struct GNUNET_TESTING_PeerGroup *pg,
1181 unsigned int first, unsigned int second)
1185 struct PeerConnection *first_iter;
1186 struct PeerConnection *second_iter;
1187 struct PeerConnection *new_first;
1188 struct PeerConnection *new_second;
1193 GNUNET_HashCode hash_first;
1194 GNUNET_HashCode hash_second;
1196 hash_from_uid (first, &hash_first);
1197 hash_from_uid (second, &hash_second);
1199 add_first = GNUNET_NO;
1201 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].allowed_peers,
1204 add_first = GNUNET_YES;
1207 add_second = GNUNET_NO;
1209 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].allowed_peers,
1212 add_second = GNUNET_YES;
1215 first_iter = pg->peers[first].connected_peers;
1216 while (first_iter != NULL)
1218 if (first_iter->daemon == pg->peers[second].daemon)
1219 add_first = GNUNET_NO;
1220 first_iter = first_iter->next;
1223 second_iter = pg->peers[second].connected_peers;
1224 add_second = GNUNET_YES;
1225 while (second_iter != NULL)
1227 if (second_iter->daemon == pg->peers[first].daemon)
1228 add_second = GNUNET_NO;
1229 second_iter = second_iter->next;
1236 GNUNET_assert (GNUNET_OK ==
1237 GNUNET_CONTAINER_multihashmap_put (pg->
1239 [first].allowed_peers,
1242 peers[second].daemon,
1243 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1245 new_first = GNUNET_malloc (sizeof (struct PeerConnection));
1246 new_first->daemon = pg->peers[second].daemon;
1247 new_first->next = pg->peers[first].connected_peers;
1248 pg->peers[first].connected_peers = new_first;
1250 pg->peers[first].num_connections++;
1256 GNUNET_assert (GNUNET_OK ==
1257 GNUNET_CONTAINER_multihashmap_put (pg->
1259 [second].allowed_peers,
1262 peers[first].daemon,
1263 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1265 new_second = GNUNET_malloc (sizeof (struct PeerConnection));
1266 new_second->daemon = pg->peers[first].daemon;
1267 new_second->next = pg->peers[second].connected_peers;
1268 pg->peers[second].connected_peers = new_second;
1269 pg->peers[first].num_connections++;
1271 pg->peers[second].num_connections++;
1279 * Add entries to the peers blacklisted list
1281 * @param pg the peer group we are working with
1282 * @param first index of the first peer
1283 * @param second index of the second peer
1285 * @return the number of connections added (can be 0, 1 or 2)
1289 blacklist_connections (struct GNUNET_TESTING_PeerGroup *pg,
1290 unsigned int first, unsigned int second)
1295 GNUNET_HashCode hash_first;
1296 GNUNET_HashCode hash_second;
1298 hash_from_uid (first, &hash_first);
1299 hash_from_uid (second, &hash_second);
1301 add_first = GNUNET_NO;
1303 GNUNET_CONTAINER_multihashmap_contains (pg->
1304 peers[first].blacklisted_peers,
1307 add_first = GNUNET_YES;
1310 add_second = GNUNET_NO;
1312 GNUNET_CONTAINER_multihashmap_contains (pg->
1313 peers[second].blacklisted_peers,
1316 add_second = GNUNET_YES;
1322 GNUNET_assert (GNUNET_OK ==
1323 GNUNET_CONTAINER_multihashmap_put (pg->
1325 [first].blacklisted_peers,
1328 peers[second].daemon,
1329 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1330 pg->peers[first].num_connections++;
1336 GNUNET_assert (GNUNET_OK ==
1337 GNUNET_CONTAINER_multihashmap_put (pg->
1339 [second].blacklisted_peers,
1342 peers[first].daemon,
1343 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1344 pg->peers[second].num_connections++;
1352 * Remove entries from the peers blacklisted list
1354 * @param pg the peer group we are working with
1355 * @param first index of the first peer
1356 * @param second index of the second peer
1358 * @return the number of connections removed (can be 0, 1 or 2)
1362 unblacklist_connections (struct GNUNET_TESTING_PeerGroup *pg,
1363 unsigned int first, unsigned int second)
1368 GNUNET_HashCode hash_first;
1369 GNUNET_HashCode hash_second;
1371 hash_from_uid (first, &hash_first);
1372 hash_from_uid (second, &hash_second);
1375 GNUNET_CONTAINER_multihashmap_contains (pg->
1376 peers[first].blacklisted_peers,
1379 GNUNET_CONTAINER_multihashmap_contains (pg->
1380 peers[second].blacklisted_peers,
1386 GNUNET_assert (GNUNET_YES ==
1387 GNUNET_CONTAINER_multihashmap_remove (pg->
1389 [first].blacklisted_peers,
1399 GNUNET_assert (GNUNET_YES ==
1400 GNUNET_CONTAINER_multihashmap_remove (pg->
1402 [second].blacklisted_peers,
1414 * Scale free network construction as described in:
1416 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
1418 * Start with a network of "one" peer, then progressively add
1419 * peers up to the total number. At each step, iterate over
1420 * all possible peers and connect new peer based on number of
1421 * existing connections of the target peer.
1423 * @param pg the peer group we are dealing with
1424 * @param proc the connection processor to use
1426 * @return the number of connections created
1429 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg,
1430 GNUNET_TESTING_ConnectionProcessor proc)
1433 unsigned int total_connections;
1434 unsigned int outer_count;
1436 unsigned int previous_total_connections;
1440 GNUNET_assert (pg->total > 1);
1442 /* Add a connection between the first two nodes */
1443 total_connections = proc (pg, 0, 1);
1445 for (outer_count = 1; outer_count < pg->total; outer_count++)
1447 previous_total_connections = total_connections;
1448 for (i = 0; i < outer_count; i++)
1451 pg->peers[i].num_connections /
1452 (double) previous_total_connections;
1455 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1456 UINT64_MAX)) / ((double) UINT64_MAX);
1458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459 "Considering connecting peer %d to peer %d\n",
1462 if (random < probability)
1465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1466 "Connecting peer %d to peer %d\n", outer_count, i);
1468 total_connections += proc (pg, outer_count, i);
1473 return total_connections;
1477 * Create a topology given a peer group (set of running peers)
1478 * and a connection processor. Creates a small world topology
1479 * according to the rewired ring construction. The basic
1480 * behavior is that a ring topology is created, but with some
1481 * probability instead of connecting a peer to the next
1482 * neighbor in the ring a connection will be created to a peer
1483 * selected uniformly at random. We use the TESTING
1484 * PERCENTAGE option to specify what number of
1485 * connections each peer should have. Default is 2,
1486 * which makes the ring, any given number is multiplied by
1487 * the log of the network size; i.e. a PERCENTAGE of 2 makes
1488 * each peer have on average 2logn connections. The additional
1489 * connections are made at increasing distance around the ring
1490 * from the original peer, or to random peers based on the re-
1491 * wiring probability. The TESTING
1492 * PROBABILITY option is used as the probability that a given
1493 * connection is rewired.
1495 * @param pg the peergroup to create the topology on
1496 * @param proc the connection processor to call to actually set
1497 * up connections between two peers
1499 * @return the number of connections that were set up
1503 create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
1504 GNUNET_TESTING_ConnectionProcessor proc)
1508 unsigned int natLog;
1509 unsigned int randomPeer;
1510 double random, logNModifier, probability;
1511 unsigned int smallWorldConnections;
1516 unsigned int useAnd;
1517 int connect_attempts;
1519 logNModifier = 0.5; /* FIXME: default value? */
1520 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1525 if (sscanf (p_string, "%lf", &logNModifier) != 1)
1526 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1528 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1529 p_string, "LOGNMODIFIER", "TESTING");
1530 GNUNET_free (p_string);
1532 probability = 0.5; /* FIXME: default percentage? */
1533 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1538 if (sscanf (p_string, "%lf", &probability) != 1)
1539 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1541 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1542 p_string, "PERCENTAGE", "TESTING");
1543 GNUNET_free (p_string);
1545 natLog = log (pg->total);
1546 connsPerPeer = ceil (natLog * logNModifier);
1548 if (connsPerPeer % 2 == 1)
1551 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."), connsPerPeer);
1553 smallWorldConnections = 0;
1554 connect_attempts = 0;
1555 for (i = 0; i < pg->total; i++)
1558 max = i + connsPerPeer / 2;
1559 min = i - connsPerPeer / 2;
1561 if (max > pg->total - 1)
1563 max = max - pg->total;
1569 min = pg->total - 1 + min;
1573 for (j = 0; j < connsPerPeer / 2; j++)
1577 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1578 UINT64_MAX) / ((double) UINT64_MAX));
1579 if (random < probability)
1581 /* Connect to uniformly selected random peer */
1583 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1585 while ((((randomPeer < max) && (randomPeer > min))
1586 && (useAnd == 0)) || (((randomPeer > min)
1587 || (randomPeer < max))
1591 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1594 smallWorldConnections += proc (pg, i, randomPeer);
1598 nodeToConnect = i + j + 1;
1599 if (nodeToConnect > pg->total - 1)
1601 nodeToConnect = nodeToConnect - pg->total;
1603 connect_attempts += proc (pg, i, nodeToConnect);
1609 connect_attempts += smallWorldConnections;
1611 return connect_attempts;
1615 * Create a topology given a peer group (set of running peers)
1616 * and a connection processor.
1618 * @param pg the peergroup to create the topology on
1619 * @param proc the connection processor to call to actually set
1620 * up connections between two peers
1622 * @return the number of connections that were set up
1626 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg,
1627 GNUNET_TESTING_ConnectionProcessor proc)
1629 unsigned int outer_count, inner_count;
1630 unsigned int cutoff;
1631 int connect_attempts;
1632 double nat_percentage;
1635 nat_percentage = 0.6; /* FIXME: default percentage? */
1636 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1641 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1642 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1644 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1645 p_string, "PERCENTAGE", "TESTING");
1646 GNUNET_free (p_string);
1651 cutoff = (unsigned int) (nat_percentage * pg->total);
1653 connect_attempts = 0;
1655 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1657 for (inner_count = outer_count + 1; inner_count < pg->total;
1660 if ((outer_count > cutoff) || (inner_count > cutoff))
1663 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1664 "Connecting peer %d to peer %d\n",
1665 outer_count, inner_count);
1667 connect_attempts += proc (pg, outer_count, inner_count);
1672 return connect_attempts;
1677 * Create a topology given a peer group (set of running peers)
1678 * and a connection processor.
1680 * @param pg the peergroup to create the topology on
1681 * @param proc the connection processor to call to actually set
1682 * up connections between two peers
1684 * @return the number of connections that were set up
1688 create_small_world (struct GNUNET_TESTING_PeerGroup *pg,
1689 GNUNET_TESTING_ConnectionProcessor proc)
1691 unsigned int i, j, k;
1692 unsigned int square;
1695 unsigned int toggle = 1;
1696 unsigned int nodeToConnect;
1697 unsigned int natLog;
1698 unsigned int node1Row;
1699 unsigned int node1Col;
1700 unsigned int node2Row;
1701 unsigned int node2Col;
1702 unsigned int distance;
1703 double probability, random, percentage;
1704 unsigned int smallWorldConnections;
1705 unsigned int small_world_it;
1707 int connect_attempts;
1708 square = floor (sqrt (pg->total));
1712 percentage = 0.5; /* FIXME: default percentage? */
1713 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1718 if (sscanf (p_string, "%lf", &percentage) != 1)
1719 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1721 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1722 p_string, "PERCENTAGE", "TESTING");
1723 GNUNET_free (p_string);
1725 if (percentage < 0.0)
1727 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1729 ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"),
1730 "PERCENTAGE", "TESTING", percentage);
1733 probability = 0.5; /* FIXME: default percentage? */
1734 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1739 if (sscanf (p_string, "%lf", &probability) != 1)
1740 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1742 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1743 p_string, "PROBABILITY", "TESTING");
1744 GNUNET_free (p_string);
1746 if (square * square != pg->total)
1748 while (rows * cols < pg->total)
1750 if (toggle % 2 == 0)
1759 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1761 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1765 connect_attempts = 0;
1766 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1767 * to the node to its right and above. Once this is over, we'll have our torus!
1768 * Special case for the last node (if the rows and columns are not equal), connect
1769 * to the first in the row to maintain topology.
1771 for (i = 0; i < pg->total; i++)
1773 /* First connect to the node to the right */
1774 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1775 nodeToConnect = i + 1;
1776 else if (i + 1 == pg->total)
1777 nodeToConnect = rows * cols - cols;
1779 nodeToConnect = i - cols + 1;
1781 connect_attempts += proc (pg, i, nodeToConnect);
1784 nodeToConnect = (rows * cols) - cols + i;
1786 nodeToConnect = i - cols;
1788 if (nodeToConnect < pg->total)
1789 connect_attempts += proc (pg, i, nodeToConnect);
1791 natLog = log (pg->total);
1792 #if VERBOSE_TESTING > 2
1793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1794 _("natural log of %d is %d, will run %d iterations\n"),
1795 pg->total, natLog, (int) (natLog * percentage));
1796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1797 _("Total connections added thus far: %u!\n"), connect_attempts);
1799 smallWorldConnections = 0;
1800 small_world_it = (unsigned int) (natLog * percentage);
1801 if (small_world_it < 1)
1803 GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1);
1804 for (i = 0; i < small_world_it; i++)
1806 for (j = 0; j < pg->total; j++)
1808 /* Determine the row and column of node at position j on the 2d torus */
1809 node1Row = j / cols;
1810 node1Col = j - (node1Row * cols);
1811 for (k = 0; k < pg->total; k++)
1813 /* Determine the row and column of node at position k on the 2d torus */
1814 node2Row = k / cols;
1815 node2Col = k - (node2Row * cols);
1816 /* Simple Cartesian distance */
1818 abs (node1Row - node2Row) + abs (node1Col - node2Col);
1821 /* Calculate probability as 1 over the square of the distance */
1822 probability = 1.0 / (distance * distance);
1823 /* Choose a random value between 0 and 1 */
1826 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1828 ((double) UINT64_MAX);
1829 /* If random < probability, then connect the two nodes */
1830 if (random < probability)
1831 smallWorldConnections += proc (pg, j, k);
1837 connect_attempts += smallWorldConnections;
1838 #if VERBOSE_TESTING > 2
1839 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1840 _("Total connections added for small world: %d!\n"),
1841 smallWorldConnections);
1843 return connect_attempts;
1847 * Create a topology given a peer group (set of running peers)
1848 * and a connection processor.
1850 * @param pg the peergroup to create the topology on
1851 * @param proc the connection processor to call to actually set
1852 * up connections between two peers
1854 * @return the number of connections that were set up
1858 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg,
1859 GNUNET_TESTING_ConnectionProcessor proc)
1862 unsigned int outer_count;
1863 unsigned int inner_count;
1864 int connect_attempts;
1868 probability = 0.5; /* FIXME: default percentage? */
1869 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1874 if (sscanf (p_string, "%lf", &probability) != 1)
1875 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1877 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1878 p_string, "PROBABILITY", "TESTING");
1879 GNUNET_free (p_string);
1881 connect_attempts = 0;
1882 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1884 for (inner_count = outer_count + 1; inner_count < pg->total;
1889 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1890 UINT64_MAX)) / ((double) UINT64_MAX);
1892 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1893 _("rand is %f probability is %f\n"), temp_rand,
1896 if (temp_rand < probability)
1898 connect_attempts += proc (pg, outer_count, inner_count);
1903 return connect_attempts;
1907 * Create a topology given a peer group (set of running peers)
1908 * and a connection processor. This particular function creates
1909 * the connections for a 2d-torus, plus additional "closest"
1910 * connections per peer.
1912 * @param pg the peergroup to create the topology on
1913 * @param proc the connection processor to call to actually set
1914 * up connections between two peers
1916 * @return the number of connections that were set up
1920 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg,
1921 GNUNET_TESTING_ConnectionProcessor proc)
1924 unsigned int square;
1927 unsigned int toggle = 1;
1928 unsigned int nodeToConnect;
1929 int connect_attempts;
1931 connect_attempts = 0;
1933 square = floor (sqrt (pg->total));
1937 if (square * square != pg->total)
1939 while (rows * cols < pg->total)
1941 if (toggle % 2 == 0)
1950 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1952 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1955 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1956 * to the node to its right and above. Once this is over, we'll have our torus!
1957 * Special case for the last node (if the rows and columns are not equal), connect
1958 * to the first in the row to maintain topology.
1960 for (i = 0; i < pg->total; i++)
1962 /* First connect to the node to the right */
1963 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1964 nodeToConnect = i + 1;
1965 else if (i + 1 == pg->total)
1966 nodeToConnect = rows * cols - cols;
1968 nodeToConnect = i - cols + 1;
1970 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1971 "Connecting peer %d to peer %d\n", i, nodeToConnect);
1973 connect_attempts += proc (pg, i, nodeToConnect);
1975 /* Second connect to the node immediately above */
1977 nodeToConnect = (rows * cols) - cols + i;
1979 nodeToConnect = i - cols;
1981 if (nodeToConnect < pg->total)
1984 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1985 "Connecting peer %d to peer %d\n", i, nodeToConnect);
1987 connect_attempts += proc (pg, i, nodeToConnect);
1992 return connect_attempts;
1997 * Create a topology given a peer group (set of running peers)
1998 * and a connection processor.
2000 * @param pg the peergroup to create the topology on
2001 * @param proc the connection processor to call to actually set
2002 * up connections between two peers
2004 * @return the number of connections that were set up
2008 create_clique (struct GNUNET_TESTING_PeerGroup *pg,
2009 GNUNET_TESTING_ConnectionProcessor proc)
2011 unsigned int outer_count;
2012 unsigned int inner_count;
2013 int connect_attempts;
2015 connect_attempts = 0;
2017 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2019 for (inner_count = outer_count + 1; inner_count < pg->total;
2023 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2024 "Connecting peer %d to peer %d\n",
2025 outer_count, inner_count);
2027 connect_attempts += proc (pg, outer_count, inner_count);
2031 return connect_attempts;
2035 * Iterator over hash map entries.
2037 * @param cls closure the peer group
2038 * @param key the key stored in the hashmap is the
2039 * index of the peer to connect to
2040 * @param value value in the hash map, handle to the peer daemon
2041 * @return GNUNET_YES if we should continue to
2046 unblacklist_iterator (void *cls,
2047 const GNUNET_HashCode * key,
2050 struct UnblacklistContext *un_ctx = cls;
2051 uint32_t second_pos;
2053 uid_from_hash (key, &second_pos);
2055 unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
2061 * Create a blacklist topology based on the allowed topology
2062 * which disallows any connections not in the allowed topology
2063 * at the transport level.
2065 * @param pg the peergroup to create the topology on
2066 * @param proc the connection processor to call to allow
2067 * up connections between two peers
2069 * @return the number of connections that were set up
2073 copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
2074 GNUNET_TESTING_ConnectionProcessor proc)
2076 struct UnblacklistContext un_ctx;
2082 for (count = 0; count < pg->total - 1; count++)
2084 un_ctx.first_uid = count;
2085 total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, &unblacklist_iterator, &un_ctx);
2087 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total);
2092 * Create a topology given a peer group (set of running peers)
2093 * and a connection processor.
2095 * @param pg the peergroup to create the topology on
2096 * @param proc the connection processor to call to actually set
2097 * up connections between two peers
2099 * @return the number of connections that were set up
2103 create_line (struct GNUNET_TESTING_PeerGroup *pg,
2104 GNUNET_TESTING_ConnectionProcessor proc)
2107 int connect_attempts;
2109 connect_attempts = 0;
2111 /* Connect each peer to the next highest numbered peer */
2112 for (count = 0; count < pg->total - 1; count++)
2115 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2116 "Connecting peer %d to peer %d\n", count, count + 1);
2118 connect_attempts += proc (pg, count, count + 1);
2121 return connect_attempts;
2125 * Create a topology given a peer group (set of running peers)
2126 * and a connection processor.
2128 * @param pg the peergroup to create the topology on
2129 * @param filename the file to read topology information from
2130 * @param proc the connection processor to call to actually set
2131 * up connections between two peers
2133 * @return the number of connections that were set up
2137 create_from_file (struct GNUNET_TESTING_PeerGroup *pg,
2139 GNUNET_TESTING_ConnectionProcessor proc)
2141 int connect_attempts;
2142 unsigned int first_peer_index;
2143 unsigned int second_peer_index;
2144 connect_attempts = 0;
2149 unsigned int total_peers;
2151 enum States curr_state;
2153 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
2154 GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ
2155 | GNUNET_DISK_PERM_USER_WRITE);
2157 if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
2159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2160 "Could not open file specified for topology!");
2161 return connect_attempts;
2164 data = GNUNET_malloc_large (frstat.st_size);
2165 GNUNET_assert(data != NULL);
2166 if (frstat.st_size !=
2167 GNUNET_DISK_fn_read (filename, data, frstat.st_size))
2169 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2170 "Could not read file %s specified for host list, ending test!", filename);
2172 return connect_attempts;
2177 /* First line should contain a single integer, specifying the number of peers */
2178 /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
2179 curr_state = NUM_PEERS;
2180 while (count < frstat.st_size - 1)
2182 if ((buf[count] == '\n') || (buf[count] == ' '))
2191 if (1 != sscanf(&buf[count], "%u", &total_peers))
2193 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read number of peers from topology file!\n");
2194 GNUNET_free_non_null(data);
2195 return connect_attempts;
2197 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read %u total peers in topology\n", total_peers);
2198 GNUNET_assert(total_peers == pg->total);
2199 curr_state = PEER_INDEX;
2200 while((buf[count] != '\n') && (count < frstat.st_size - 1))
2205 if (1 != sscanf(&buf[count], "%u", &first_peer_index))
2207 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read peer index from topology file!\n");
2208 GNUNET_free_non_null(data);
2209 return connect_attempts;
2211 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read next peer index %u\n", first_peer_index);
2212 while((buf[count] != ':') && (count < frstat.st_size - 1))
2215 curr_state = OTHER_PEER_INDEX;
2218 if (1 == sscanf(&buf[count], ":"))
2219 curr_state = OTHER_PEER_INDEX;
2222 case OTHER_PEER_INDEX:
2223 if (1 != sscanf(&buf[count], "%u", &second_peer_index))
2225 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to peer index from topology file!\n");
2226 GNUNET_free_non_null(data);
2227 return connect_attempts;
2229 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read second peer index %u\n", second_peer_index);
2230 /* Assume file is written with first peer 1, but array index is 0 */
2231 connect_attempts += proc (pg, first_peer_index - 1, second_peer_index - 1);
2232 while((buf[count] != '\n') && (buf[count] != ',') && (count < frstat.st_size - 1))
2234 if (buf[count] == '\n')
2236 curr_state = PEER_INDEX;
2238 else if (buf[count] != ',')
2240 curr_state = OTHER_PEER_INDEX;
2245 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Found bad data in topology file while in state %d!\n", curr_state);
2247 return connect_attempts;
2252 /* Connect each peer to the next highest numbered peer */
2253 for (count = 0; count < pg->total - 1; count++)
2256 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2257 "Connecting peer %d to peer %d\n", first_peer_index, second_peer_index);
2259 connect_attempts += proc (pg, first_peer_index, second_peer_index);
2262 return connect_attempts;
2266 * Create a topology given a peer group (set of running peers)
2267 * and a connection processor.
2269 * @param pg the peergroup to create the topology on
2270 * @param proc the connection processor to call to actually set
2271 * up connections between two peers
2273 * @return the number of connections that were set up
2277 create_ring (struct GNUNET_TESTING_PeerGroup *pg,
2278 GNUNET_TESTING_ConnectionProcessor proc)
2281 int connect_attempts;
2283 connect_attempts = 0;
2285 /* Connect each peer to the next highest numbered peer */
2286 for (count = 0; count < pg->total - 1; count++)
2289 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2290 "Connecting peer %d to peer %d\n", count, count + 1);
2292 connect_attempts += proc (pg, count, count + 1);
2295 /* Connect the last peer to the first peer */
2296 connect_attempts += proc (pg, pg->total - 1, 0);
2298 return connect_attempts;
2303 * Iterator for writing friends of a peer to a file.
2305 * @param cls closure, an open writable file handle
2306 * @param key the key the daemon was stored under
2307 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2309 * @return GNUNET_YES to continue iteration
2311 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
2312 * with a single file_iterator that takes a closure which contains
2313 * the prefix to write before the peer. Then this could be used
2314 * for blacklisting multiple transports and writing the friend
2315 * file. I'm sure *someone* will complain loudly about other
2316 * things that negate these functions even existing so no point in
2320 friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2322 FILE *temp_friend_handle = cls;
2323 struct GNUNET_TESTING_Daemon *peer = value;
2324 struct GNUNET_PeerIdentity *temppeer;
2325 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2327 temppeer = &peer->id;
2328 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2329 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2334 struct BlacklistContext
2337 * The (open) file handle to write to
2339 FILE *temp_file_handle;
2342 * The transport that this peer will be blacklisted on.
2348 * Iterator for writing blacklist data to appropriate files.
2350 * @param cls closure, an open writable file handle
2351 * @param key the key the daemon was stored under
2352 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2354 * @return GNUNET_YES to continue iteration
2357 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2359 struct BlacklistContext *blacklist_ctx = cls;
2360 struct GNUNET_TESTING_Daemon *peer = value;
2361 struct GNUNET_PeerIdentity *temppeer;
2362 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2364 temppeer = &peer->id;
2365 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2366 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
2367 fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
2368 blacklist_ctx->transport, (char *) &peer_enc);
2374 * Create the friend files based on the PeerConnection's
2375 * of each peer in the peer group, and copy the files
2376 * to the appropriate place
2378 * @param pg the peer group we are dealing with
2381 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
2383 FILE *temp_friend_handle;
2384 unsigned int pg_iter;
2385 char *temp_service_path;
2386 struct GNUNET_OS_Process **procarr;
2389 enum GNUNET_OS_ProcessStatusType type;
2390 unsigned long return_code;
2395 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2396 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2398 mytemp = GNUNET_DISK_mktemp ("friends");
2399 GNUNET_assert (mytemp != NULL);
2400 temp_friend_handle = fopen (mytemp, "wt");
2401 GNUNET_assert (temp_friend_handle != NULL);
2402 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers,
2403 &friend_file_iterator,
2404 temp_friend_handle);
2405 fclose (temp_friend_handle);
2408 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2409 daemon->cfg, "PATHS",
2411 &temp_service_path))
2413 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2415 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2416 "SERVICEHOME", "PATHS");
2417 if (UNLINK (mytemp) != 0)
2418 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2420 GNUNET_free (mytemp);
2424 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2426 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
2427 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2431 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2432 _("Copying file with command cp %s %s\n"), mytemp, arg);
2437 else /* Remote, scp the file to the correct place */
2439 if (NULL != pg->peers[pg_iter].daemon->username)
2440 GNUNET_asprintf (&arg, "%s@%s:%s/friends",
2441 pg->peers[pg_iter].daemon->username,
2442 pg->peers[pg_iter].daemon->hostname,
2445 GNUNET_asprintf (&arg, "%s:%s/friends",
2446 pg->peers[pg_iter].daemon->hostname,
2449 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2454 _("Copying file with command scp %s %s\n"), mytemp,
2459 GNUNET_free (temp_service_path);
2460 GNUNET_free (mytemp);
2464 ret = GNUNET_SYSERR;
2465 while ((count < max_wait) && (ret != GNUNET_OK))
2468 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2471 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2472 _("Checking copy status of file %d\n"), pg_iter);
2474 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2476 if (GNUNET_OS_process_status
2477 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2479 ret = GNUNET_SYSERR;
2481 else if ((type != GNUNET_OS_PROCESS_EXITED)
2482 || (return_code != 0))
2484 ret = GNUNET_SYSERR;
2488 GNUNET_OS_process_close (procarr[pg_iter]);
2489 procarr[pg_iter] = NULL;
2491 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2492 _("File %d copied\n"), pg_iter);
2498 if (ret == GNUNET_SYSERR)
2500 /* FIXME: why sleep here? -CG */
2506 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2507 _("Finished copying all friend files!\n"));
2509 GNUNET_free (procarr);
2515 * Create the blacklist files based on the PeerConnection's
2516 * of each peer in the peer group, and copy the files
2517 * to the appropriate place.
2519 * @param pg the peer group we are dealing with
2520 * @param transports space delimited list of transports to blacklist
2523 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
2524 const char *transports)
2526 FILE *temp_file_handle;
2527 static struct BlacklistContext blacklist_ctx;
2528 unsigned int pg_iter;
2529 char *temp_service_path;
2530 struct GNUNET_OS_Process **procarr;
2533 enum GNUNET_OS_ProcessStatusType type;
2534 unsigned long return_code;
2541 char *temp_transports;
2544 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2545 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2547 mytemp = GNUNET_DISK_mktemp ("blacklist");
2548 GNUNET_assert (mytemp != NULL);
2549 temp_file_handle = fopen (mytemp, "wt");
2550 GNUNET_assert (temp_file_handle != NULL);
2551 temp_transports = GNUNET_strdup (transports);
2552 blacklist_ctx.temp_file_handle = temp_file_handle;
2553 transport_len = strlen (temp_transports) + 1;
2556 for (i = 0; i < transport_len; i++)
2558 if ((temp_transports[i] == ' ') && (pos == NULL))
2559 continue; /* At start of string (whitespace) */
2560 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
2562 temp_transports[i] = '\0';
2563 blacklist_ctx.transport = pos;
2564 entry_count = GNUNET_CONTAINER_multihashmap_iterate (pg->
2566 [pg_iter].blacklisted_peers,
2567 &blacklist_file_iterator,
2570 } /* At beginning of actual string */
2571 else if (pos == NULL)
2573 pos = &temp_transports[i];
2577 GNUNET_free (temp_transports);
2578 fclose (temp_file_handle);
2581 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2582 daemon->cfg, "PATHS",
2584 &temp_service_path))
2586 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2588 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2589 "SERVICEHOME", "PATHS");
2590 if (UNLINK (mytemp) != 0)
2591 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2593 GNUNET_free (mytemp);
2597 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2599 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
2600 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2604 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2605 _("Copying file with command cp %s %s\n"), mytemp, arg);
2610 else /* Remote, scp the file to the correct place */
2612 if (NULL != pg->peers[pg_iter].daemon->username)
2613 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist",
2614 pg->peers[pg_iter].daemon->username,
2615 pg->peers[pg_iter].daemon->hostname,
2618 GNUNET_asprintf (&arg, "%s:%s/blacklist",
2619 pg->peers[pg_iter].daemon->hostname,
2622 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2627 _("Copying file with command scp %s %s\n"), mytemp,
2632 GNUNET_free (temp_service_path);
2633 GNUNET_free (mytemp);
2637 ret = GNUNET_SYSERR;
2638 while ((count < max_wait) && (ret != GNUNET_OK))
2641 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2644 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2645 _("Checking copy status of file %d\n"), pg_iter);
2647 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2649 if (GNUNET_OS_process_status
2650 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2652 ret = GNUNET_SYSERR;
2654 else if ((type != GNUNET_OS_PROCESS_EXITED)
2655 || (return_code != 0))
2657 ret = GNUNET_SYSERR;
2661 GNUNET_OS_process_close (procarr[pg_iter]);
2662 procarr[pg_iter] = NULL;
2664 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2665 _("File %d copied\n"), pg_iter);
2671 if (ret == GNUNET_SYSERR)
2673 /* FIXME: why sleep here? -CG */
2679 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2680 _("Finished copying all blacklist files!\n"));
2682 GNUNET_free (procarr);
2688 * Internal notification of a connection, kept so that we can ensure some connections
2689 * happen instead of flooding all testing daemons with requests to connect.
2692 internal_connect_notify (void *cls,
2693 const struct GNUNET_PeerIdentity *first,
2694 const struct GNUNET_PeerIdentity *second,
2696 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
2697 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
2698 struct GNUNET_TESTING_Daemon *first_daemon,
2699 struct GNUNET_TESTING_Daemon *second_daemon,
2702 struct ConnectTopologyContext *ct_ctx = cls;
2703 struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
2704 outstanding_connects--;
2705 ct_ctx->remaining_connections--;
2706 if (ct_ctx->remaining_connections == 0)
2708 if (ct_ctx->notify_connections_done != NULL)
2709 ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL);
2710 GNUNET_free (ct_ctx);
2713 if (pg->notify_connection != NULL)
2714 pg->notify_connection (pg->notify_connection_cls, first, second, distance,
2715 first_cfg, second_cfg, first_daemon, second_daemon,
2721 * Either delay a connection (because there are too many outstanding)
2722 * or schedule it for right now.
2724 * @param cls a connection context
2725 * @param tc the task runtime context
2728 schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2730 struct ConnectContext *connect_context = cls;
2732 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
2735 if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
2737 #if VERBOSE_TESTING > 2
2738 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2740 ("Delaying connect, we have too many outstanding connections!\n"));
2742 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
2743 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
2744 &schedule_connect, connect_context);
2748 #if VERBOSE_TESTING > 2
2749 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2750 _("Creating connection, outstanding_connections is %d\n"),
2751 outstanding_connects);
2753 outstanding_connects++;
2754 GNUNET_TESTING_daemons_connect (connect_context->first,
2755 connect_context->second,
2758 &internal_connect_notify,
2759 connect_context->ct_ctx);
2760 GNUNET_free (connect_context);
2766 * Iterator for actually scheduling connections to be created
2767 * between two peers.
2769 * @param cls closure, a GNUNET_TESTING_Daemon
2770 * @param key the key the second Daemon was stored under
2771 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2773 * @return GNUNET_YES to continue iteration
2776 connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2778 struct ConnectTopologyContext *ct_ctx = cls;
2779 struct PeerData *first = ct_ctx->first;
2780 struct GNUNET_TESTING_Daemon *second = value;
2781 struct ConnectContext *connect_context;
2783 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
2784 connect_context->first = first->daemon;
2785 connect_context->second = second;
2786 connect_context->ct_ctx = ct_ctx;
2787 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
2794 * Iterator for copying all entries in the allowed hashmap to the
2797 * @param cls closure, a GNUNET_TESTING_Daemon
2798 * @param key the key the second Daemon was stored under
2799 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2801 * @return GNUNET_YES to continue iteration
2804 copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2806 struct PeerData *first = cls;
2808 GNUNET_assert (GNUNET_OK ==
2809 GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key,
2811 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2817 * Make the peers to connect the same as those that are allowed to be
2820 * @param pg the peer group
2823 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
2825 unsigned int pg_iter;
2830 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2833 GNUNET_CONTAINER_multihashmap_iterate (pg->
2834 peers[pg_iter].allowed_peers,
2835 ©_topology_iterator,
2836 &pg->peers[pg_iter]);
2837 if (GNUNET_SYSERR == ret)
2838 return GNUNET_SYSERR;
2840 total = total + ret;
2848 * Connect the topology as specified by the PeerConnection's
2849 * of each peer in the peer group
2851 * @param pg the peer group we are dealing with
2852 * @param notify_callback callback to notify when finished
2853 * @param notify_cls closure for notify callback
2855 * @return the number of connections that will be attempted
2858 connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
2859 GNUNET_TESTING_NotifyCompletion notify_callback,
2862 unsigned int pg_iter;
2865 struct ConnectTopologyContext *ct_ctx;
2867 struct PeerConnection *connection_iter;
2868 struct ConnectContext *connect_context;
2872 ct_ctx = GNUNET_malloc (sizeof (struct ConnectTopologyContext));
2873 ct_ctx->notify_connections_done = notify_callback;
2874 ct_ctx->notify_cls = notify_cls;
2877 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2880 GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
2885 GNUNET_free (ct_ctx);
2888 ct_ctx->remaining_connections = total;
2891 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2893 ct_ctx->first = &pg->peers[pg_iter];
2895 GNUNET_CONTAINER_multihashmap_iterate (pg->
2896 peers[pg_iter].connect_peers,
2897 &connect_iterator, ct_ctx);
2898 GNUNET_assert (GNUNET_SYSERR != ret && ret >= 0);
2899 total = total + ret;
2902 connection_iter = FIXME;
2903 while (connection_iter != NULL)
2905 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
2906 connect_context->pg = pg;
2907 connect_context->first = FIXME;
2908 connect_context->second = connection_iter->daemon;
2909 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
2910 connection_iter = connection_iter->next;
2919 * Takes a peer group and creates a topology based on the
2920 * one specified. Creates a topology means generates friend
2921 * files for the peers so they can only connect to those allowed
2922 * by the topology. This will only have an effect once peers
2923 * are started if the FRIENDS_ONLY option is set in the base
2924 * config. Also takes an optional restrict topology which
2925 * disallows connections based on particular transports
2926 * UNLESS they are specified in the restricted topology.
2928 * @param pg the peer group struct representing the running peers
2929 * @param topology which topology to connect the peers in
2930 * @param restrict_topology disallow restrict_transports transport
2931 * connections to peers NOT in this topology
2932 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
2933 * @param restrict_transports space delimited list of transports to blacklist
2934 * to create restricted topology
2936 * @return the maximum number of connections were all allowed peers
2937 * connected to each other
2940 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
2941 enum GNUNET_TESTING_Topology topology,
2942 enum GNUNET_TESTING_Topology
2944 const char *restrict_transports)
2947 unsigned int num_connections;
2948 int unblacklisted_connections;
2953 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2955 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n"));
2957 num_connections = create_clique (pg, &add_allowed_connections);
2959 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2962 _("Creating small world (ring) topology\n"));
2965 create_small_world_ring (pg, &add_allowed_connections);
2967 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2969 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2970 _("Creating small world (2d-torus) topology\n"));
2972 num_connections = create_small_world (pg, &add_allowed_connections);
2974 case GNUNET_TESTING_TOPOLOGY_RING:
2976 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n"));
2978 num_connections = create_ring (pg, &add_allowed_connections);
2980 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2982 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n"));
2984 num_connections = create_2d_torus (pg, &add_allowed_connections);
2986 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2988 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2989 _("Creating Erdos-Renyi topology\n"));
2991 num_connections = create_erdos_renyi (pg, &add_allowed_connections);
2993 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2995 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n"));
2997 num_connections = create_nated_internet (pg, &add_allowed_connections);
2999 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3001 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3002 _("Creating Scale Free topology\n"));
3004 num_connections = create_scale_free (pg, &add_allowed_connections);
3006 case GNUNET_TESTING_TOPOLOGY_LINE:
3008 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3009 _("Creating straight line topology\n"));
3011 num_connections = create_line (pg, &add_allowed_connections);
3013 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3015 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3016 _("Creating topology from file!\n"));
3019 GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing", "topology_file",
3021 num_connections = create_from_file (pg, filename, &add_allowed_connections);
3024 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!");
3025 num_connections = 0;
3028 case GNUNET_TESTING_TOPOLOGY_NONE:
3030 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3032 ("Creating no allowed topology (all peers can connect at core level)\n"));
3034 num_connections = 0;
3037 num_connections = 0;
3042 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
3044 ret = create_and_copy_friend_files (pg);
3045 if (ret != GNUNET_OK)
3048 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3049 _("Failed during friend file copying!\n"));
3051 return GNUNET_SYSERR;
3056 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3057 _("Friend files created/copied successfully!\n"));
3062 /* Use the create clique method to initially set all connections as blacklisted. */
3063 create_clique (pg, &blacklist_connections);
3065 unblacklisted_connections = 0;
3066 /* Un-blacklist connections as per the topology specified */
3067 switch (restrict_topology)
3069 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3071 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3072 _("Blacklisting all but clique topology\n"));
3074 unblacklisted_connections =
3075 create_clique (pg, &unblacklist_connections);
3077 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3079 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3080 _("Blacklisting all but small world (ring) topology\n"));
3082 unblacklisted_connections =
3083 create_small_world_ring (pg, &unblacklist_connections);
3085 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3089 ("Blacklisting all but small world (2d-torus) topology\n"));
3091 unblacklisted_connections =
3092 create_small_world (pg, &unblacklist_connections);
3094 case GNUNET_TESTING_TOPOLOGY_RING:
3096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3097 _("Blacklisting all but ring topology\n"));
3099 unblacklisted_connections = create_ring (pg, &unblacklist_connections);
3101 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3103 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3104 _("Blacklisting all but 2d torus topology\n"));
3106 unblacklisted_connections =
3107 create_2d_torus (pg, &unblacklist_connections);
3109 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3111 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3112 _("Blacklisting all but Erdos-Renyi topology\n"));
3114 unblacklisted_connections =
3115 create_erdos_renyi (pg, &unblacklist_connections);
3117 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3120 _("Blacklisting all but InterNAT topology\n"));
3122 unblacklisted_connections =
3123 create_nated_internet (pg, &unblacklist_connections);
3125 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3128 _("Blacklisting all but Scale Free topology\n"));
3130 unblacklisted_connections =
3131 create_scale_free (pg, &unblacklist_connections);
3133 case GNUNET_TESTING_TOPOLOGY_LINE:
3135 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3136 _("Blacklisting all but straight line topology\n"));
3138 unblacklisted_connections = create_line (pg, &unblacklist_connections);
3140 case GNUNET_TESTING_TOPOLOGY_NONE:
3141 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3143 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3145 ("Creating no blacklist topology (all peers can connect at transport level)\n"));
3147 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3149 ("Creating blacklist topology from allowed\n"));
3150 unblacklisted_connections = copy_allowed (pg, &unblacklist_connections);
3155 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
3157 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'", restrict_transports);
3158 ret = create_and_copy_blacklist_files (pg, restrict_transports);
3159 if (ret != GNUNET_OK)
3162 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3163 _("Failed during blacklist file copying!\n"));
3170 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3171 _("Blacklist files created/copied successfully!\n"));
3175 return num_connections;
3180 * Iterator for choosing random peers to connect.
3182 * @param cls closure, a RandomContext
3183 * @param key the key the second Daemon was stored under
3184 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3186 * @return GNUNET_YES to continue iteration
3189 random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3191 struct RandomContext *random_ctx = cls;
3192 double random_number;
3193 uint32_t second_pos;
3194 GNUNET_HashCode first_hash;
3197 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
3198 UINT64_MAX)) / ((double) UINT64_MAX);
3199 if (random_number < random_ctx->percentage)
3201 GNUNET_assert (GNUNET_OK ==
3202 GNUNET_CONTAINER_multihashmap_put (random_ctx->
3203 first->connect_peers_working_set,
3205 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3207 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
3208 uid_from_hash (key, &second_pos);
3209 hash_from_uid (random_ctx->first_uid, &first_hash);
3210 GNUNET_assert (random_ctx->pg->total > second_pos);
3211 GNUNET_assert (GNUNET_YES ==
3212 GNUNET_CONTAINER_multihashmap_remove (random_ctx->
3214 [second_pos].connect_peers,
3223 * Iterator for adding at least X peers to a peers connection set.
3225 * @param cls closure, MinimumContext
3226 * @param key the key the second Daemon was stored under
3227 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3229 * @return GNUNET_YES to continue iteration
3232 minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3234 struct MinimumContext *min_ctx = cls;
3235 uint32_t second_pos;
3236 GNUNET_HashCode first_hash;
3239 if (GNUNET_CONTAINER_multihashmap_size
3240 (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
3242 for (i = 0; i < min_ctx->num_to_add; i++)
3244 if (min_ctx->pg_array[i] == min_ctx->current)
3246 GNUNET_assert (GNUNET_OK ==
3247 GNUNET_CONTAINER_multihashmap_put
3248 (min_ctx->first->connect_peers_working_set, key,
3250 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3251 uid_from_hash (key, &second_pos);
3252 hash_from_uid (min_ctx->first_uid, &first_hash);
3253 GNUNET_assert (min_ctx->pg->total > second_pos);
3254 GNUNET_assert (GNUNET_OK ==
3255 GNUNET_CONTAINER_multihashmap_put (min_ctx->
3257 [second_pos].connect_peers_working_set,
3261 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3262 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
3263 GNUNET_assert (GNUNET_YES ==
3264 GNUNET_CONTAINER_multihashmap_remove
3265 (min_ctx->pg->peers[second_pos].connect_peers,
3266 &first_hash, min_ctx->first->daemon));
3273 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
3279 * Iterator for adding peers to a connection set based on a depth first search.
3281 * @param cls closure, MinimumContext
3282 * @param key the key the second daemon was stored under
3283 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3285 * @return GNUNET_YES to continue iteration
3288 dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3290 struct DFSContext *dfs_ctx = cls;
3291 GNUNET_HashCode first_hash;
3293 if (dfs_ctx->current == dfs_ctx->chosen)
3295 GNUNET_assert (GNUNET_OK ==
3296 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
3297 first->connect_peers_working_set,
3299 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3300 uid_from_hash (key, &dfs_ctx->second_uid);
3301 hash_from_uid (dfs_ctx->first_uid, &first_hash);
3302 GNUNET_assert (GNUNET_OK ==
3303 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
3305 [dfs_ctx->second_uid].connect_peers_working_set,
3309 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3310 GNUNET_assert (GNUNET_YES ==
3311 GNUNET_CONTAINER_multihashmap_remove (dfs_ctx->
3313 [dfs_ctx->second_uid].connect_peers,
3317 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
3318 return GNUNET_NO; /* We have found our peer, don't iterate more */
3327 * From the set of connections possible, choose percentage percent of connections
3328 * to actually connect.
3330 * @param pg the peergroup we are dealing with
3331 * @param percentage what percent of total connections to make
3334 choose_random_connections (struct GNUNET_TESTING_PeerGroup *pg,
3337 struct RandomContext random_ctx;
3340 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3342 random_ctx.first_uid = pg_iter;
3343 random_ctx.first = &pg->peers[pg_iter];
3344 random_ctx.percentage = percentage;
3346 pg->peers[pg_iter].connect_peers_working_set =
3347 GNUNET_CONTAINER_multihashmap_create (pg->total);
3348 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
3349 &random_connect_iterator,
3351 /* Now remove the old connections */
3352 GNUNET_CONTAINER_multihashmap_destroy (pg->
3353 peers[pg_iter].connect_peers);
3354 /* And replace with the random set */
3355 pg->peers[pg_iter].connect_peers =
3356 pg->peers[pg_iter].connect_peers_working_set;
3361 * From the set of connections possible, choose at least num connections per
3364 * @param pg the peergroup we are dealing with
3365 * @param num how many connections at least should each peer have (if possible)?
3368 choose_minimum (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
3370 struct MinimumContext minimum_ctx;
3373 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3375 pg->peers[pg_iter].connect_peers_working_set =
3376 GNUNET_CONTAINER_multihashmap_create (num);
3379 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3381 minimum_ctx.first_uid = pg_iter;
3382 minimum_ctx.pg_array =
3383 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
3384 GNUNET_CONTAINER_multihashmap_size
3385 (pg->peers[pg_iter].connect_peers));
3386 minimum_ctx.first = &pg->peers[pg_iter];
3387 minimum_ctx.pg = pg;
3388 minimum_ctx.num_to_add = num;
3389 minimum_ctx.current = 0;
3390 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
3391 &minimum_connect_iterator,
3395 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3397 /* Remove the "old" connections */
3398 GNUNET_CONTAINER_multihashmap_destroy (pg->
3399 peers[pg_iter].connect_peers);
3400 /* And replace with the working set */
3401 pg->peers[pg_iter].connect_peers =
3402 pg->peers[pg_iter].connect_peers_working_set;
3409 count_workingset_connections (struct GNUNET_TESTING_PeerGroup *pg)
3412 unsigned int pg_iter;
3416 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3419 GNUNET_CONTAINER_multihashmap_size (pg->
3421 [pg_iter].connect_peers_working_set);
3429 count_allowed_connections (struct GNUNET_TESTING_PeerGroup *pg)
3432 unsigned int pg_iter;
3436 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3439 GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
3446 struct FindClosestContext
3449 * The currently known closest peer.
3451 struct GNUNET_TESTING_Daemon *closest;
3454 * The info for the peer we are adding connections for.
3456 struct PeerData *curr_peer;
3459 * The distance (bits) between the current
3460 * peer and the currently known closest.
3462 unsigned int closest_dist;
3465 * The offset of the closest known peer in
3468 unsigned int closest_num;
3472 * Iterator over hash map entries of the allowed
3473 * peer connections. Find the closest, not already
3474 * connected peer and return it.
3476 * @param cls closure (struct FindClosestContext)
3477 * @param key current key code (hash of offset in pg)
3478 * @param value value in the hash map - a GNUNET_TESTING_Daemon
3479 * @return GNUNET_YES if we should continue to
3484 find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value)
3486 struct FindClosestContext *closest_ctx = cls;
3487 struct GNUNET_TESTING_Daemon *daemon = value;
3489 if (((closest_ctx->closest == NULL) ||
3490 (GNUNET_CRYPTO_hash_matching_bits
3491 (&daemon->id.hashPubKey,
3492 &closest_ctx->curr_peer->daemon->id.hashPubKey) >
3493 closest_ctx->closest_dist))
3495 GNUNET_CONTAINER_multihashmap_contains (closest_ctx->
3496 curr_peer->connect_peers,
3499 closest_ctx->closest_dist =
3500 GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey,
3501 &closest_ctx->curr_peer->daemon->
3503 closest_ctx->closest = daemon;
3504 uid_from_hash (key, &closest_ctx->closest_num);
3510 * From the set of connections possible, choose at num connections per
3511 * peer based on depth which are closest out of those allowed. Guaranteed
3512 * to add num peers to connect to, provided there are that many peers
3513 * in the underlay topology to connect to.
3515 * @param pg the peergroup we are dealing with
3516 * @param num how many connections at least should each peer have (if possible)?
3517 * @param proc processor to actually add the connections
3520 add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num,
3521 GNUNET_TESTING_ConnectionProcessor proc)
3523 struct FindClosestContext closest_ctx;
3527 for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */
3529 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3531 closest_ctx.curr_peer = &pg->peers[pg_iter];
3532 closest_ctx.closest = NULL;
3533 closest_ctx.closest_dist = 0;
3534 closest_ctx.closest_num = 0;
3535 GNUNET_CONTAINER_multihashmap_iterate (pg->
3536 peers[pg_iter].allowed_peers,
3537 &find_closest_peers,
3539 if (closest_ctx.closest != NULL)
3541 GNUNET_assert (closest_ctx.closest_num < pg->total);
3542 proc (pg, pg_iter, closest_ctx.closest_num);
3549 * From the set of connections possible, choose at least num connections per
3550 * peer based on depth first traversal of peer connections. If DFS leaves
3551 * peers unconnected, ensure those peers get connections.
3553 * @param pg the peergroup we are dealing with
3554 * @param num how many connections at least should each peer have (if possible)?
3557 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
3559 struct DFSContext dfs_ctx;
3562 uint32_t starting_peer;
3563 uint32_t least_connections;
3564 GNUNET_HashCode second_hash;
3566 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3568 pg->peers[pg_iter].connect_peers_working_set =
3569 GNUNET_CONTAINER_multihashmap_create (num);
3574 while ((count_workingset_connections (pg) < num * pg->total)
3575 && (count_allowed_connections (pg) > 0))
3577 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
3579 least_connections = -1; /* Set to very high number */
3580 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3582 if (GNUNET_CONTAINER_multihashmap_size
3583 (pg->peers[pg_iter].connect_peers_working_set) <
3586 starting_peer = pg_iter;
3588 GNUNET_CONTAINER_multihashmap_size (pg->
3590 [pg_iter].connect_peers_working_set);
3595 if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
3601 /* Choose a random peer from the chosen peers set of connections to add */
3603 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
3604 GNUNET_CONTAINER_multihashmap_size
3605 (pg->peers[starting_peer].connect_peers));
3606 dfs_ctx.first_uid = starting_peer;
3607 dfs_ctx.first = &pg->peers[starting_peer];
3609 dfs_ctx.current = 0;
3611 GNUNET_CONTAINER_multihashmap_iterate (pg->
3613 [starting_peer].connect_peers,
3614 &dfs_connect_iterator, &dfs_ctx);
3615 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
3616 hash_from_uid (dfs_ctx.second_uid, &second_hash);
3617 GNUNET_assert (GNUNET_YES ==
3618 GNUNET_CONTAINER_multihashmap_remove (pg->peers
3619 [starting_peer].connect_peers,
3623 [dfs_ctx.second_uid].daemon));
3624 starting_peer = dfs_ctx.second_uid;
3627 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3629 /* Remove the "old" connections */
3630 GNUNET_CONTAINER_multihashmap_destroy (pg->
3631 peers[pg_iter].connect_peers);
3632 /* And replace with the working set */
3633 pg->peers[pg_iter].connect_peers =
3634 pg->peers[pg_iter].connect_peers_working_set;
3639 * Internal callback for topology information for a particular peer.
3642 internal_topology_callback (void *cls,
3643 const struct GNUNET_PeerIdentity *peer,
3644 const struct GNUNET_TRANSPORT_ATS_Information
3647 struct CoreContext *core_ctx = cls;
3648 struct TopologyIterateContext *iter_ctx = core_ctx->iter_context;
3650 if (peer == NULL) /* Either finished, or something went wrong */
3652 iter_ctx->completed++;
3653 iter_ctx->connected--;
3654 /* One core context allocated per iteration, must free! */
3655 GNUNET_free (core_ctx);
3659 iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id,
3663 if (iter_ctx->completed == iter_ctx->total)
3665 iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL);
3666 /* Once all are done, free the iteration context */
3667 GNUNET_free (iter_ctx);
3673 * Check running topology iteration tasks, if below max start a new one, otherwise
3674 * schedule for some time in the future.
3677 schedule_get_topology (void *cls,
3678 const struct GNUNET_SCHEDULER_TaskContext *tc)
3680 struct CoreContext *core_context = cls;
3681 struct TopologyIterateContext *topology_context =
3682 (struct TopologyIterateContext *) core_context->iter_context;
3683 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3686 if (topology_context->connected > MAX_OUTSTANDING_CONNECTIONS)
3688 #if VERBOSE_TESTING > 2
3689 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3691 ("Delaying connect, we have too many outstanding connections!\n"));
3693 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3694 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3695 &schedule_get_topology, core_context);
3699 #if VERBOSE_TESTING > 2
3700 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3701 _("Creating connection, outstanding_connections is %d\n"),
3702 outstanding_connects);
3704 topology_context->connected++;
3707 GNUNET_CORE_iterate_peers (core_context->daemon->cfg,
3708 &internal_topology_callback,
3711 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n");
3712 internal_topology_callback (core_context, NULL, NULL);
3718 * Iterate over all (running) peers in the peer group, retrieve
3719 * all connections that each currently has.
3722 GNUNET_TESTING_get_topology (struct GNUNET_TESTING_PeerGroup *pg,
3723 GNUNET_TESTING_NotifyTopology cb, void *cls)
3725 struct TopologyIterateContext *topology_context;
3726 struct CoreContext *core_ctx;
3728 unsigned int total_count;
3730 /* Allocate a single topology iteration context */
3731 topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext));
3732 topology_context->topology_cb = cb;
3733 topology_context->cls = cls;
3735 for (i = 0; i < pg->total; i++)
3737 if (pg->peers[i].daemon->running == GNUNET_YES)
3739 /* Allocate one core context per core we need to connect to */
3740 core_ctx = GNUNET_malloc (sizeof (struct CoreContext));
3741 core_ctx->daemon = pg->peers[i].daemon;
3742 /* Set back pointer to topology iteration context */
3743 core_ctx->iter_context = topology_context;
3744 GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx);
3748 if (total_count == 0)
3750 cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!");
3751 GNUNET_free (topology_context);
3754 topology_context->total = total_count;
3759 * Callback function to process statistic values.
3760 * This handler is here only really to insert a peer
3761 * identity (or daemon) so the statistics can be uniquely
3762 * tied to a single running peer.
3764 * @param cls closure
3765 * @param subsystem name of subsystem that created the statistic
3766 * @param name the name of the datum
3767 * @param value the current value
3768 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
3769 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
3772 internal_stats_callback (void *cls,
3773 const char *subsystem,
3774 const char *name, uint64_t value, int is_persistent)
3776 struct StatsCoreContext *core_context = cls;
3777 struct StatsIterateContext *stats_context =
3778 (struct StatsIterateContext *) core_context->iter_context;
3780 return stats_context->proc (stats_context->cls, &core_context->daemon->id,
3781 subsystem, name, value, is_persistent);
3785 * Internal continuation call for statistics iteration.
3787 * @param cls closure, the CoreContext for this iteration
3788 * @param success whether or not the statistics iterations
3789 * was canceled or not (we don't care)
3792 internal_stats_cont (void *cls, int success)
3794 struct StatsCoreContext *core_context = cls;
3795 struct StatsIterateContext *stats_context =
3796 (struct StatsIterateContext *) core_context->iter_context;
3798 stats_context->connected--;
3799 stats_context->completed++;
3801 if (stats_context->completed == stats_context->total)
3803 stats_context->cont (stats_context->cls, GNUNET_YES);
3804 GNUNET_free (stats_context);
3807 if (core_context->stats_handle != NULL)
3808 GNUNET_STATISTICS_destroy (core_context->stats_handle, GNUNET_NO);
3810 GNUNET_free (core_context);
3814 * Check running topology iteration tasks, if below max start a new one, otherwise
3815 * schedule for some time in the future.
3818 schedule_get_statistics (void *cls,
3819 const struct GNUNET_SCHEDULER_TaskContext *tc)
3821 struct StatsCoreContext *core_context = cls;
3822 struct StatsIterateContext *stats_context =
3823 (struct StatsIterateContext *) core_context->iter_context;
3825 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3828 if (stats_context->connected > MAX_OUTSTANDING_CONNECTIONS)
3830 #if VERBOSE_TESTING > 2
3831 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3833 ("Delaying connect, we have too many outstanding connections!\n"));
3835 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3836 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3837 &schedule_get_statistics, core_context);
3841 #if VERBOSE_TESTING > 2
3842 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3843 _("Creating connection, outstanding_connections is %d\n"),
3844 outstanding_connects);
3847 stats_context->connected++;
3848 core_context->stats_handle =
3849 GNUNET_STATISTICS_create ("testing", core_context->daemon->cfg);
3850 if (core_context->stats_handle == NULL)
3852 internal_stats_cont (core_context, GNUNET_NO);
3856 core_context->stats_get_handle =
3857 GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL,
3858 GNUNET_TIME_relative_get_forever (),
3859 &internal_stats_cont, &internal_stats_callback,
3861 if (core_context->stats_get_handle == NULL)
3862 internal_stats_cont (core_context, GNUNET_NO);
3867 struct DuplicateStats
3870 * Next item in the list
3872 struct DuplicateStats *next;
3875 * Nasty string, concatenation of relevant information.
3877 char *unique_string;
3881 * Check whether the combination of port/host/unix domain socket
3882 * already exists in the list of peers being checked for statistics.
3884 * @param pg the peergroup in question
3885 * @param specific_peer the peer we're concerned with
3886 * @param stats_list the list to return to the caller
3888 * @return GNUNET_YES if the statistics instance has been seen already,
3889 * GNUNET_NO if not (and we may have added it to the list)
3892 stats_check_existing (struct GNUNET_TESTING_PeerGroup *pg,
3893 struct PeerData *specific_peer,
3894 struct DuplicateStats **stats_list)
3896 struct DuplicateStats *pos;
3897 char *unix_domain_socket;
3898 unsigned long long port;
3901 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing",
3902 "single_statistics_per_host"))
3903 return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
3907 GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg, "statistics",
3908 "unixpath", &unix_domain_socket))
3912 GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg, "statistics",
3915 GNUNET_free(unix_domain_socket);
3919 if (specific_peer->daemon->hostname != NULL)
3920 GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
3921 unix_domain_socket, port);
3923 GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port);
3927 if (0 == strcmp (to_match, pos->unique_string))
3929 GNUNET_free (unix_domain_socket);
3930 GNUNET_free (to_match);
3935 pos = GNUNET_malloc (sizeof (struct DuplicateStats));
3936 pos->unique_string = to_match;
3937 pos->next = *stats_list;
3939 GNUNET_free (unix_domain_socket);
3944 * Iterate over all (running) peers in the peer group, retrieve
3945 * all statistics from each.
3948 GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
3949 GNUNET_STATISTICS_Callback cont,
3950 GNUNET_TESTING_STATISTICS_Iterator proc,
3953 struct StatsIterateContext *stats_context;
3954 struct StatsCoreContext *core_ctx;
3956 unsigned int total_count;
3957 struct DuplicateStats *stats_list;
3958 struct DuplicateStats *pos;
3961 /* Allocate a single stats iteration context */
3962 stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext));
3963 stats_context->cont = cont;
3964 stats_context->proc = proc;
3965 stats_context->cls = cls;
3968 for (i = 0; i < pg->total; i++)
3970 if ((pg->peers[i].daemon->running == GNUNET_YES)
3972 stats_check_existing (pg, &pg->peers[i], &stats_list)))
3974 /* Allocate one core context per core we need to connect to */
3975 core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext));
3976 core_ctx->daemon = pg->peers[i].daemon;
3977 /* Set back pointer to topology iteration context */
3978 core_ctx->iter_context = stats_context;
3979 GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx);
3984 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3985 "Retrieving stats from %u total instances.\n", total_count);
3986 stats_context->total = total_count;
3987 if (stats_list != NULL)
3992 GNUNET_free (pos->unique_string);
3993 stats_list = pos->next;
3995 pos = stats_list->next;
4002 * There are many ways to connect peers that are supported by this function.
4003 * To connect peers in the same topology that was created via the
4004 * GNUNET_TESTING_create_topology, the topology variable must be set to
4005 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
4006 * a new instance of that topology will be generated and attempted to be
4007 * connected. This could result in some connections being impossible,
4008 * because some topologies are non-deterministic.
4010 * @param pg the peer group struct representing the running peers
4011 * @param topology which topology to connect the peers in
4012 * @param options options for connecting the topology
4013 * @param option_modifier modifier for options that take a parameter
4014 * @param notify_callback notification to be called once all connections completed
4015 * @param notify_cls closure for notification callback
4017 * @return the number of connections that will be attempted, GNUNET_SYSERR on error
4020 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
4021 enum GNUNET_TESTING_Topology topology,
4022 enum GNUNET_TESTING_TopologyOption options,
4023 double option_modifier,
4024 GNUNET_TESTING_NotifyCompletion
4025 notify_callback, void *notify_cls)
4029 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
4030 #if VERBOSE_TOPOLOGY
4031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4032 _("Creating clique CONNECT topology\n"));
4034 create_clique (pg, &add_actual_connections);
4036 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
4037 #if VERBOSE_TOPOLOGY
4038 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4039 _("Creating small world (ring) CONNECT topology\n"));
4041 create_small_world_ring (pg, &add_actual_connections);
4043 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
4044 #if VERBOSE_TOPOLOGY
4045 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4046 _("Creating small world (2d-torus) CONNECT topology\n"));
4048 create_small_world (pg, &add_actual_connections);
4050 case GNUNET_TESTING_TOPOLOGY_RING:
4051 #if VERBOSE_TOPOLOGY
4052 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4053 _("Creating ring CONNECT topology\n"));
4055 create_ring (pg, &add_actual_connections);
4057 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
4058 #if VERBOSE_TOPOLOGY
4059 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4060 _("Creating 2d torus CONNECT topology\n"));
4062 create_2d_torus (pg, &add_actual_connections);
4064 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
4065 #if VERBOSE_TOPOLOGY
4066 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4067 _("Creating Erdos-Renyi CONNECT topology\n"));
4069 create_erdos_renyi (pg, &add_actual_connections);
4071 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
4072 #if VERBOSE_TOPOLOGY
4073 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4074 _("Creating InterNAT CONNECT topology\n"));
4076 create_nated_internet (pg, &add_actual_connections);
4078 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
4079 #if VERBOSE_TOPOLOGY
4080 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4081 _("Creating Scale Free CONNECT topology\n"));
4083 create_scale_free (pg, &add_actual_connections);
4085 case GNUNET_TESTING_TOPOLOGY_LINE:
4086 #if VERBOSE_TOPOLOGY
4087 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4088 _("Creating straight line CONNECT topology\n"));
4090 create_line (pg, &add_actual_connections);
4092 case GNUNET_TESTING_TOPOLOGY_NONE:
4093 #if VERBOSE_TOPOLOGY
4094 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4095 _("Creating no CONNECT topology\n"));
4097 copy_allowed_topology (pg);
4100 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4102 ("Unknown topology specification, can't connect peers!\n"));
4103 return GNUNET_SYSERR;
4108 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
4109 #if VERBOSE_TOPOLOGY
4110 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4112 ("Connecting random subset (%'.2f percent) of possible peers\n"),
4113 100 * option_modifier);
4115 choose_random_connections (pg, option_modifier);
4117 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
4118 #if VERBOSE_TOPOLOGY
4119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4120 _("Connecting a minimum of %u peers each (if possible)\n"),
4121 (unsigned int) option_modifier);
4123 choose_minimum (pg, (unsigned int) option_modifier);
4125 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
4126 #if VERBOSE_TOPOLOGY
4127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4129 ("Using DFS to connect a minimum of %u peers each (if possible)\n"),
4130 (unsigned int) option_modifier);
4132 perform_dfs (pg, (int) option_modifier);
4134 case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST:
4135 #if VERBOSE_TOPOLOGY
4136 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4138 ("Finding additional %u closest peers each (if possible)\n"),
4139 (unsigned int) option_modifier);
4141 add_closest (pg, (unsigned int) option_modifier,
4142 &add_actual_connections);
4144 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
4146 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
4152 return connect_topology (pg, notify_callback, notify_cls);
4156 * Callback that is called whenever a hostkey is generated
4157 * for a peer. Call the real callback and decrement the
4158 * starting counter for the peergroup.
4160 * @param cls closure
4161 * @param id identifier for the daemon, NULL on error
4162 * @param d handle for the daemon
4163 * @param emsg error message (NULL on success)
4166 internal_hostkey_callback (void *cls,
4167 const struct GNUNET_PeerIdentity *id,
4168 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4170 struct InternalStartContext *internal_context = cls;
4171 internal_context->peer->pg->starting--;
4172 internal_context->peer->pg->started++;
4173 if (internal_context->hostkey_callback != NULL)
4174 internal_context->hostkey_callback (internal_context->hostkey_cls, id, d,
4176 else if (internal_context->peer->pg->started ==
4177 internal_context->peer->pg->total)
4179 internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
4180 GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg);
4185 * Callback that is called whenever a peer has finished starting.
4186 * Call the real callback and decrement the starting counter
4187 * for the peergroup.
4189 * @param cls closure
4190 * @param id identifier for the daemon, NULL on error
4192 * @param d handle for the daemon
4193 * @param emsg error message (NULL on success)
4196 internal_startup_callback (void *cls,
4197 const struct GNUNET_PeerIdentity *id,
4198 const struct GNUNET_CONFIGURATION_Handle *cfg,
4199 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4201 struct InternalStartContext *internal_context = cls;
4202 internal_context->peer->pg->starting--;
4203 if (internal_context->start_cb != NULL)
4204 internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d,
4209 internal_continue_startup (void *cls,
4210 const struct GNUNET_SCHEDULER_TaskContext *tc)
4212 struct InternalStartContext *internal_context = cls;
4214 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4219 if (internal_context->peer->pg->starting < MAX_CONCURRENT_STARTING)
4221 internal_context->peer->pg->starting++;
4222 GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
4226 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4227 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4228 &internal_continue_startup,
4235 * Callback for informing us about a successful
4236 * or unsuccessful churn start call.
4238 * @param cls a ChurnContext
4239 * @param id the peer identity of the started peer
4240 * @param cfg the handle to the configuration of the peer
4241 * @param d handle to the daemon for the peer
4242 * @param emsg NULL on success, non-NULL on failure
4246 churn_start_callback (void *cls,
4247 const struct GNUNET_PeerIdentity *id,
4248 const struct GNUNET_CONFIGURATION_Handle *cfg,
4249 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4251 struct ChurnRestartContext *startup_ctx = cls;
4252 struct ChurnContext *churn_ctx = startup_ctx->churn_ctx;
4254 unsigned int total_left;
4255 char *error_message;
4257 error_message = NULL;
4260 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4261 "Churn stop callback failed with error `%s'\n", emsg);
4262 churn_ctx->num_failed_start++;
4266 churn_ctx->num_to_start--;
4270 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4271 "Started peer, %d left.\n", churn_ctx->num_to_start);
4275 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
4276 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
4278 if (total_left == 0)
4280 if ((churn_ctx->num_failed_stop > 0)
4281 || (churn_ctx->num_failed_start > 0))
4282 GNUNET_asprintf (&error_message,
4283 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
4284 churn_ctx->num_failed_start,
4285 churn_ctx->num_failed_stop);
4286 churn_ctx->cb (churn_ctx->cb_cls, error_message);
4287 GNUNET_free_non_null (error_message);
4288 GNUNET_free (churn_ctx);
4289 GNUNET_free (startup_ctx);
4295 schedule_churn_restart (void *cls,
4296 const struct GNUNET_SCHEDULER_TaskContext *tc)
4298 struct PeerRestartContext *peer_restart_ctx = cls;
4299 struct ChurnRestartContext *startup_ctx =
4300 peer_restart_ctx->churn_restart_ctx;
4302 if (startup_ctx->outstanding > MAX_CONCURRENT_STARTING)
4303 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4304 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4305 &schedule_churn_restart, peer_restart_ctx);
4308 GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon,
4309 startup_ctx->timeout,
4310 &churn_start_callback,
4312 GNUNET_free (peer_restart_ctx);
4317 internal_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
4319 struct InternalStartContext *internal_context = cls;
4321 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4326 if (internal_context->peer->pg->starting < MAX_CONCURRENT_HOSTKEYS)
4328 internal_context->peer->pg->starting++;
4329 internal_context->peer->daemon =
4330 GNUNET_TESTING_daemon_start (internal_context->peer->cfg,
4331 internal_context->timeout,
4332 internal_context->hostname,
4333 internal_context->username,
4334 internal_context->sshport,
4335 internal_context->hostkey,
4336 &internal_hostkey_callback,
4338 &internal_startup_callback,
4343 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4344 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4345 &internal_start, internal_context);
4350 * Function which continues a peer group starting up
4351 * after successfully generating hostkeys for each peer.
4353 * @param pg the peer group to continue starting
4357 GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg)
4362 for (i = 0; i < pg->total; i++)
4364 GNUNET_SCHEDULER_add_now (&internal_continue_startup,
4365 &pg->peers[i].internal_context);
4366 //GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
4371 * Start count gnunet instances with the same set of transports and
4372 * applications. The port numbers (any option called "PORT") will be
4373 * adjusted to ensure that no two peers running on the same system
4374 * have the same port(s) in their respective configurations.
4376 * @param cfg configuration template to use
4377 * @param total number of daemons to start
4378 * @param timeout total time allowed for peers to start
4379 * @param hostkey_callback function to call on each peers hostkey generation
4380 * if NULL, peers will be started by this call, if non-null,
4381 * GNUNET_TESTING_daemons_continue_startup must be called after
4382 * successful hostkey generation
4383 * @param hostkey_cls closure for hostkey callback
4384 * @param cb function to call on each daemon that was started
4385 * @param cb_cls closure for cb
4386 * @param connect_callback function to call each time two hosts are connected
4387 * @param connect_callback_cls closure for connect_callback
4388 * @param hostnames linked list of host structs to use to start peers on
4389 * (NULL to run on localhost only)
4391 * @return NULL on error, otherwise handle to control peer group
4393 struct GNUNET_TESTING_PeerGroup *
4394 GNUNET_TESTING_daemons_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
4396 struct GNUNET_TIME_Relative timeout,
4397 GNUNET_TESTING_NotifyHostkeyCreated
4398 hostkey_callback, void *hostkey_cls,
4399 GNUNET_TESTING_NotifyDaemonRunning cb,
4401 GNUNET_TESTING_NotifyConnection
4402 connect_callback, void *connect_callback_cls,
4403 const struct GNUNET_TESTING_Host *hostnames)
4405 struct GNUNET_TESTING_PeerGroup *pg;
4406 const struct GNUNET_TESTING_Host *hostpos;
4412 const char *hostname;
4413 const char *username;
4414 char *baseservicehome;
4415 char *newservicehome;
4417 char *hostkeys_file;
4419 struct GNUNET_DISK_FileHandle *fd;
4420 struct GNUNET_CONFIGURATION_Handle *pcfg;
4422 unsigned int hostcnt;
4428 uint64_t total_hostkeys;
4435 hostkey_data = NULL;
4438 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
4440 pg->notify_connection = connect_callback;
4441 pg->notify_connection_cls = connect_callback_cls;
4443 pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
4444 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
4445 if (NULL != hostnames)
4448 hostpos = hostnames;
4449 while (hostpos != NULL)
4451 hostpos = hostpos->next;
4454 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
4457 hostpos = hostnames;
4458 while (hostpos != NULL)
4460 pg->hosts[off].minport = LOW_PORT;
4461 pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname);
4462 if (hostpos->username != NULL)
4463 pg->hosts[off].username = GNUNET_strdup (hostpos->username);
4464 pg->hosts[off].sshport = hostpos->port;
4465 hostpos = hostpos->next;
4475 pg->num_hosts = off;
4479 /* skip leading spaces */
4480 while ((0 != *hostnames) && (isspace ((unsigned char) *hostnames)))
4483 while ('\0' != *rpos)
4485 if (isspace ((unsigned char) *rpos))
4489 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
4491 start = GNUNET_strdup (hostnames);
4493 while ('\0' != *pos)
4495 if (isspace ((unsigned char) *pos))
4498 if (strlen (start) > 0)
4500 pg->hosts[off].minport = LOW_PORT;
4501 pg->hosts[off++].hostname = start;
4507 if (strlen (start) > 0)
4509 pg->hosts[off].minport = LOW_PORT;
4510 pg->hosts[off++].hostname = start;
4514 GNUNET_free (start);
4515 GNUNET_free (pg->hosts);
4519 minport = 0; /* make gcc happy */
4528 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "HOSTKEYSFILE",
4531 if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file))
4532 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Couldn't read hostkeys file!");
4535 /* Check hostkey file size, read entire thing into memory */
4536 fd = GNUNET_DISK_file_open (hostkeys_file, GNUNET_DISK_OPEN_READ,
4537 GNUNET_DISK_PERM_NONE);
4540 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", hostkeys_file);
4544 if (GNUNET_YES != GNUNET_DISK_file_size (hostkeys_file, &fs, GNUNET_YES))
4547 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Found file size %llu for hostkeys, expect hostkeys to be size %d\n", fs, HOSTKEYFILESIZE);
4549 if (fs % HOSTKEYFILESIZE != 0)
4551 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "File size %llu seems incorrect for hostkeys...\n", fs);
4555 total_hostkeys = fs / HOSTKEYFILESIZE;
4556 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Will read %llu hostkeys from file\n", total_hostkeys);
4557 hostkey_data = GNUNET_malloc_large (fs);
4558 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, hostkey_data, fs));
4563 for (off = 0; off < total; off++)
4567 hostname = pg->hosts[off % hostcnt].hostname;
4568 username = pg->hosts[off % hostcnt].username;
4569 sshport = pg->hosts[off % hostcnt].sshport;
4570 pcfg = make_config (cfg,
4571 &pg->hosts[off % hostcnt].minport,
4572 &upnum, hostname, &fdnum);
4579 pcfg = make_config (cfg, &minport, &upnum, hostname, &fdnum);
4584 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4586 ("Could not create configuration for peer number %u on `%s'!\n"),
4587 off, hostname == NULL ? "localhost" : hostname);
4592 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
4595 GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
4596 GNUNET_free (baseservicehome);
4600 tmpdir = getenv ("TMPDIR");
4601 tmpdir = tmpdir ? tmpdir : "/tmp";
4602 GNUNET_asprintf (&newservicehome,
4604 tmpdir, "gnunet-testing-test-test", off);
4606 GNUNET_CONFIGURATION_set_value_string (pcfg,
4608 "SERVICEHOME", newservicehome);
4609 GNUNET_free (newservicehome);
4610 pg->peers[off].cfg = pcfg;
4611 pg->peers[off].allowed_peers =
4612 GNUNET_CONTAINER_multihashmap_create (total);
4613 pg->peers[off].connect_peers =
4614 GNUNET_CONTAINER_multihashmap_create (total);
4615 pg->peers[off].blacklisted_peers =
4616 GNUNET_CONTAINER_multihashmap_create (total);
4617 pg->peers[off].pg = pg;
4619 pg->peers[off].internal_context.peer = &pg->peers[off];
4620 pg->peers[off].internal_context.timeout = timeout;
4621 pg->peers[off].internal_context.hostname = hostname;
4622 pg->peers[off].internal_context.username = username;
4623 pg->peers[off].internal_context.sshport = sshport;
4624 if (hostkey_data != NULL)
4625 pg->peers[off].internal_context.hostkey = &hostkey_data[off * HOSTKEYFILESIZE];
4626 pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
4627 pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
4628 pg->peers[off].internal_context.start_cb = cb;
4629 pg->peers[off].internal_context.start_cb_cls = cb_cls;
4631 GNUNET_SCHEDULER_add_now (&internal_start,
4632 &pg->peers[off].internal_context);
4639 * Get a daemon by number, so callers don't have to do nasty
4640 * offsetting operation.
4642 struct GNUNET_TESTING_Daemon *
4643 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg,
4644 unsigned int position)
4646 if (position < pg->total)
4647 return pg->peers[position].daemon;
4653 * Get a daemon by peer identity, so callers can
4654 * retrieve the daemon without knowing it's offset.
4656 * @param pg the peer group to retrieve the daemon from
4657 * @param peer_id the peer identity of the daemon to retrieve
4659 * @return the daemon on success, or NULL if no such peer identity is found
4661 struct GNUNET_TESTING_Daemon *
4662 GNUNET_TESTING_daemon_get_by_id (struct GNUNET_TESTING_PeerGroup *pg,
4663 struct GNUNET_PeerIdentity *peer_id)
4667 for (i = 0; i < pg->total; i++)
4670 memcmp (&pg->peers[i].daemon->id, peer_id,
4671 sizeof (struct GNUNET_PeerIdentity)))
4672 return pg->peers[i].daemon;
4679 * Prototype of a function that will be called when a
4680 * particular operation was completed the testing library.
4682 * @param cls closure (a struct RestartContext)
4683 * @param id id of the peer that was restarted
4684 * @param cfg handle to the configuration of the peer
4685 * @param d handle to the daemon that was restarted
4686 * @param emsg NULL on success
4689 restart_callback (void *cls,
4690 const struct GNUNET_PeerIdentity *id,
4691 const struct GNUNET_CONFIGURATION_Handle *cfg,
4692 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4694 struct RestartContext *restart_context = cls;
4698 restart_context->peers_restarted++;
4702 restart_context->peers_restart_failed++;
4705 if (restart_context->peers_restarted == restart_context->peer_group->total)
4707 restart_context->callback (restart_context->callback_cls, NULL);
4708 GNUNET_free (restart_context);
4710 else if (restart_context->peers_restart_failed +
4711 restart_context->peers_restarted ==
4712 restart_context->peer_group->total)
4714 restart_context->callback (restart_context->callback_cls,
4715 "Failed to restart peers!");
4716 GNUNET_free (restart_context);
4722 * Callback for informing us about a successful
4723 * or unsuccessful churn stop call.
4725 * @param cls a ChurnContext
4726 * @param emsg NULL on success, non-NULL on failure
4730 churn_stop_callback (void *cls, const char *emsg)
4732 struct ShutdownContext *shutdown_ctx = cls;
4733 struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls;
4734 unsigned int total_left;
4735 char *error_message;
4737 error_message = NULL;
4738 shutdown_ctx->outstanding--;
4742 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4743 "Churn stop callback failed with error `%s'\n", emsg);
4744 churn_ctx->num_failed_stop++;
4748 churn_ctx->num_to_stop--;
4752 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4753 "Stopped peer, %d left.\n", churn_ctx->num_to_stop);
4756 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
4757 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
4759 if (total_left == 0)
4761 if ((churn_ctx->num_failed_stop > 0)
4762 || (churn_ctx->num_failed_start > 0))
4764 GNUNET_asprintf (&error_message,
4765 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
4766 churn_ctx->num_failed_start,
4767 churn_ctx->num_failed_stop);
4769 churn_ctx->cb (churn_ctx->cb_cls, error_message);
4770 GNUNET_free_non_null (error_message);
4771 GNUNET_free (churn_ctx);
4772 GNUNET_free (shutdown_ctx);
4777 * Count the number of running peers.
4779 * @param pg handle for the peer group
4781 * @return the number of currently running peers in the peer group
4784 GNUNET_TESTING_daemons_running (struct GNUNET_TESTING_PeerGroup *pg)
4787 unsigned int running = 0;
4788 for (i = 0; i < pg->total; i++)
4790 if (pg->peers[i].daemon->running == GNUNET_YES)
4792 GNUNET_assert (running != -1);
4800 * Task to rate limit the number of outstanding peer shutdown
4801 * requests. This is necessary for making sure we don't do
4802 * too many ssh connections at once, but is generally nicer
4803 * to any system as well (graduated task starts, as opposed
4804 * to calling gnunet-arm N times all at once).
4807 schedule_churn_shutdown_task (void *cls,
4808 const struct GNUNET_SCHEDULER_TaskContext *tc)
4810 struct PeerShutdownContext *peer_shutdown_ctx = cls;
4811 struct ShutdownContext *shutdown_ctx;
4813 GNUNET_assert (peer_shutdown_ctx != NULL);
4814 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
4815 GNUNET_assert (shutdown_ctx != NULL);
4817 if (shutdown_ctx->outstanding > MAX_CONCURRENT_SHUTDOWN)
4818 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4819 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4820 &schedule_churn_shutdown_task,
4824 shutdown_ctx->outstanding++;
4825 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
4826 shutdown_ctx->timeout, shutdown_ctx->cb,
4827 shutdown_ctx, GNUNET_NO, GNUNET_YES);
4828 GNUNET_free (peer_shutdown_ctx);
4833 * Simulate churn by stopping some peers (and possibly
4834 * re-starting others if churn is called multiple times). This
4835 * function can only be used to create leave-join churn (peers "never"
4836 * leave for good). First "voff" random peers that are currently
4837 * online will be taken offline; then "von" random peers that are then
4838 * offline will be put back online. No notifications will be
4839 * generated for any of these operations except for the callback upon
4842 * @param pg handle for the peer group
4843 * @param voff number of peers that should go offline
4844 * @param von number of peers that should come back online;
4845 * must be zero on first call (since "testbed_start"
4846 * always starts all of the peers)
4847 * @param timeout how long to wait for operations to finish before
4849 * @param cb function to call at the end
4850 * @param cb_cls closure for cb
4853 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
4856 struct GNUNET_TIME_Relative timeout,
4857 GNUNET_TESTING_NotifyCompletion cb,
4860 struct ChurnContext *churn_ctx;
4861 struct ShutdownContext *shutdown_ctx;
4862 struct PeerShutdownContext *peer_shutdown_ctx;
4863 struct PeerRestartContext *peer_restart_ctx;
4864 struct ChurnRestartContext *churn_startup_ctx;
4866 unsigned int running;
4867 unsigned int stopped;
4868 unsigned int total_running;
4869 unsigned int total_stopped;
4871 unsigned int *running_arr;
4872 unsigned int *stopped_arr;
4873 unsigned int *running_permute;
4874 unsigned int *stopped_permute;
4879 if ((von == 0) && (voff == 0)) /* No peers at all? */
4885 for (i = 0; i < pg->total; i++)
4887 if (pg->peers[i].daemon->running == GNUNET_YES)
4889 GNUNET_assert (running != -1);
4894 GNUNET_assert (stopped != -1);
4901 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4902 "Trying to stop more peers than are currently running!\n");
4903 cb (cb_cls, "Trying to stop more peers than are currently running!");
4909 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4910 "Trying to start more peers than are currently stopped!\n");
4911 cb (cb_cls, "Trying to start more peers than are currently stopped!");
4915 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
4919 running_arr = GNUNET_malloc (running * sizeof (unsigned int));
4923 stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int));
4925 running_permute = NULL;
4926 stopped_permute = NULL;
4930 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, running);
4933 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, stopped);
4935 total_running = running;
4936 total_stopped = stopped;
4940 churn_ctx->num_to_start = von;
4941 churn_ctx->num_to_stop = voff;
4943 churn_ctx->cb_cls = cb_cls;
4945 for (i = 0; i < pg->total; i++)
4947 if (pg->peers[i].daemon->running == GNUNET_YES)
4949 GNUNET_assert ((running_arr != NULL) && (total_running > running));
4950 running_arr[running] = i;
4955 GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
4956 stopped_arr[stopped] = i;
4961 GNUNET_assert (running >= voff);
4964 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
4965 shutdown_ctx->cb = &churn_stop_callback;
4966 shutdown_ctx->cb_cls = churn_ctx;
4967 shutdown_ctx->total_peers = voff;
4968 shutdown_ctx->timeout = timeout;
4971 for (i = 0; i < voff; i++)
4974 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n",
4975 running_permute[i]);
4977 GNUNET_assert (running_arr != NULL);
4978 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
4979 peer_shutdown_ctx->daemon =
4980 pg->peers[running_arr[running_permute[i]]].daemon;
4981 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
4982 GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task,
4986 GNUNET_TESTING_daemon_stop (pg->peers[running_arr[running_permute[i]]].daemon,
4988 &churn_stop_callback, churn_ctx,
4989 GNUNET_NO, GNUNET_YES); */
4992 GNUNET_assert (stopped >= von);
4995 churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
4996 churn_startup_ctx->churn_ctx = churn_ctx;
4997 churn_startup_ctx->timeout = timeout;
4999 for (i = 0; i < von; i++)
5002 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n",
5003 stopped_permute[i]);
5005 GNUNET_assert (stopped_arr != NULL);
5006 peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext));
5007 peer_restart_ctx->churn_restart_ctx = churn_startup_ctx;
5008 peer_restart_ctx->daemon =
5009 pg->peers[stopped_arr[stopped_permute[i]]].daemon;
5010 GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx);
5012 GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon,
5013 timeout, &churn_start_callback, churn_ctx); */
5016 GNUNET_free_non_null (running_arr);
5017 GNUNET_free_non_null (stopped_arr);
5018 GNUNET_free_non_null (running_permute);
5019 GNUNET_free_non_null (stopped_permute);
5024 * Restart all peers in the given group.
5026 * @param pg the handle to the peer group
5027 * @param callback function to call on completion (or failure)
5028 * @param callback_cls closure for the callback function
5031 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg,
5032 GNUNET_TESTING_NotifyCompletion callback,
5035 struct RestartContext *restart_context;
5040 restart_context = GNUNET_malloc (sizeof (struct RestartContext));
5041 restart_context->peer_group = pg;
5042 restart_context->peers_restarted = 0;
5043 restart_context->callback = callback;
5044 restart_context->callback_cls = callback_cls;
5046 for (off = 0; off < pg->total; off++)
5048 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon,
5049 &restart_callback, restart_context);
5055 * Start or stop an individual peer from the given group.
5057 * @param pg handle to the peer group
5058 * @param offset which peer to start or stop
5059 * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
5060 * @param timeout how long to wait for shutdown
5061 * @param cb function to call at the end
5062 * @param cb_cls closure for cb
5065 GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg,
5066 unsigned int offset,
5068 struct GNUNET_TIME_Relative timeout,
5069 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
5071 struct ShutdownContext *shutdown_ctx;
5072 struct ChurnRestartContext *startup_ctx;
5073 struct ChurnContext *churn_ctx;
5075 if (GNUNET_NO == desired_status)
5077 if (NULL != pg->peers[offset].daemon)
5079 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
5080 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
5081 churn_ctx->num_to_start = 0;
5082 churn_ctx->num_to_stop = 1;
5084 churn_ctx->cb_cls = cb_cls;
5085 shutdown_ctx->cb_cls = churn_ctx;
5086 GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon,
5087 timeout, &churn_stop_callback,
5088 shutdown_ctx, GNUNET_NO, GNUNET_YES);
5091 else if (GNUNET_YES == desired_status)
5093 if (NULL == pg->peers[offset].daemon)
5095 startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
5096 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
5097 churn_ctx->num_to_start = 1;
5098 churn_ctx->num_to_stop = 0;
5100 churn_ctx->cb_cls = cb_cls;
5101 startup_ctx->churn_ctx = churn_ctx;
5102 GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon,
5103 timeout, &churn_start_callback,
5113 * Callback for shutting down peers in a peer group.
5115 * @param cls closure (struct ShutdownContext)
5116 * @param emsg NULL on success
5119 internal_shutdown_callback (void *cls, const char *emsg)
5121 struct ShutdownContext *shutdown_ctx = cls;
5123 shutdown_ctx->outstanding--;
5126 shutdown_ctx->peers_down++;
5130 shutdown_ctx->peers_failed++;
5133 if ((shutdown_ctx->cb != NULL)
5134 && (shutdown_ctx->peers_down + shutdown_ctx->peers_failed ==
5135 shutdown_ctx->total_peers))
5137 if (shutdown_ctx->peers_failed > 0)
5138 shutdown_ctx->cb (shutdown_ctx->cb_cls,
5139 "Not all peers successfully shut down!");
5141 shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL);
5142 GNUNET_free (shutdown_ctx);
5148 * Task to rate limit the number of outstanding peer shutdown
5149 * requests. This is necessary for making sure we don't do
5150 * too many ssh connections at once, but is generally nicer
5151 * to any system as well (graduated task starts, as opposed
5152 * to calling gnunet-arm N times all at once).
5155 schedule_shutdown_task (void *cls,
5156 const struct GNUNET_SCHEDULER_TaskContext *tc)
5158 struct PeerShutdownContext *peer_shutdown_ctx = cls;
5159 struct ShutdownContext *shutdown_ctx;
5161 GNUNET_assert (peer_shutdown_ctx != NULL);
5162 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
5163 GNUNET_assert (shutdown_ctx != NULL);
5165 if (shutdown_ctx->outstanding > MAX_CONCURRENT_SHUTDOWN)
5166 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5167 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5168 &schedule_shutdown_task, peer_shutdown_ctx);
5171 shutdown_ctx->outstanding++;
5172 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
5173 shutdown_ctx->timeout,
5174 &internal_shutdown_callback, shutdown_ctx,
5175 GNUNET_YES, GNUNET_NO);
5176 GNUNET_free (peer_shutdown_ctx);
5181 * Shutdown all peers started in the given group.
5183 * @param pg handle to the peer group
5184 * @param timeout how long to wait for shutdown
5185 * @param cb callback to notify upon success or failure
5186 * @param cb_cls closure for cb
5189 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg,
5190 struct GNUNET_TIME_Relative timeout,
5191 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
5194 struct ShutdownContext *shutdown_ctx;
5195 struct PeerShutdownContext *peer_shutdown_ctx;
5197 GNUNET_assert (pg->total > 0);
5199 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
5200 shutdown_ctx->cb = cb;
5201 shutdown_ctx->cb_cls = cb_cls;
5202 shutdown_ctx->total_peers = pg->total;
5203 shutdown_ctx->timeout = timeout;
5204 /* shtudown_ctx->outstanding = 0; */
5206 for (off = 0; off < pg->total; off++)
5208 GNUNET_assert (NULL != pg->peers[off].daemon);
5209 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
5210 peer_shutdown_ctx->daemon = pg->peers[off].daemon;
5211 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
5212 GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx);
5213 //GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, timeout, shutdown_cb, shutdown_ctx, GNUNET_YES, GNUNET_NO);
5214 if (NULL != pg->peers[off].cfg)
5215 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
5216 if (pg->peers[off].allowed_peers != NULL)
5217 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers);
5218 if (pg->peers[off].connect_peers != NULL)
5219 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers);
5220 if (pg->peers[off].blacklisted_peers != NULL)
5221 GNUNET_CONTAINER_multihashmap_destroy (pg->
5222 peers[off].blacklisted_peers);
5224 GNUNET_free (pg->peers);
5225 for (off = 0; off < pg->num_hosts; off++)
5227 GNUNET_free (pg->hosts[off].hostname);
5228 GNUNET_free_non_null (pg->hosts[off].username);
5230 GNUNET_free_non_null (pg->hosts);
5235 /* end of testing_group.c */