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_YES
35 #define VERBOSE_TOPOLOGY GNUNET_YES
37 #define DEBUG_CHURN GNUNET_NO
42 * Lowest port used for GNUnet testing. Should be high enough to not
43 * conflict with other applications running on the hosts but be low
44 * enough to not conflict with client-ports (typically starting around
47 #define LOW_PORT 10000
50 * Highest port used for GNUnet testing. Should be low enough to not
51 * conflict with the port range for "local" ports (client apps; see
52 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
54 #define HIGH_PORT 56000
56 /* Maximum time to delay connect attempt */
57 #define MAX_CONNECT_DELAY 300
59 #define MAX_CONCURRENT_HOSTKEYS 500
62 * Which list of peers do we need to modify?
66 /** Modify allowed peers */
69 /** Modify connect peers */
72 /** Modify blacklist peers */
75 /** Modify workingset peers */
80 * Prototype of a function called whenever two peers would be connected
81 * in a certain topology.
83 typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor) (struct
84 GNUNET_TESTING_PeerGroup
94 * Context for handling churning a peer group
99 * The peergroup we are dealing with.
101 struct GNUNET_TESTING_PeerGroup *pg;
104 * Callback used to notify of churning finished
106 GNUNET_TESTING_NotifyCompletion cb;
109 * Closure for callback
114 * Number of peers that still need to be started
116 unsigned int num_to_start;
119 * Number of peers that still need to be stopped
121 unsigned int num_to_stop;
124 * Number of peers that failed to start
126 unsigned int num_failed_start;
129 * Number of peers that failed to stop
131 unsigned int num_failed_stop;
134 struct RestartContext
137 * The group of peers being restarted
139 struct GNUNET_TESTING_PeerGroup *peer_group;
142 * How many peers have been restarted thus far
144 unsigned int peers_restarted;
147 * How many peers got an error when restarting
149 unsigned int peers_restart_failed;
152 * The function to call once all peers have been restarted
154 GNUNET_TESTING_NotifyCompletion callback;
157 * Closure for callback function
164 struct ShutdownContext
166 struct GNUNET_TESTING_PeerGroup *pg;
168 * Total peers to wait for
170 unsigned int total_peers;
173 * Number of peers successfully shut down
175 unsigned int peers_down;
178 * Number of peers failed to shut down
180 unsigned int peers_failed;
183 * Number of peers we have started shutting
184 * down. If too many, wait on them.
186 unsigned int outstanding;
189 * Timeout for shutdown.
191 struct GNUNET_TIME_Relative timeout;
194 * Callback to call when all peers either
195 * shutdown or failed to shutdown
197 GNUNET_TESTING_NotifyCompletion cb;
206 * Individual shutdown context for a particular peer.
208 struct PeerShutdownContext
211 * Pointer to the high level shutdown context.
213 struct ShutdownContext *shutdown_ctx;
216 * The daemon handle for the peer to shut down.
218 struct GNUNET_TESTING_Daemon *daemon;
222 * Individual shutdown context for a particular peer.
224 struct PeerRestartContext
227 * Pointer to the high level restart context.
229 struct ChurnRestartContext *churn_restart_ctx;
232 * The daemon handle for the peer to shut down.
234 struct GNUNET_TESTING_Daemon *daemon;
238 struct CreateTopologyContext
242 * Function to call with number of connections
244 GNUNET_TESTING_NotifyConnections cont;
247 * Closure for connection notification
254 /** Waiting to read number of peers */
257 /** Should find next peer index */
260 /** Should find colon */
263 /** Should read other peer index, space, or endline */
269 struct PeerConnection
274 struct PeerConnection *prev;
279 struct PeerConnection *next;
283 * Index of daemon in pg->peers
290 struct InternalStartContext
293 * Pointer to peerdata
295 struct PeerData *peer;
298 * Timeout for peer startup
300 struct GNUNET_TIME_Relative timeout;
303 * Client callback for hostkey notification
305 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
308 * Closure for hostkey_callback
313 * Client callback for peer start notification
315 GNUNET_TESTING_NotifyDaemonRunning start_cb;
323 * Hostname, where to start the peer
325 const char *hostname;
328 * Username to use when connecting to the
331 const char *username;
334 * Pointer to starting memory location of a hostkey
339 * Port to use for ssh.
345 struct ChurnRestartContext
348 * PeerGroup that we are working with.
350 struct GNUNET_TESTING_PeerGroup *pg;
353 * Number of restarts currently in flight.
355 unsigned int outstanding;
358 * Handle to the underlying churn context.
360 struct ChurnContext *churn_ctx;
363 * How long to allow the operation to take.
365 struct GNUNET_TIME_Relative timeout;
369 * Data we keep per peer.
374 * (Initial) configuration of the host.
375 * (initial because clients could change
376 * it and we would not know about those
379 struct GNUNET_CONFIGURATION_Handle *cfg;
382 * Handle for controlling the daemon.
384 struct GNUNET_TESTING_Daemon *daemon;
387 * The peergroup this peer belongs to.
389 struct GNUNET_TESTING_PeerGroup *pg;
393 * Linked list of allowed peer connections.
395 struct PeerConnection *allowed_peers_head;
398 * Linked list of allowed peer connections.
400 struct PeerConnection *allowed_peers_tail;
403 * Linked list of blacklisted peer connections.
405 struct PeerConnection *blacklisted_peers_head;
408 * Linked list of blacklisted peer connections.
410 struct PeerConnection *blacklisted_peers_tail;
413 * Linked list of connect peer connections.
415 struct PeerConnection *connect_peers_head;
418 * Linked list of connect peer connections.
420 struct PeerConnection *connect_peers_tail;
423 * Linked list of connect peer connections.
425 struct PeerConnection *connect_peers_working_set_head;
428 * Linked list of connect peer connections.
430 struct PeerConnection *connect_peers_working_set_tail;
434 * Hash map of allowed peer connections (F2F created topology)
436 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
439 * Hash map of blacklisted peers
441 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
444 * Hash map of peer connections
446 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
449 * Temporary hash map of peer connections
451 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
455 * Temporary variable for topology creation, should be reset before
456 * creating any topology so the count is valid once finished.
461 * Context to keep track of peers being started, to
462 * stagger hostkey generation and peer startup.
464 struct InternalStartContext internal_context;
469 * Linked list of per-host data.
479 * SSH username to use when connecting to this host.
484 * SSH port to use when connecting to this host.
489 * Lowest port that we have not yet used
495 struct TopologyIterateContext
498 * The peergroup we are working with.
500 struct GNUNET_TESTING_PeerGroup *pg;
503 * Callback for notifying of two connected peers.
505 GNUNET_TESTING_NotifyTopology topology_cb;
508 * Closure for topology_cb
513 * Number of peers currently connected to.
515 unsigned int connected;
518 * Number of peers we have finished iterating.
520 unsigned int completed;
523 * Number of peers total.
528 struct StatsIterateContext
531 * The peergroup that we are dealing with.
533 struct GNUNET_TESTING_PeerGroup *pg;
536 * Continuation to call once all stats information has been retrieved.
538 GNUNET_STATISTICS_Callback cont;
541 * Proc function to call on each value received.
543 GNUNET_TESTING_STATISTICS_Iterator proc;
546 * Closure for topology_cb
551 * Number of peers currently connected to.
553 unsigned int connected;
556 * Number of peers we have finished iterating.
558 unsigned int completed;
561 * Number of peers total.
569 struct GNUNET_TESTING_Daemon *daemon;
572 struct StatsCoreContext
575 struct GNUNET_TESTING_Daemon *daemon;
577 * Handle to the statistics service.
579 struct GNUNET_STATISTICS_Handle *stats_handle;
582 * Handle for getting statistics.
584 struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
588 * Handle to a group of GNUnet peers.
590 struct GNUNET_TESTING_PeerGroup
593 * Configuration template.
595 const struct GNUNET_CONFIGURATION_Handle *cfg;
598 * Function to call on each started daemon.
600 //GNUNET_TESTING_NotifyDaemonRunning cb;
608 * Function to call on each topology connection created
610 GNUNET_TESTING_NotifyConnection notify_connection;
613 * Callback for notify_connection
615 void *notify_connection_cls;
618 * Array of information about hosts.
620 struct HostData *hosts;
623 * Number of hosts (size of HostData)
625 unsigned int num_hosts;
628 * Array of "total" peers.
630 struct PeerData *peers;
633 * Number of peers in this group.
638 * At what time should we fail the peer startup process?
640 struct GNUNET_TIME_Absolute max_timeout;
643 * How many peers are being started right now?
645 unsigned int starting;
648 * How many peers have already been started?
650 unsigned int started;
653 * Number of possible connections to peers
656 unsigned int max_outstanding_connections;
659 * Number of connects we are waiting on, allows us to rate limit
662 unsigned int outstanding_connects;
665 * Hostkeys loaded from a file.
672 struct GNUNET_CONFIGURATION_Handle *ret;
673 const struct GNUNET_CONFIGURATION_Handle *orig;
674 const char *hostname;
680 struct ConnectTopologyContext
683 * How many connections are left to create.
685 unsigned int remaining_connections;
688 * Handle to group of peers.
690 struct GNUNET_TESTING_PeerGroup *pg;
694 * Temp value set for each iteration.
696 //struct PeerData *first;
699 * Notification that all peers are connected.
701 GNUNET_TESTING_NotifyCompletion notify_connections_done;
704 * Closure for notify.
709 struct ConnectContext
712 * Peer to connect second to.
714 struct GNUNET_TESTING_Daemon *first;
717 * Peer to connect first to.
719 struct GNUNET_TESTING_Daemon *second;
722 * Higher level topology connection context.
724 struct ConnectTopologyContext *ct_ctx;
727 * How long to try this connection before timing out.
729 struct GNUNET_TIME_Relative connect_timeout;
732 * How many times to retry connecting the two peers.
734 unsigned int connect_attempts;
737 * Whether this connection has been accounted for in the schedule_connect call.
742 struct UnblacklistContext
747 struct GNUNET_TESTING_PeerGroup *pg;
750 * uid of the first peer
760 struct GNUNET_TESTING_PeerGroup *pg;
763 * uid of the first peer
768 * Peer data for first peer.
770 struct PeerData *first;
773 * Random percentage to use
778 struct MinimumContext
783 struct GNUNET_TESTING_PeerGroup *pg;
786 * uid of the first peer
791 * Peer data for first peer.
793 struct PeerData *first;
796 * Number of conns per peer
798 unsigned int num_to_add;
801 * Permuted array of all possible connections. Only add the Nth
802 * peer if it's in the Nth position.
804 unsigned int *pg_array;
807 * What number is the current element we are iterating over?
809 unsigned int current;
817 struct GNUNET_TESTING_PeerGroup *pg;
820 * uid of the first peer
825 * uid of the second peer
830 * Peer data for first peer.
832 struct PeerData *first;
835 * Which peer has been chosen as the one to add?
840 * What number is the current element we are iterating over?
842 unsigned int current;
847 * Convert unique ID to hash code.
849 * @param uid unique ID to convert
850 * @param hash set to uid (extended with zeros)
853 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
855 memset (hash, 0, sizeof (GNUNET_HashCode));
856 *((uint32_t *) hash) = uid;
860 * Convert hash code to unique ID.
862 * @param uid unique ID to convert
863 * @param hash set to uid (extended with zeros)
866 uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
868 memcpy (uid, hash, sizeof (uint32_t));
873 * Get a topology from a string input.
875 * @param topology where to write the retrieved topology
876 * @param topology_string The string to attempt to
877 * get a configuration value from
878 * @return GNUNET_YES if topology string matched a
879 * known topology, GNUNET_NO if not
882 GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
883 const char *topology_string)
886 * Strings representing topologies in enum
888 static const char *topology_strings[] = {
890 * A clique (everyone connected to everyone else).
895 * Small-world network (2d torus plus random links).
900 * Small-world network (ring plus random links).
920 * Certain percentage of peers are unable to communicate directly
921 * replicating NAT conditions
926 * Scale free topology.
931 * Straight line topology.
936 * All peers are disconnected.
941 * Read the topology from a file.
949 if (topology_string == NULL)
951 while (topology_strings[curr] != NULL)
953 if (strcasecmp (topology_strings[curr], topology_string) == 0)
960 *topology = GNUNET_TESTING_TOPOLOGY_NONE;
966 * Get connect topology option from string input.
968 * @param topology_option where to write the retrieved topology
969 * @param topology_string The string to attempt to
970 * get a configuration value from
971 * @return GNUNET_YES if string matched a known
972 * topology option, GNUNET_NO if not
975 GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption
977 const char *topology_string)
980 * Options for connecting a topology as strings.
982 static const char *topology_option_strings[] = {
984 * Try to connect all peers specified in the topology.
989 * Choose a random subset of connections to create.
991 "CONNECT_RANDOM_SUBSET",
994 * Create at least X connections for each peer.
999 * Using a depth first search, create one connection
1000 * per peer. If any are missed (graph disconnected)
1001 * start over at those peers until all have at least one
1007 * Find the N closest peers to each allowed peer in the
1008 * topology and make sure a connection to those peers
1009 * exists in the connect topology.
1014 * No options specified.
1022 if (topology_string == NULL)
1024 while (NULL != topology_option_strings[curr])
1026 if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
1028 *topology_option = curr;
1033 *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
1038 * Function to iterate over options. Copies
1039 * the options to the target configuration,
1040 * updating PORT values as needed.
1042 * @param cls closure
1043 * @param section name of the section
1044 * @param option name of the option
1045 * @param value value of the option
1048 update_config (void *cls,
1049 const char *section, const char *option, const char *value)
1051 struct UpdateContext *ctx = cls;
1055 char *single_variable;
1056 char *per_host_variable;
1057 unsigned long long num_per_host;
1059 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
1061 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
1064 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1067 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
1071 GNUNET_free (single_variable);
1074 if (0 == strcmp (option, "UNIXPATH"))
1076 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
1077 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
1079 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1082 GNUNET_snprintf (uval,
1084 "/tmp/test-service-%s-%u", section, ctx->upnum++);
1087 else if ((GNUNET_YES ==
1088 GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1090 &num_per_host)) && (num_per_host > 0))
1093 GNUNET_snprintf (uval,
1095 "/tmp/test-service-%s-%u",
1096 section, ctx->fdnum % num_per_host);
1099 GNUNET_free (single_variable);
1100 GNUNET_free (per_host_variable);
1103 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
1105 value = ctx->hostname;
1108 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
1113 * Create a new configuration using the given configuration
1114 * as a template; however, each PORT in the existing cfg
1115 * must be renumbered by incrementing "*port". If we run
1116 * out of "*port" numbers, return NULL.
1118 * @param cfg template configuration
1119 * @param port port numbers to use, update to reflect
1120 * port numbers that were used
1121 * @param upnum number to make unix domain socket names unique
1122 * @param hostname hostname of the controlling host, to allow control connections from
1123 * @param fdnum number used to offset the unix domain socket for grouped processes
1124 * (such as statistics or peerinfo, which can be shared among others)
1126 * @return new configuration, NULL on error
1128 static struct GNUNET_CONFIGURATION_Handle *
1129 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
1131 uint32_t * upnum, const char *hostname, uint32_t * fdnum)
1133 struct UpdateContext uc;
1136 char *allowed_hosts;
1142 uc.ret = GNUNET_CONFIGURATION_create ();
1143 uc.hostname = hostname;
1146 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1147 if (uc.nport >= HIGH_PORT)
1150 GNUNET_CONFIGURATION_destroy (uc.ret);
1154 if (GNUNET_CONFIGURATION_get_value_string
1155 (cfg, "testing", "control_host", &control_host) == GNUNET_OK)
1157 if (hostname != NULL)
1158 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host,
1161 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host);
1163 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM",
1165 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "UNIXPATH",
1167 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport",
1168 "ACCEPT_FROM", allowed_hosts);
1169 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "UNIXPATH",
1171 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "ACCEPT_FROM",
1173 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "UNIXPATH",
1175 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics",
1176 "ACCEPT_FROM", allowed_hosts);
1177 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "UNIXPATH",
1179 GNUNET_free_non_null (control_host);
1180 GNUNET_free (allowed_hosts);
1184 /* arm needs to know to allow connections from the host on which it is running,
1185 * otherwise gnunet-arm is unable to connect to it in some instances */
1186 if (hostname != NULL)
1188 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname);
1189 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1190 "BINDTO", hostname);
1191 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1192 "BINDTO", hostname);
1193 GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM",
1195 GNUNET_free (allowed_hosts);
1199 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1200 "BINDTO", "127.0.0.1");
1201 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1202 "BINDTO", "127.0.0.1");
1205 *port = (uint16_t) uc.nport;
1213 * Add entries to the some list
1215 * @param pg the peer group we are working with
1216 * @param first index of the first peer
1217 * @param second index of the second peer
1218 * @param list the peer list to use
1220 * @return the number of connections added (can be 0, 1 or 2)
1224 remove_connections (struct GNUNET_TESTING_PeerGroup *pg,
1225 unsigned int first, unsigned int second,
1226 enum PeerLists list)
1230 struct PeerConnection **first_list;
1231 struct PeerConnection **second_list;
1232 struct PeerConnection *first_iter;
1233 struct PeerConnection *second_iter;
1234 struct PeerConnection **first_tail;
1235 struct PeerConnection **second_tail;
1238 GNUNET_HashCode hash_first;
1239 GNUNET_HashCode hash_second;
1241 hash_from_uid (first, &hash_first);
1242 hash_from_uid (second, &hash_second);
1250 first_list = &pg->peers[first].allowed_peers_head;
1251 second_list = &pg->peers[second].allowed_peers_head;
1252 first_tail = &pg->peers[first].allowed_peers_tail;
1253 second_tail = &pg->peers[second].allowed_peers_tail;
1256 first_list = &pg->peers[first].connect_peers_head;
1257 second_list = &pg->peers[second].connect_peers_head;
1258 first_tail = &pg->peers[first].connect_peers_tail;
1259 second_tail = &pg->peers[second].connect_peers_tail;
1262 first_list = &pg->peers[first].blacklisted_peers_head;
1263 second_list = &pg->peers[second].blacklisted_peers_head;
1264 first_tail = &pg->peers[first].blacklisted_peers_tail;
1265 second_tail = &pg->peers[second].blacklisted_peers_tail;
1268 first_list = &pg->peers[first].connect_peers_working_set_head;
1269 second_list = &pg->peers[second].connect_peers_working_set_head;
1270 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1271 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1278 first_iter = *first_list;
1279 while (first_iter != NULL)
1281 if (first_iter->index == second)
1283 GNUNET_CONTAINER_DLL_remove(*first_list, *first_tail, first_iter);
1284 GNUNET_free(first_iter);
1288 first_iter = first_iter->next;
1291 second_iter = *second_list;
1292 while (second_iter != NULL)
1294 if (second_iter->index == first)
1296 GNUNET_CONTAINER_DLL_remove(*second_list, *second_tail, second_iter);
1297 GNUNET_free(second_iter);
1301 second_iter = second_iter->next;
1305 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1308 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[first].blacklisted_peers,
1313 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1316 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[second].blacklisted_peers,
1325 * Add entries to the some list
1327 * @param pg the peer group we are working with
1328 * @param first index of the first peer
1329 * @param second index of the second peer
1330 * @param list the list type that we should modify
1332 * @return the number of connections added (can be 0, 1 or 2)
1336 add_connections (struct GNUNET_TESTING_PeerGroup *pg,
1337 unsigned int first, unsigned int second,
1338 enum PeerLists list)
1344 struct PeerConnection **first_list;
1345 struct PeerConnection **second_list;
1346 struct PeerConnection *first_iter;
1347 struct PeerConnection *second_iter;
1348 struct PeerConnection *new_first;
1349 struct PeerConnection *new_second;
1350 struct PeerConnection **first_tail;
1351 struct PeerConnection **second_tail;
1353 GNUNET_HashCode hash_first;
1354 GNUNET_HashCode hash_second;
1356 hash_from_uid (first, &hash_first);
1357 hash_from_uid (second, &hash_second);
1364 first_list = &pg->peers[first].allowed_peers_head;
1365 second_list = &pg->peers[second].allowed_peers_head;
1366 first_tail = &pg->peers[first].allowed_peers_tail;
1367 second_tail = &pg->peers[second].allowed_peers_tail;
1370 first_list = &pg->peers[first].connect_peers_head;
1371 second_list = &pg->peers[second].connect_peers_head;
1372 first_tail = &pg->peers[first].connect_peers_tail;
1373 second_tail = &pg->peers[second].connect_peers_tail;
1376 first_list = &pg->peers[first].blacklisted_peers_head;
1377 second_list = &pg->peers[second].blacklisted_peers_head;
1378 first_tail = &pg->peers[first].blacklisted_peers_tail;
1379 second_tail = &pg->peers[second].blacklisted_peers_tail;
1382 first_list = &pg->peers[first].connect_peers_working_set_head;
1383 second_list = &pg->peers[second].connect_peers_working_set_head;
1384 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1385 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1392 add_first = GNUNET_YES;
1393 add_second = GNUNET_YES;
1395 first_iter = *first_list;
1396 while (first_iter != NULL)
1398 if (first_iter->index == second)
1400 add_first = GNUNET_NO;
1403 first_iter = first_iter->next;
1406 second_iter = *second_list;
1407 while (second_iter != NULL)
1409 if (second_iter->index == first)
1411 add_second = GNUNET_NO;
1414 second_iter = second_iter->next;
1418 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1421 add_first = GNUNET_YES;
1425 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1428 add_second = GNUNET_YES;
1436 new_first = GNUNET_malloc (sizeof (struct PeerConnection));
1437 new_first->index = second;
1438 GNUNET_CONTAINER_DLL_insert(*first_list, *first_tail, new_first);
1440 new_first->next = *first_list;
1441 *first_list = new_first;*/
1443 GNUNET_assert (GNUNET_OK ==
1444 GNUNET_CONTAINER_multihashmap_put (pg->
1446 [first].blacklisted_peers,
1449 peers[second].daemon,
1450 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1452 pg->peers[first].num_connections++;
1459 new_second = GNUNET_malloc (sizeof (struct PeerConnection));
1460 new_second->index = first;
1461 GNUNET_CONTAINER_DLL_insert(*second_list, *second_tail, new_second);
1463 new_second->next = *second_list;
1464 *second_list = new_second;
1467 GNUNET_assert (GNUNET_OK ==
1468 GNUNET_CONTAINER_multihashmap_put (pg->
1470 [second].blacklisted_peers,
1473 peers[first].daemon,
1474 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1476 pg->peers[second].num_connections++;
1485 * Scale free network construction as described in:
1487 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
1489 * Start with a network of "one" peer, then progressively add
1490 * peers up to the total number. At each step, iterate over
1491 * all possible peers and connect new peer based on number of
1492 * existing connections of the target peer.
1494 * @param pg the peer group we are dealing with
1495 * @param proc the connection processor to use
1496 * @param list the peer list to use
1498 * @return the number of connections created
1501 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg,
1502 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1505 unsigned int total_connections;
1506 unsigned int outer_count;
1508 unsigned int previous_total_connections;
1512 GNUNET_assert (pg->total > 1);
1514 /* Add a connection between the first two nodes */
1515 total_connections = proc (pg, 0, 1, list);
1517 for (outer_count = 1; outer_count < pg->total; outer_count++)
1519 previous_total_connections = total_connections;
1520 for (i = 0; i < outer_count; i++)
1523 pg->peers[i].num_connections /
1524 (double) previous_total_connections;
1527 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1528 UINT64_MAX)) / ((double) UINT64_MAX);
1530 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1531 "Considering connecting peer %d to peer %d\n",
1534 if (random < probability)
1537 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1538 "Connecting peer %d to peer %d\n", outer_count, i);
1540 total_connections += proc (pg, outer_count, i, list);
1545 return total_connections;
1549 * Create a topology given a peer group (set of running peers)
1550 * and a connection processor. Creates a small world topology
1551 * according to the rewired ring construction. The basic
1552 * behavior is that a ring topology is created, but with some
1553 * probability instead of connecting a peer to the next
1554 * neighbor in the ring a connection will be created to a peer
1555 * selected uniformly at random. We use the TESTING
1556 * PERCENTAGE option to specify what number of
1557 * connections each peer should have. Default is 2,
1558 * which makes the ring, any given number is multiplied by
1559 * the log of the network size; i.e. a PERCENTAGE of 2 makes
1560 * each peer have on average 2logn connections. The additional
1561 * connections are made at increasing distance around the ring
1562 * from the original peer, or to random peers based on the re-
1563 * wiring probability. The TESTING
1564 * PROBABILITY option is used as the probability that a given
1565 * connection is rewired.
1567 * @param pg the peergroup to create the topology on
1568 * @param proc the connection processor to call to actually set
1569 * up connections between two peers
1570 * @param list the peer list to use
1572 * @return the number of connections that were set up
1576 create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
1577 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1581 unsigned int natLog;
1582 unsigned int randomPeer;
1583 double random, logNModifier, probability;
1584 unsigned int smallWorldConnections;
1589 unsigned int useAnd;
1590 int connect_attempts;
1592 logNModifier = 0.5; /* FIXME: default value? */
1593 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1598 if (sscanf (p_string, "%lf", &logNModifier) != 1)
1599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1601 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1602 p_string, "LOGNMODIFIER", "TESTING");
1603 GNUNET_free (p_string);
1605 probability = 0.5; /* FIXME: default percentage? */
1606 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1611 if (sscanf (p_string, "%lf", &probability) != 1)
1612 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1614 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1615 p_string, "PERCENTAGE", "TESTING");
1616 GNUNET_free (p_string);
1618 natLog = log (pg->total);
1619 connsPerPeer = ceil (natLog * logNModifier);
1621 if (connsPerPeer % 2 == 1)
1624 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."), connsPerPeer);
1626 smallWorldConnections = 0;
1627 connect_attempts = 0;
1628 for (i = 0; i < pg->total; i++)
1631 max = i + connsPerPeer / 2;
1632 min = i - connsPerPeer / 2;
1634 if (max > pg->total - 1)
1636 max = max - pg->total;
1642 min = pg->total - 1 + min;
1646 for (j = 0; j < connsPerPeer / 2; j++)
1650 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1651 UINT64_MAX) / ((double) UINT64_MAX));
1652 if (random < probability)
1654 /* Connect to uniformly selected random peer */
1656 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1658 while ((((randomPeer < max) && (randomPeer > min))
1659 && (useAnd == 0)) || (((randomPeer > min)
1660 || (randomPeer < max))
1664 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1667 smallWorldConnections += proc (pg, i, randomPeer, list);
1671 nodeToConnect = i + j + 1;
1672 if (nodeToConnect > pg->total - 1)
1674 nodeToConnect = nodeToConnect - pg->total;
1676 connect_attempts += proc (pg, i, nodeToConnect, list);
1682 connect_attempts += smallWorldConnections;
1684 return connect_attempts;
1688 * Create a topology given a peer group (set of running peers)
1689 * and a connection processor.
1691 * @param pg the peergroup to create the topology on
1692 * @param proc the connection processor to call to actually set
1693 * up connections between two peers
1694 * @param list the peer list to use
1696 * @return the number of connections that were set up
1700 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg,
1701 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1703 unsigned int outer_count, inner_count;
1704 unsigned int cutoff;
1705 int connect_attempts;
1706 double nat_percentage;
1709 nat_percentage = 0.6; /* FIXME: default percentage? */
1710 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1715 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1716 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1718 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1719 p_string, "PERCENTAGE", "TESTING");
1720 GNUNET_free (p_string);
1725 cutoff = (unsigned int) (nat_percentage * pg->total);
1727 connect_attempts = 0;
1729 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1731 for (inner_count = outer_count + 1; inner_count < pg->total;
1734 if ((outer_count > cutoff) || (inner_count > cutoff))
1737 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1738 "Connecting peer %d to peer %d\n",
1739 outer_count, inner_count);
1741 connect_attempts += proc (pg, outer_count, inner_count, list);
1746 return connect_attempts;
1751 * Create a topology given a peer group (set of running peers)
1752 * and a connection processor.
1754 * @param pg the peergroup to create the topology on
1755 * @param proc the connection processor to call to actually set
1756 * up connections between two peers
1757 * @param list the peer list to use
1759 * @return the number of connections that were set up
1763 create_small_world (struct GNUNET_TESTING_PeerGroup *pg,
1764 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1766 unsigned int i, j, k;
1767 unsigned int square;
1770 unsigned int toggle = 1;
1771 unsigned int nodeToConnect;
1772 unsigned int natLog;
1773 unsigned int node1Row;
1774 unsigned int node1Col;
1775 unsigned int node2Row;
1776 unsigned int node2Col;
1777 unsigned int distance;
1778 double probability, random, percentage;
1779 unsigned int smallWorldConnections;
1780 unsigned int small_world_it;
1782 int connect_attempts;
1783 square = floor (sqrt (pg->total));
1787 percentage = 0.5; /* FIXME: default percentage? */
1788 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1793 if (sscanf (p_string, "%lf", &percentage) != 1)
1794 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1796 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1797 p_string, "PERCENTAGE", "TESTING");
1798 GNUNET_free (p_string);
1800 if (percentage < 0.0)
1802 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1804 ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"),
1805 "PERCENTAGE", "TESTING", percentage);
1808 probability = 0.5; /* FIXME: default percentage? */
1809 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1814 if (sscanf (p_string, "%lf", &probability) != 1)
1815 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1817 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1818 p_string, "PROBABILITY", "TESTING");
1819 GNUNET_free (p_string);
1821 if (square * square != pg->total)
1823 while (rows * cols < pg->total)
1825 if (toggle % 2 == 0)
1834 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1836 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1840 connect_attempts = 0;
1841 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1842 * to the node to its right and above. Once this is over, we'll have our torus!
1843 * Special case for the last node (if the rows and columns are not equal), connect
1844 * to the first in the row to maintain topology.
1846 for (i = 0; i < pg->total; i++)
1848 /* First connect to the node to the right */
1849 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1850 nodeToConnect = i + 1;
1851 else if (i + 1 == pg->total)
1852 nodeToConnect = rows * cols - cols;
1854 nodeToConnect = i - cols + 1;
1856 connect_attempts += proc (pg, i, nodeToConnect, list);
1860 nodeToConnect = (rows * cols) - cols + i;
1861 if (nodeToConnect >= pg->total)
1862 nodeToConnect -= cols;
1865 nodeToConnect = i - cols;
1867 if (nodeToConnect < pg->total)
1868 connect_attempts += proc (pg, i, nodeToConnect, list);
1870 natLog = log (pg->total);
1871 #if VERBOSE_TESTING > 2
1872 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1873 _("natural log of %d is %d, will run %d iterations\n"),
1874 pg->total, natLog, (int) (natLog * percentage));
1875 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1876 _("Total connections added thus far: %u!\n"), connect_attempts);
1878 smallWorldConnections = 0;
1879 small_world_it = (unsigned int) (natLog * percentage);
1880 if (small_world_it < 1)
1882 GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1);
1883 for (i = 0; i < small_world_it; i++)
1885 for (j = 0; j < pg->total; j++)
1887 /* Determine the row and column of node at position j on the 2d torus */
1888 node1Row = j / cols;
1889 node1Col = j - (node1Row * cols);
1890 for (k = 0; k < pg->total; k++)
1892 /* Determine the row and column of node at position k on the 2d torus */
1893 node2Row = k / cols;
1894 node2Col = k - (node2Row * cols);
1895 /* Simple Cartesian distance */
1897 abs (node1Row - node2Row) + abs (node1Col - node2Col);
1900 /* Calculate probability as 1 over the square of the distance */
1901 probability = 1.0 / (distance * distance);
1902 /* Choose a random value between 0 and 1 */
1905 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1907 ((double) UINT64_MAX);
1908 /* If random < probability, then connect the two nodes */
1909 if (random < probability)
1910 smallWorldConnections += proc (pg, j, k, list);
1916 connect_attempts += smallWorldConnections;
1917 #if VERBOSE_TESTING > 2
1918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1919 _("Total connections added for small world: %d!\n"),
1920 smallWorldConnections);
1922 return connect_attempts;
1926 * Create a topology given a peer group (set of running peers)
1927 * and a connection processor.
1929 * @param pg the peergroup to create the topology on
1930 * @param proc the connection processor to call to actually set
1931 * up connections between two peers
1932 * @param list the peer list to use
1934 * @return the number of connections that were set up
1938 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg,
1939 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1942 unsigned int outer_count;
1943 unsigned int inner_count;
1944 int connect_attempts;
1948 probability = 0.5; /* FIXME: default percentage? */
1949 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1954 if (sscanf (p_string, "%lf", &probability) != 1)
1955 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1957 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1958 p_string, "PROBABILITY", "TESTING");
1959 GNUNET_free (p_string);
1961 connect_attempts = 0;
1962 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1964 for (inner_count = outer_count + 1; inner_count < pg->total;
1969 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1970 UINT64_MAX)) / ((double) UINT64_MAX);
1972 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1973 _("rand is %f probability is %f\n"), temp_rand,
1976 if (temp_rand < probability)
1978 connect_attempts += proc (pg, outer_count, inner_count, list);
1983 return connect_attempts;
1987 * Create a topology given a peer group (set of running peers)
1988 * and a connection processor. This particular function creates
1989 * the connections for a 2d-torus, plus additional "closest"
1990 * connections per peer.
1992 * @param pg the peergroup to create the topology on
1993 * @param proc the connection processor to call to actually set
1994 * up connections between two peers
1995 * @param list the peer list to use
1997 * @return the number of connections that were set up
2001 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg,
2002 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2005 unsigned int square;
2008 unsigned int toggle = 1;
2009 unsigned int nodeToConnect;
2010 int connect_attempts;
2012 connect_attempts = 0;
2014 square = floor (sqrt (pg->total));
2018 if (square * square != pg->total)
2020 while (rows * cols < pg->total)
2022 if (toggle % 2 == 0)
2031 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2033 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2036 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2037 * to the node to its right and above. Once this is over, we'll have our torus!
2038 * Special case for the last node (if the rows and columns are not equal), connect
2039 * to the first in the row to maintain topology.
2041 for (i = 0; i < pg->total; i++)
2043 /* First connect to the node to the right */
2044 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2045 nodeToConnect = i + 1;
2046 else if (i + 1 == pg->total)
2047 nodeToConnect = rows * cols - cols;
2049 nodeToConnect = i - cols + 1;
2051 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2052 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2054 connect_attempts += proc (pg, i, nodeToConnect, list);
2056 /* Second connect to the node immediately above */
2059 nodeToConnect = (rows * cols) - cols + i;
2060 if (nodeToConnect >= pg->total)
2061 nodeToConnect -= cols;
2064 nodeToConnect = i - cols;
2066 if (nodeToConnect < pg->total)
2069 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2070 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2072 connect_attempts += proc (pg, i, nodeToConnect, list);
2077 return connect_attempts;
2082 * Create a topology given a peer group (set of running peers)
2083 * and a connection processor.
2085 * @param pg the peergroup to create the topology on
2086 * @param proc the connection processor to call to actually set
2087 * up connections between two peers
2088 * @param list the peer list to use
2090 * @return the number of connections that were set up
2094 create_clique (struct GNUNET_TESTING_PeerGroup *pg,
2095 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2097 unsigned int outer_count;
2098 unsigned int inner_count;
2099 int connect_attempts;
2101 connect_attempts = 0;
2103 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2105 for (inner_count = outer_count + 1; inner_count < pg->total;
2109 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2110 "Connecting peer %d to peer %d\n",
2111 outer_count, inner_count);
2113 connect_attempts += proc (pg, outer_count, inner_count, list);
2117 return connect_attempts;
2122 * Iterator over hash map entries.
2124 * @param cls closure the peer group
2125 * @param key the key stored in the hashmap is the
2126 * index of the peer to connect to
2127 * @param value value in the hash map, handle to the peer daemon
2128 * @return GNUNET_YES if we should continue to
2133 unblacklist_iterator (void *cls,
2134 const GNUNET_HashCode * key,
2137 struct UnblacklistContext *un_ctx = cls;
2138 uint32_t second_pos;
2140 uid_from_hash (key, &second_pos);
2142 unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
2149 * Create a blacklist topology based on the allowed topology
2150 * which disallows any connections not in the allowed topology
2151 * at the transport level.
2153 * @param pg the peergroup to create the topology on
2154 * @param proc the connection processor to call to allow
2155 * up connections between two peers
2157 * @return the number of connections that were set up
2161 copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
2162 GNUNET_TESTING_ConnectionProcessor proc)
2164 struct UnblacklistContext un_ctx;
2167 struct PeerConnection *iter;
2171 for (count = 0; count < pg->total - 1; count++)
2173 un_ctx.first_uid = count;
2175 iter = pg->peers[count].allowed_peers_head;
2176 while (iter != NULL)
2178 remove_connections(pg, count, iter->index, BLACKLIST);
2179 //unblacklist_connections(pg, count, iter->index);
2183 total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, &unblacklist_iterator, &un_ctx);
2186 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total);
2191 * Create a topology given a peer group (set of running peers)
2192 * and a connection processor.
2194 * @param pg the peergroup to create the topology on
2195 * @param proc the connection processor to call to actually set
2196 * up connections between two peers
2197 * @param list which list should be modified
2199 * @return the number of connections that were set up
2203 create_line (struct GNUNET_TESTING_PeerGroup *pg,
2204 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2207 int connect_attempts;
2209 connect_attempts = 0;
2211 /* Connect each peer to the next highest numbered peer */
2212 for (count = 0; count < pg->total - 1; count++)
2215 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2216 "Connecting peer %d to peer %d\n", count, count + 1);
2218 connect_attempts += proc (pg, count, count + 1, list);
2221 return connect_attempts;
2225 * Create a topology given a peer group (set of running peers)
2226 * and a connection processor.
2228 * @param pg the peergroup to create the topology on
2229 * @param filename the file to read topology information from
2230 * @param proc the connection processor to call to actually set
2231 * up connections between two peers
2232 * @param list the peer list to use
2234 * @return the number of connections that were set up
2238 create_from_file (struct GNUNET_TESTING_PeerGroup *pg,
2240 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2242 int connect_attempts;
2243 unsigned int first_peer_index;
2244 unsigned int second_peer_index;
2245 connect_attempts = 0;
2250 unsigned int total_peers;
2252 enum States curr_state;
2254 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
2255 GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ);
2257 if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
2259 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2260 "Could not open file `%s' specified for topology!", filename);
2261 return connect_attempts;
2264 data = GNUNET_malloc_large (frstat.st_size);
2265 GNUNET_assert(data != NULL);
2266 if (frstat.st_size !=
2267 GNUNET_DISK_fn_read (filename, data, frstat.st_size))
2269 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2270 "Could not read file %s specified for host list, ending test!", filename);
2272 return connect_attempts;
2277 /* First line should contain a single integer, specifying the number of peers */
2278 /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
2279 curr_state = NUM_PEERS;
2280 while (count < frstat.st_size - 1)
2282 if ((buf[count] == '\n') || (buf[count] == ' '))
2291 if (1 != sscanf(&buf[count], "%u", &total_peers))
2293 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read number of peers from topology file!\n");
2294 GNUNET_free_non_null(data);
2295 return connect_attempts;
2297 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read %u total peers in topology\n", total_peers);
2298 GNUNET_assert(total_peers == pg->total);
2299 curr_state = PEER_INDEX;
2300 while((buf[count] != '\n') && (count < frstat.st_size - 1))
2305 if (1 != sscanf(&buf[count], "%u", &first_peer_index))
2307 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read peer index from topology file!\n");
2308 GNUNET_free_non_null(data);
2309 return connect_attempts;
2311 while((buf[count] != ':') && (count < frstat.st_size - 1))
2314 curr_state = OTHER_PEER_INDEX;
2317 if (1 == sscanf(&buf[count], ":"))
2318 curr_state = OTHER_PEER_INDEX;
2321 case OTHER_PEER_INDEX:
2322 if (1 != sscanf(&buf[count], "%u", &second_peer_index))
2324 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to peer index from topology file!\n");
2325 GNUNET_free_non_null(data);
2326 return connect_attempts;
2328 /* Assume file is written with first peer 1, but array index is 0 */
2329 connect_attempts += proc (pg, first_peer_index - 1, second_peer_index - 1, list);
2330 while((buf[count] != '\n') && (buf[count] != ',') && (count < frstat.st_size - 1))
2332 if (buf[count] == '\n')
2334 curr_state = PEER_INDEX;
2336 else if (buf[count] != ',')
2338 curr_state = OTHER_PEER_INDEX;
2343 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Found bad data in topology file while in state %d!\n", curr_state);
2345 return connect_attempts;
2350 /* Connect each peer to the next highest numbered peer */
2351 for (count = 0; count < pg->total - 1; count++)
2354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2355 "Connecting peer %d to peer %d\n", first_peer_index, second_peer_index);
2357 connect_attempts += proc (pg, first_peer_index, second_peer_index);
2360 return connect_attempts;
2364 * Create a topology given a peer group (set of running peers)
2365 * and a connection processor.
2367 * @param pg the peergroup to create the topology on
2368 * @param proc the connection processor to call to actually set
2369 * up connections between two peers
2370 * @param list the peer list to use
2372 * @return the number of connections that were set up
2376 create_ring (struct GNUNET_TESTING_PeerGroup *pg,
2377 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2380 int connect_attempts;
2382 connect_attempts = 0;
2384 /* Connect each peer to the next highest numbered peer */
2385 for (count = 0; count < pg->total - 1; count++)
2388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2389 "Connecting peer %d to peer %d\n", count, count + 1);
2391 connect_attempts += proc (pg, count, count + 1, list);
2394 /* Connect the last peer to the first peer */
2395 connect_attempts += proc (pg, pg->total - 1, 0, list);
2397 return connect_attempts;
2402 * Iterator for writing friends of a peer to a file.
2404 * @param cls closure, an open writable file handle
2405 * @param key the key the daemon was stored under
2406 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2408 * @return GNUNET_YES to continue iteration
2410 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
2411 * with a single file_iterator that takes a closure which contains
2412 * the prefix to write before the peer. Then this could be used
2413 * for blacklisting multiple transports and writing the friend
2414 * file. I'm sure *someone* will complain loudly about other
2415 * things that negate these functions even existing so no point in
2419 friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2421 FILE *temp_friend_handle = cls;
2422 struct GNUNET_TESTING_Daemon *peer = value;
2423 struct GNUNET_PeerIdentity *temppeer;
2424 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2426 temppeer = &peer->id;
2427 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2428 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2433 struct BlacklistContext
2436 * The (open) file handle to write to
2438 FILE *temp_file_handle;
2441 * The transport that this peer will be blacklisted on.
2447 * Iterator for writing blacklist data to appropriate files.
2449 * @param cls closure, an open writable file handle
2450 * @param key the key the daemon was stored under
2451 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2453 * @return GNUNET_YES to continue iteration
2456 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2458 struct BlacklistContext *blacklist_ctx = cls;
2459 struct GNUNET_TESTING_Daemon *peer = value;
2460 struct GNUNET_PeerIdentity *temppeer;
2461 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2463 temppeer = &peer->id;
2464 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2465 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
2466 fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
2467 blacklist_ctx->transport, (char *) &peer_enc);
2475 * Create the friend files based on the PeerConnection's
2476 * of each peer in the peer group, and copy the files
2477 * to the appropriate place
2479 * @param pg the peer group we are dealing with
2482 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
2484 FILE *temp_friend_handle;
2485 unsigned int pg_iter;
2486 char *temp_service_path;
2487 struct GNUNET_OS_Process **procarr;
2490 enum GNUNET_OS_ProcessStatusType type;
2491 unsigned long return_code;
2496 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2497 struct PeerConnection *conn_iter;
2499 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2500 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2502 mytemp = GNUNET_DISK_mktemp ("friends");
2503 GNUNET_assert (mytemp != NULL);
2504 temp_friend_handle = fopen (mytemp, "wt");
2505 GNUNET_assert (temp_friend_handle != NULL);
2507 conn_iter = pg->peers[pg_iter].allowed_peers_head;
2508 while (conn_iter != NULL)
2510 GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon->id.hashPubKey, &peer_enc);
2511 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2512 conn_iter = conn_iter->next;
2515 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers,
2516 &friend_file_iterator,
2517 temp_friend_handle);
2519 fclose (temp_friend_handle);
2522 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2523 daemon->cfg, "PATHS",
2525 &temp_service_path))
2527 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2529 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2530 "SERVICEHOME", "PATHS");
2531 if (UNLINK (mytemp) != 0)
2532 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2534 GNUNET_free (mytemp);
2538 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2540 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
2541 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2545 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2546 _("Copying file with command cp %s %s\n"), mytemp, arg);
2551 else /* Remote, scp the file to the correct place */
2553 if (NULL != pg->peers[pg_iter].daemon->username)
2554 GNUNET_asprintf (&arg, "%s@%s:%s/friends",
2555 pg->peers[pg_iter].daemon->username,
2556 pg->peers[pg_iter].daemon->hostname,
2559 GNUNET_asprintf (&arg, "%s:%s/friends",
2560 pg->peers[pg_iter].daemon->hostname,
2563 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568 _("Copying file with command scp %s %s\n"), mytemp,
2573 GNUNET_free (temp_service_path);
2574 GNUNET_free (mytemp);
2578 ret = GNUNET_SYSERR;
2579 while ((count < max_wait) && (ret != GNUNET_OK))
2582 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2585 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2586 _("Checking copy status of file %d\n"), pg_iter);
2588 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2590 if (GNUNET_OS_process_status
2591 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2593 ret = GNUNET_SYSERR;
2595 else if ((type != GNUNET_OS_PROCESS_EXITED)
2596 || (return_code != 0))
2598 ret = GNUNET_SYSERR;
2602 GNUNET_OS_process_close (procarr[pg_iter]);
2603 procarr[pg_iter] = NULL;
2605 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2606 _("File %d copied\n"), pg_iter);
2612 if (ret == GNUNET_SYSERR)
2614 /* FIXME: why sleep here? -CG */
2620 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2621 _("Finished copying all friend files!\n"));
2623 GNUNET_free (procarr);
2629 * Create the blacklist files based on the PeerConnection's
2630 * of each peer in the peer group, and copy the files
2631 * to the appropriate place.
2633 * @param pg the peer group we are dealing with
2634 * @param transports space delimited list of transports to blacklist
2637 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
2638 const char *transports)
2640 FILE *temp_file_handle;
2641 unsigned int pg_iter;
2642 char *temp_service_path;
2643 struct GNUNET_OS_Process **procarr;
2646 enum GNUNET_OS_ProcessStatusType type;
2647 unsigned long return_code;
2654 char *temp_transports;
2657 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2658 struct PeerConnection *conn_iter;
2660 static struct BlacklistContext blacklist_ctx;
2663 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2664 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2666 mytemp = GNUNET_DISK_mktemp ("blacklist");
2667 GNUNET_assert (mytemp != NULL);
2668 temp_file_handle = fopen (mytemp, "wt");
2669 GNUNET_assert (temp_file_handle != NULL);
2670 temp_transports = GNUNET_strdup (transports);
2672 blacklist_ctx.temp_file_handle = temp_file_handle;
2674 transport_len = strlen (temp_transports) + 1;
2677 for (i = 0; i < transport_len; i++)
2679 if ((temp_transports[i] == ' ') && (pos == NULL))
2680 continue; /* At start of string (whitespace) */
2681 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
2683 temp_transports[i] = '\0';
2685 conn_iter = pg->peers[pg_iter].blacklisted_peers_head;
2686 while (conn_iter != NULL)
2688 GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon->id.hashPubKey, &peer_enc);
2689 fprintf (temp_file_handle, "%s:%s\n", pos, (char *) &peer_enc);
2690 conn_iter = conn_iter->next;
2694 blacklist_ctx.transport = pos;
2695 entry_count = GNUNET_CONTAINER_multihashmap_iterate (pg->
2697 [pg_iter].blacklisted_peers,
2698 &blacklist_file_iterator,
2702 } /* At beginning of actual string */
2703 else if (pos == NULL)
2705 pos = &temp_transports[i];
2709 GNUNET_free (temp_transports);
2710 fclose (temp_file_handle);
2713 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2714 daemon->cfg, "PATHS",
2716 &temp_service_path))
2718 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2720 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2721 "SERVICEHOME", "PATHS");
2722 if (UNLINK (mytemp) != 0)
2723 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2725 GNUNET_free (mytemp);
2729 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2731 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
2732 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2736 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2737 _("Copying file with command cp %s %s\n"), mytemp, arg);
2742 else /* Remote, scp the file to the correct place */
2744 if (NULL != pg->peers[pg_iter].daemon->username)
2745 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist",
2746 pg->peers[pg_iter].daemon->username,
2747 pg->peers[pg_iter].daemon->hostname,
2750 GNUNET_asprintf (&arg, "%s:%s/blacklist",
2751 pg->peers[pg_iter].daemon->hostname,
2754 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2759 _("Copying file with command scp %s %s\n"), mytemp,
2764 GNUNET_free (temp_service_path);
2765 GNUNET_free (mytemp);
2769 ret = GNUNET_SYSERR;
2770 while ((count < max_wait) && (ret != GNUNET_OK))
2773 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2776 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2777 _("Checking copy status of file %d\n"), pg_iter);
2779 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2781 if (GNUNET_OS_process_status
2782 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2784 ret = GNUNET_SYSERR;
2786 else if ((type != GNUNET_OS_PROCESS_EXITED)
2787 || (return_code != 0))
2789 ret = GNUNET_SYSERR;
2793 GNUNET_OS_process_close (procarr[pg_iter]);
2794 procarr[pg_iter] = NULL;
2796 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2797 _("File %d copied\n"), pg_iter);
2803 if (ret == GNUNET_SYSERR)
2805 /* FIXME: why sleep here? -CG */
2811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2812 _("Finished copying all blacklist files!\n"));
2814 GNUNET_free (procarr);
2820 * Internal notification of a connection, kept so that we can ensure some connections
2821 * happen instead of flooding all testing daemons with requests to connect.
2824 internal_connect_notify (void *cls,
2825 const struct GNUNET_PeerIdentity *first,
2826 const struct GNUNET_PeerIdentity *second,
2828 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
2829 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
2830 struct GNUNET_TESTING_Daemon *first_daemon,
2831 struct GNUNET_TESTING_Daemon *second_daemon,
2834 struct ConnectTopologyContext *ct_ctx = cls;
2835 struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
2836 pg->outstanding_connects--;
2837 ct_ctx->remaining_connections--;
2838 if (ct_ctx->remaining_connections == 0)
2840 if (ct_ctx->notify_connections_done != NULL)
2841 ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL);
2842 GNUNET_free (ct_ctx);
2845 if (pg->notify_connection != NULL)
2846 pg->notify_connection (pg->notify_connection_cls, first, second, distance,
2847 first_cfg, second_cfg, first_daemon, second_daemon,
2853 * Either delay a connection (because there are too many outstanding)
2854 * or schedule it for right now.
2856 * @param cls a connection context
2857 * @param tc the task runtime context
2860 schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2862 struct ConnectContext *connect_context = cls;
2863 struct GNUNET_TESTING_PeerGroup *pg = connect_context->ct_ctx->pg;
2865 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
2868 if (pg->outstanding_connects > pg->max_outstanding_connections)
2870 #if VERBOSE_TESTING > 2
2871 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2873 ("Delaying connect, we have too many outstanding connections!\n"));
2875 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
2876 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
2877 &schedule_connect, connect_context);
2881 #if VERBOSE_TESTING > 2
2882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2883 _("Creating connection, outstanding_connections is %d\n"),
2884 outstanding_connects);
2886 pg->outstanding_connects++;
2887 GNUNET_TESTING_daemons_connect (connect_context->first,
2888 connect_context->second,
2889 connect_context->connect_timeout,
2890 connect_context->connect_attempts,
2891 &internal_connect_notify,
2892 connect_context->ct_ctx);
2893 GNUNET_free (connect_context);
2899 * Iterator for actually scheduling connections to be created
2900 * between two peers.
2902 * @param cls closure, a GNUNET_TESTING_Daemon
2903 * @param key the key the second Daemon was stored under
2904 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2906 * @return GNUNET_YES to continue iteration
2909 connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2911 struct ConnectTopologyContext *ct_ctx = cls;
2912 struct PeerData *first = ct_ctx->first;
2913 struct GNUNET_TESTING_Daemon *second = value;
2914 struct ConnectContext *connect_context;
2916 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
2917 connect_context->first = first->daemon;
2918 connect_context->second = second;
2919 connect_context->ct_ctx = ct_ctx;
2920 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
2928 * Iterator for copying all entries in the allowed hashmap to the
2931 * @param cls closure, a GNUNET_TESTING_Daemon
2932 * @param key the key the second Daemon was stored under
2933 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2935 * @return GNUNET_YES to continue iteration
2938 copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2940 struct PeerData *first = cls;
2942 GNUNET_assert (GNUNET_OK ==
2943 GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key,
2945 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2952 * Make the peers to connect the same as those that are allowed to be
2955 * @param pg the peer group
2958 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
2960 unsigned int pg_iter;
2964 struct PeerConnection *iter;
2968 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2971 iter = pg->peers[pg_iter].allowed_peers_head;
2972 while (iter != NULL)
2974 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating connection between %d and %d\n", pg_iter, iter->index);
2975 total += add_connections(pg, pg_iter, iter->index, CONNECT);
2976 //total += add_actual_connections(pg, pg_iter, iter->index);
2981 GNUNET_CONTAINER_multihashmap_iterate (pg->
2982 peers[pg_iter].allowed_peers,
2983 ©_topology_iterator,
2984 &pg->peers[pg_iter]);
2986 if (GNUNET_SYSERR == ret)
2987 return GNUNET_SYSERR;
2989 total = total + ret;
2997 * Connect the topology as specified by the PeerConnection's
2998 * of each peer in the peer group
3000 * @param pg the peer group we are dealing with
3001 * @param connect_timeout how long try connecting two peers
3002 * @param connect_attempts how many times (max) to attempt
3003 * @param notify_callback callback to notify when finished
3004 * @param notify_cls closure for notify callback
3006 * @return the number of connections that will be attempted
3009 connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
3010 struct GNUNET_TIME_Relative connect_timeout,
3011 unsigned int connect_attempts,
3012 GNUNET_TESTING_NotifyCompletion notify_callback,
3015 unsigned int pg_iter;
3017 struct ConnectTopologyContext *ct_ctx;
3019 struct PeerConnection *connection_iter;
3020 struct ConnectContext *connect_context;
3026 ct_ctx = GNUNET_malloc (sizeof (struct ConnectTopologyContext));
3027 ct_ctx->notify_connections_done = notify_callback;
3028 ct_ctx->notify_cls = notify_cls;
3031 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3034 connection_iter = pg->peers[pg_iter].connect_peers_head;
3035 while (connection_iter != NULL)
3037 connection_iter = connection_iter->next;
3042 GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
3048 GNUNET_free (ct_ctx);
3051 ct_ctx->remaining_connections = total;
3054 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3057 connection_iter = pg->peers[pg_iter].connect_peers_head;
3058 while (connection_iter != NULL)
3060 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Scheduling connect of peer %d to peer %d\n", pg_iter, connection_iter->index);
3061 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3062 connect_context->first = pg->peers[pg_iter].daemon;
3063 connect_context->second = pg->peers[connection_iter->index].daemon;
3064 connect_context->ct_ctx = ct_ctx;
3065 connect_context->connect_timeout = connect_timeout;
3066 connect_context->connect_attempts = connect_attempts;
3067 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3068 connection_iter = connection_iter->next;
3073 GNUNET_CONTAINER_multihashmap_iterate (pg->
3074 peers[pg_iter].connect_peers,
3075 &connect_iterator, ct_ctx);
3076 GNUNET_assert (GNUNET_SYSERR != ret && ret >= 0);
3077 total = total + ret;
3085 * Takes a peer group and creates a topology based on the
3086 * one specified. Creates a topology means generates friend
3087 * files for the peers so they can only connect to those allowed
3088 * by the topology. This will only have an effect once peers
3089 * are started if the FRIENDS_ONLY option is set in the base
3090 * config. Also takes an optional restrict topology which
3091 * disallows connections based on particular transports
3092 * UNLESS they are specified in the restricted topology.
3094 * @param pg the peer group struct representing the running peers
3095 * @param topology which topology to connect the peers in
3096 * @param restrict_topology disallow restrict_transports transport
3097 * connections to peers NOT in this topology
3098 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
3099 * @param restrict_transports space delimited list of transports to blacklist
3100 * to create restricted topology
3102 * @return the maximum number of connections were all allowed peers
3103 * connected to each other
3106 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
3107 enum GNUNET_TESTING_Topology topology,
3108 enum GNUNET_TESTING_Topology
3110 const char *restrict_transports)
3114 unsigned int num_connections;
3115 int unblacklisted_connections;
3120 for (i = 0; i < pg->total; i++)
3122 pg->peers[i].allowed_peers =
3123 GNUNET_CONTAINER_multihashmap_create (100);
3124 pg->peers[i].connect_peers =
3125 GNUNET_CONTAINER_multihashmap_create (100);
3126 pg->peers[i].blacklisted_peers =
3127 GNUNET_CONTAINER_multihashmap_create (100);
3128 pg->peers[i].pg = pg;
3134 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3136 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n"));
3138 num_connections = create_clique (pg, &add_connections, ALLOWED);
3140 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3142 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3143 _("Creating small world (ring) topology\n"));
3146 create_small_world_ring (pg, &add_connections, ALLOWED);
3148 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3150 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3151 _("Creating small world (2d-torus) topology\n"));
3153 num_connections = create_small_world (pg, &add_connections, ALLOWED);
3155 case GNUNET_TESTING_TOPOLOGY_RING:
3157 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n"));
3159 num_connections = create_ring (pg, &add_connections, ALLOWED);
3161 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3163 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n"));
3165 num_connections = create_2d_torus (pg, &add_connections, ALLOWED);
3167 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3169 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3170 _("Creating Erdos-Renyi topology\n"));
3172 num_connections = create_erdos_renyi (pg, &add_connections, ALLOWED);
3174 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3176 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n"));
3178 num_connections = create_nated_internet (pg, &add_connections, ALLOWED);
3180 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3183 _("Creating Scale Free topology\n"));
3185 num_connections = create_scale_free (pg, &add_connections, ALLOWED);
3187 case GNUNET_TESTING_TOPOLOGY_LINE:
3189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3190 _("Creating straight line topology\n"));
3192 num_connections = create_line (pg, &add_connections, ALLOWED);
3194 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3197 _("Creating topology from file!\n"));
3200 GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing", "topology_file",
3202 num_connections = create_from_file (pg, filename, &add_connections, ALLOWED);
3205 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!\n");
3206 num_connections = 0;
3209 case GNUNET_TESTING_TOPOLOGY_NONE:
3211 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3213 ("Creating no allowed topology (all peers can connect at core level)\n"));
3215 num_connections = pg->total * pg->total; /* Clique is allowed! */
3218 num_connections = 0;
3223 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
3225 ret = create_and_copy_friend_files (pg);
3226 if (ret != GNUNET_OK)
3229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3230 _("Failed during friend file copying!\n"));
3232 return GNUNET_SYSERR;
3237 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3238 _("Friend files created/copied successfully!\n"));
3243 /* Use the create clique method to initially set all connections as blacklisted. */
3244 if ((restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE) && (restrict_topology != GNUNET_TESTING_TOPOLOGY_FROM_FILE))
3245 create_clique (pg, &add_connections, BLACKLIST);
3247 unblacklisted_connections = 0;
3248 /* Un-blacklist connections as per the topology specified */
3249 switch (restrict_topology)
3251 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3253 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3254 _("Blacklisting all but clique topology\n"));
3256 unblacklisted_connections =
3257 create_clique (pg, &remove_connections, BLACKLIST);
3259 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3262 _("Blacklisting all but small world (ring) topology\n"));
3264 unblacklisted_connections =
3265 create_small_world_ring (pg, &remove_connections, BLACKLIST);
3267 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3269 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3271 ("Blacklisting all but small world (2d-torus) topology\n"));
3273 unblacklisted_connections =
3274 create_small_world (pg, &remove_connections, BLACKLIST);
3276 case GNUNET_TESTING_TOPOLOGY_RING:
3278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3279 _("Blacklisting all but ring topology\n"));
3281 unblacklisted_connections = create_ring (pg, &remove_connections, BLACKLIST);
3283 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3286 _("Blacklisting all but 2d torus topology\n"));
3288 unblacklisted_connections =
3289 create_2d_torus (pg, &remove_connections, BLACKLIST);
3291 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3293 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3294 _("Blacklisting all but Erdos-Renyi topology\n"));
3296 unblacklisted_connections =
3297 create_erdos_renyi (pg, &remove_connections, BLACKLIST);
3299 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3302 _("Blacklisting all but InterNAT topology\n"));
3304 unblacklisted_connections =
3305 create_nated_internet (pg, &remove_connections, BLACKLIST);
3307 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3310 _("Blacklisting all but Scale Free topology\n"));
3312 unblacklisted_connections =
3313 create_scale_free (pg, &remove_connections, BLACKLIST);
3315 case GNUNET_TESTING_TOPOLOGY_LINE:
3317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3318 _("Blacklisting all but straight line topology\n"));
3320 unblacklisted_connections = create_line (pg, &remove_connections, BLACKLIST);
3322 case GNUNET_TESTING_TOPOLOGY_NONE: /* Fall through */
3323 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3325 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3327 ("Creating no blacklist topology (all peers can connect at transport level)\n"));
3329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3331 ("Creating blacklist topology from allowed\n"));
3332 unblacklisted_connections = copy_allowed (pg, &remove_connections);
3337 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
3339 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'\n", restrict_transports);
3340 ret = create_and_copy_blacklist_files (pg, restrict_transports);
3341 if (ret != GNUNET_OK)
3344 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3345 _("Failed during blacklist file copying!\n"));
3352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3353 _("Blacklist files created/copied successfully!\n"));
3357 return num_connections;
3363 * Iterator for choosing random peers to connect.
3365 * @param cls closure, a RandomContext
3366 * @param key the key the second Daemon was stored under
3367 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3369 * @return GNUNET_YES to continue iteration
3372 random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3374 struct RandomContext *random_ctx = cls;
3375 double random_number;
3376 uint32_t second_pos;
3377 GNUNET_HashCode first_hash;
3380 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
3381 UINT64_MAX)) / ((double) UINT64_MAX);
3382 if (random_number < random_ctx->percentage)
3384 GNUNET_assert (GNUNET_OK ==
3385 GNUNET_CONTAINER_multihashmap_put (random_ctx->
3386 first->connect_peers_working_set,
3388 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3391 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
3392 uid_from_hash (key, &second_pos);
3393 hash_from_uid (random_ctx->first_uid, &first_hash);
3394 GNUNET_assert (random_ctx->pg->total > second_pos);
3395 GNUNET_assert (GNUNET_YES ==
3396 GNUNET_CONTAINER_multihashmap_remove (random_ctx->
3398 [second_pos].connect_peers,
3408 * Iterator for adding at least X peers to a peers connection set.
3410 * @param cls closure, MinimumContext
3411 * @param key the key the second Daemon was stored under
3412 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3414 * @return GNUNET_YES to continue iteration
3417 minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3419 struct MinimumContext *min_ctx = cls;
3420 uint32_t second_pos;
3421 GNUNET_HashCode first_hash;
3424 if (GNUNET_CONTAINER_multihashmap_size
3425 (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
3427 for (i = 0; i < min_ctx->num_to_add; i++)
3429 if (min_ctx->pg_array[i] == min_ctx->current)
3431 GNUNET_assert (GNUNET_OK ==
3432 GNUNET_CONTAINER_multihashmap_put
3433 (min_ctx->first->connect_peers_working_set, key,
3435 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3436 uid_from_hash (key, &second_pos);
3437 hash_from_uid (min_ctx->first_uid, &first_hash);
3438 GNUNET_assert (min_ctx->pg->total > second_pos);
3439 GNUNET_assert (GNUNET_OK ==
3440 GNUNET_CONTAINER_multihashmap_put (min_ctx->
3442 [second_pos].connect_peers_working_set,
3446 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3447 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
3448 GNUNET_assert (GNUNET_YES ==
3449 GNUNET_CONTAINER_multihashmap_remove
3450 (min_ctx->pg->peers[second_pos].connect_peers,
3451 &first_hash, min_ctx->first->daemon));
3458 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
3464 * Iterator for adding peers to a connection set based on a depth first search.
3466 * @param cls closure, MinimumContext
3467 * @param key the key the second daemon was stored under
3468 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3470 * @return GNUNET_YES to continue iteration
3473 dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3475 struct DFSContext *dfs_ctx = cls;
3476 GNUNET_HashCode first_hash;
3478 if (dfs_ctx->current == dfs_ctx->chosen)
3480 GNUNET_assert (GNUNET_OK ==
3481 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
3482 first->connect_peers_working_set,
3484 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3485 uid_from_hash (key, &dfs_ctx->second_uid);
3486 hash_from_uid (dfs_ctx->first_uid, &first_hash);
3487 GNUNET_assert (GNUNET_OK ==
3488 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
3490 [dfs_ctx->second_uid].connect_peers_working_set,
3494 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3495 GNUNET_assert (GNUNET_YES ==
3496 GNUNET_CONTAINER_multihashmap_remove (dfs_ctx->
3498 [dfs_ctx->second_uid].connect_peers,
3502 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
3503 return GNUNET_NO; /* We have found our peer, don't iterate more */
3512 * From the set of connections possible, choose percentage percent of connections
3513 * to actually connect.
3515 * @param pg the peergroup we are dealing with
3516 * @param percentage what percent of total connections to make
3519 choose_random_connections (struct GNUNET_TESTING_PeerGroup *pg,
3522 struct RandomContext random_ctx;
3525 struct PeerConnection *temp_peers;
3526 struct PeerConnection *conn_iter;
3527 double random_number;
3530 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3532 random_ctx.first_uid = pg_iter;
3533 random_ctx.first = &pg->peers[pg_iter];
3534 random_ctx.percentage = percentage;
3538 conn_iter = pg->peers[pg_iter].connect_peers_head;
3539 while (conn_iter != NULL)
3543 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
3544 UINT64_MAX)) / ((double) UINT64_MAX);
3545 if (random_number < percentage)
3547 add_connections(pg, pg_iter, conn_iter->index, WORKING_SET);
3549 conn_iter = conn_iter->next;
3552 pg->peers[pg_iter].connect_peers_working_set =
3553 GNUNET_CONTAINER_multihashmap_create (pg->total);
3554 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
3555 &random_connect_iterator,
3557 /* Now remove the old connections */
3558 GNUNET_CONTAINER_multihashmap_destroy (pg->
3559 peers[pg_iter].connect_peers);
3560 /* And replace with the random set */
3561 pg->peers[pg_iter].connect_peers =
3562 pg->peers[pg_iter].connect_peers_working_set;
3566 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3568 conn_iter = pg->peers[pg_iter].connect_peers_head;
3569 while (pg->peers[pg_iter].connect_peers_head != NULL)
3570 remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT);
3572 pg->peers[pg_iter].connect_peers_head = pg->peers[pg_iter].connect_peers_working_set_head;
3573 pg->peers[pg_iter].connect_peers_tail = pg->peers[pg_iter].connect_peers_working_set_tail;
3574 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
3575 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
3581 * Count the number of connections in a linked list of connections.
3583 * @param conn_list the connection list to get the count of
3585 * @return the number of elements in the list
3588 count_connections (struct PeerConnection *conn_list)
3590 struct PeerConnection *iter;
3594 while (iter != NULL)
3604 count_workingset_connections (struct GNUNET_TESTING_PeerGroup *pg)
3607 unsigned int pg_iter;
3609 struct PeerConnection *conn_iter;
3613 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3616 conn_iter = pg->peers[pg_iter].connect_peers_working_set_head;
3617 while (conn_iter != NULL)
3620 conn_iter = conn_iter->next;
3624 GNUNET_CONTAINER_multihashmap_size (pg->
3626 [pg_iter].connect_peers_working_set);
3635 count_allowed_connections (struct GNUNET_TESTING_PeerGroup *pg)
3638 unsigned int pg_iter;
3640 struct PeerConnection *conn_iter;
3644 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3647 conn_iter = pg->peers[pg_iter].allowed_peers_head;
3648 while (conn_iter != NULL)
3651 conn_iter = conn_iter->next;
3655 GNUNET_CONTAINER_multihashmap_size (pg->
3657 [pg_iter].allowed_peers);
3666 * From the set of connections possible, choose at least num connections per
3669 * @param pg the peergroup we are dealing with
3670 * @param num how many connections at least should each peer have (if possible)?
3673 choose_minimum (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
3676 struct MinimumContext minimum_ctx;
3678 struct PeerConnection *conn_iter;
3679 unsigned int temp_list_size;
3682 uint32_t random; /* Random list entry to connect peer to */
3687 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3689 temp_list_size = count_connections(pg->peers[pg_iter].connect_peers_head);
3690 if (temp_list_size == 0)
3692 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Peer %d has 0 connections!?!?\n", pg_iter);
3695 for (i = 0; i < num; i++)
3697 random = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, temp_list_size);
3698 conn_iter = pg->peers[pg_iter].connect_peers_head;
3699 for (count = 0; count < random; count++)
3700 conn_iter = conn_iter->next;
3701 /* We now have a random connection, connect it! */
3702 GNUNET_assert(conn_iter != NULL);
3703 add_connections(pg, pg_iter, conn_iter->index, WORKING_SET);
3707 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3709 pg->peers[pg_iter].connect_peers_working_set =
3710 GNUNET_CONTAINER_multihashmap_create (num);
3713 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3715 minimum_ctx.first_uid = pg_iter;
3716 minimum_ctx.pg_array =
3717 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
3718 GNUNET_CONTAINER_multihashmap_size
3719 (pg->peers[pg_iter].connect_peers));
3720 minimum_ctx.first = &pg->peers[pg_iter];
3721 minimum_ctx.pg = pg;
3722 minimum_ctx.num_to_add = num;
3723 minimum_ctx.current = 0;
3724 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
3725 &minimum_connect_iterator,
3729 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3731 /* Remove the "old" connections */
3732 GNUNET_CONTAINER_multihashmap_destroy (pg->
3733 peers[pg_iter].connect_peers);
3734 /* And replace with the working set */
3735 pg->peers[pg_iter].connect_peers =
3736 pg->peers[pg_iter].connect_peers_working_set;
3739 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3741 while (pg->peers[pg_iter].connect_peers_head != NULL)
3742 remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT);
3744 pg->peers[pg_iter].connect_peers_head = pg->peers[pg_iter].connect_peers_working_set_head;
3745 pg->peers[pg_iter].connect_peers_tail = pg->peers[pg_iter].connect_peers_working_set_tail;
3746 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
3747 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
3752 struct FindClosestContext
3755 * The currently known closest peer.
3757 struct GNUNET_TESTING_Daemon *closest;
3760 * The info for the peer we are adding connections for.
3762 struct PeerData *curr_peer;
3765 * The distance (bits) between the current
3766 * peer and the currently known closest.
3768 unsigned int closest_dist;
3771 * The offset of the closest known peer in
3774 unsigned int closest_num;
3778 * Iterator over hash map entries of the allowed
3779 * peer connections. Find the closest, not already
3780 * connected peer and return it.
3782 * @param cls closure (struct FindClosestContext)
3783 * @param key current key code (hash of offset in pg)
3784 * @param value value in the hash map - a GNUNET_TESTING_Daemon
3785 * @return GNUNET_YES if we should continue to
3790 find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value)
3792 struct FindClosestContext *closest_ctx = cls;
3793 struct GNUNET_TESTING_Daemon *daemon = value;
3795 if (((closest_ctx->closest == NULL) ||
3796 (GNUNET_CRYPTO_hash_matching_bits
3797 (&daemon->id.hashPubKey,
3798 &closest_ctx->curr_peer->daemon->id.hashPubKey) >
3799 closest_ctx->closest_dist))
3801 GNUNET_CONTAINER_multihashmap_contains (closest_ctx->
3802 curr_peer->connect_peers,
3805 closest_ctx->closest_dist =
3806 GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey,
3807 &closest_ctx->curr_peer->daemon->
3809 closest_ctx->closest = daemon;
3810 uid_from_hash (key, &closest_ctx->closest_num);
3817 * From the set of connections possible, choose at num connections per
3818 * peer based on depth which are closest out of those allowed. Guaranteed
3819 * to add num peers to connect to, provided there are that many peers
3820 * in the underlay topology to connect to.
3822 * @param pg the peergroup we are dealing with
3823 * @param num how many connections at least should each peer have (if possible)?
3824 * @param proc processor to actually add the connections
3825 * @param list the peer list to use
3828 add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num,
3829 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
3834 struct FindClosestContext closest_ctx;
3839 for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */
3841 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3843 closest_ctx.curr_peer = &pg->peers[pg_iter];
3844 closest_ctx.closest = NULL;
3845 closest_ctx.closest_dist = 0;
3846 closest_ctx.closest_num = 0;
3847 GNUNET_CONTAINER_multihashmap_iterate (pg->
3848 peers[pg_iter].allowed_peers,
3849 &find_closest_peers,
3851 if (closest_ctx.closest != NULL)
3853 GNUNET_assert (closest_ctx.closest_num < pg->total);
3854 proc (pg, pg_iter, closest_ctx.closest_num, list);
3862 * From the set of connections possible, choose at least num connections per
3863 * peer based on depth first traversal of peer connections. If DFS leaves
3864 * peers unconnected, ensure those peers get connections.
3866 * @param pg the peergroup we are dealing with
3867 * @param num how many connections at least should each peer have (if possible)?
3870 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
3874 uint32_t starting_peer;
3875 uint32_t least_connections;
3876 uint32_t random_connection;
3878 unsigned int temp_count;
3879 struct PeerConnection *peer_iter;
3881 struct DFSContext dfs_ctx;
3882 GNUNET_HashCode second_hash;
3888 while ((count_workingset_connections (pg) < num * pg->total)
3889 && (count_allowed_connections (pg) > 0))
3891 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
3893 least_connections = -1; /* Set to very high number */
3894 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3896 temp_count = count_connections(pg->peers[pg_iter].connect_peers_working_set_head);
3897 if (temp_count < least_connections)
3899 starting_peer = pg_iter;
3900 least_connections = temp_count;
3905 temp_count = count_connections(pg->peers[starting_peer].connect_peers_head);
3906 if (temp_count == 0)
3907 continue; /* FIXME: infinite loop? */
3909 random_connection = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, temp_count);
3911 peer_iter = pg->peers[starting_peer].connect_peers_head;
3912 while (temp_count < random_connection)
3914 peer_iter = peer_iter->next;
3917 GNUNET_assert(peer_iter != NULL);
3918 add_connections(pg, starting_peer, peer_iter->index, WORKING_SET);
3919 remove_connections(pg, starting_peer, peer_iter->index, CONNECT);
3920 starting_peer = peer_iter->index;
3925 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3927 pg->peers[pg_iter].connect_peers_working_set =
3928 GNUNET_CONTAINER_multihashmap_create (num);
3933 while ((count_workingset_connections (pg) < num * pg->total)
3934 && (count_allowed_connections (pg) > 0))
3936 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
3938 least_connections = -1; /* Set to very high number */
3939 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3941 if (GNUNET_CONTAINER_multihashmap_size
3942 (pg->peers[pg_iter].connect_peers_working_set) <
3945 starting_peer = pg_iter;
3947 GNUNET_CONTAINER_multihashmap_size (pg->
3949 [pg_iter].connect_peers_working_set);
3954 if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
3960 /* Choose a random peer from the chosen peers set of connections to add */
3962 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
3963 GNUNET_CONTAINER_multihashmap_size
3964 (pg->peers[starting_peer].connect_peers));
3965 dfs_ctx.first_uid = starting_peer;
3966 dfs_ctx.first = &pg->peers[starting_peer];
3968 dfs_ctx.current = 0;
3970 GNUNET_CONTAINER_multihashmap_iterate (pg->
3972 [starting_peer].connect_peers,
3973 &dfs_connect_iterator, &dfs_ctx);
3974 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
3975 hash_from_uid (dfs_ctx.second_uid, &second_hash);
3976 GNUNET_assert (GNUNET_YES ==
3977 GNUNET_CONTAINER_multihashmap_remove (pg->peers
3978 [starting_peer].connect_peers,
3982 [dfs_ctx.second_uid].daemon));
3983 starting_peer = dfs_ctx.second_uid;
3986 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3988 /* Remove the "old" connections */
3989 GNUNET_CONTAINER_multihashmap_destroy (pg->
3990 peers[pg_iter].connect_peers);
3991 /* And replace with the working set */
3992 pg->peers[pg_iter].connect_peers =
3993 pg->peers[pg_iter].connect_peers_working_set;
4000 * Internal callback for topology information for a particular peer.
4003 internal_topology_callback (void *cls,
4004 const struct GNUNET_PeerIdentity *peer,
4005 const struct GNUNET_TRANSPORT_ATS_Information
4008 struct CoreContext *core_ctx = cls;
4009 struct TopologyIterateContext *iter_ctx = core_ctx->iter_context;
4011 if (peer == NULL) /* Either finished, or something went wrong */
4013 iter_ctx->completed++;
4014 iter_ctx->connected--;
4015 /* One core context allocated per iteration, must free! */
4016 GNUNET_free (core_ctx);
4020 iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id,
4024 if (iter_ctx->completed == iter_ctx->total)
4026 iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL);
4027 /* Once all are done, free the iteration context */
4028 GNUNET_free (iter_ctx);
4034 * Check running topology iteration tasks, if below max start a new one, otherwise
4035 * schedule for some time in the future.
4038 schedule_get_topology (void *cls,
4039 const struct GNUNET_SCHEDULER_TaskContext *tc)
4041 struct CoreContext *core_context = cls;
4042 struct TopologyIterateContext *topology_context =
4043 (struct TopologyIterateContext *) core_context->iter_context;
4044 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4047 if (topology_context->connected > topology_context->pg->max_outstanding_connections)
4049 #if VERBOSE_TESTING > 2
4050 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4052 ("Delaying connect, we have too many outstanding connections!\n"));
4054 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4055 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4056 &schedule_get_topology, core_context);
4060 #if VERBOSE_TESTING > 2
4061 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4062 _("Creating connection, outstanding_connections is %d\n"),
4063 outstanding_connects);
4065 topology_context->connected++;
4068 GNUNET_CORE_iterate_peers (core_context->daemon->cfg,
4069 &internal_topology_callback,
4072 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n");
4073 internal_topology_callback (core_context, NULL, NULL);
4079 * Iterate over all (running) peers in the peer group, retrieve
4080 * all connections that each currently has.
4083 GNUNET_TESTING_get_topology (struct GNUNET_TESTING_PeerGroup *pg,
4084 GNUNET_TESTING_NotifyTopology cb, void *cls)
4086 struct TopologyIterateContext *topology_context;
4087 struct CoreContext *core_ctx;
4089 unsigned int total_count;
4091 /* Allocate a single topology iteration context */
4092 topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext));
4093 topology_context->topology_cb = cb;
4094 topology_context->cls = cls;
4095 topology_context->pg = pg;
4097 for (i = 0; i < pg->total; i++)
4099 if (pg->peers[i].daemon->running == GNUNET_YES)
4101 /* Allocate one core context per core we need to connect to */
4102 core_ctx = GNUNET_malloc (sizeof (struct CoreContext));
4103 core_ctx->daemon = pg->peers[i].daemon;
4104 /* Set back pointer to topology iteration context */
4105 core_ctx->iter_context = topology_context;
4106 GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx);
4110 if (total_count == 0)
4112 cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!");
4113 GNUNET_free (topology_context);
4116 topology_context->total = total_count;
4121 * Callback function to process statistic values.
4122 * This handler is here only really to insert a peer
4123 * identity (or daemon) so the statistics can be uniquely
4124 * tied to a single running peer.
4126 * @param cls closure
4127 * @param subsystem name of subsystem that created the statistic
4128 * @param name the name of the datum
4129 * @param value the current value
4130 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
4131 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
4134 internal_stats_callback (void *cls,
4135 const char *subsystem,
4136 const char *name, uint64_t value, int is_persistent)
4138 struct StatsCoreContext *core_context = cls;
4139 struct StatsIterateContext *stats_context =
4140 (struct StatsIterateContext *) core_context->iter_context;
4142 return stats_context->proc (stats_context->cls, &core_context->daemon->id,
4143 subsystem, name, value, is_persistent);
4147 * Internal continuation call for statistics iteration.
4149 * @param cls closure, the CoreContext for this iteration
4150 * @param success whether or not the statistics iterations
4151 * was canceled or not (we don't care)
4154 internal_stats_cont (void *cls, int success)
4156 struct StatsCoreContext *core_context = cls;
4157 struct StatsIterateContext *stats_context =
4158 (struct StatsIterateContext *) core_context->iter_context;
4160 stats_context->connected--;
4161 stats_context->completed++;
4163 if (stats_context->completed == stats_context->total)
4165 stats_context->cont (stats_context->cls, GNUNET_YES);
4166 GNUNET_free (stats_context);
4169 if (core_context->stats_handle != NULL)
4170 GNUNET_STATISTICS_destroy (core_context->stats_handle, GNUNET_NO);
4172 GNUNET_free (core_context);
4176 * Check running topology iteration tasks, if below max start a new one, otherwise
4177 * schedule for some time in the future.
4180 schedule_get_statistics (void *cls,
4181 const struct GNUNET_SCHEDULER_TaskContext *tc)
4183 struct StatsCoreContext *core_context = cls;
4184 struct StatsIterateContext *stats_context =
4185 (struct StatsIterateContext *) core_context->iter_context;
4187 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4190 if (stats_context->connected > stats_context->pg->max_outstanding_connections)
4192 #if VERBOSE_TESTING > 2
4193 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4195 ("Delaying connect, we have too many outstanding connections!\n"));
4197 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4198 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4199 &schedule_get_statistics, core_context);
4203 #if VERBOSE_TESTING > 2
4204 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4205 _("Creating connection, outstanding_connections is %d\n"),
4206 outstanding_connects);
4209 stats_context->connected++;
4210 core_context->stats_handle =
4211 GNUNET_STATISTICS_create ("testing", core_context->daemon->cfg);
4212 if (core_context->stats_handle == NULL)
4214 internal_stats_cont (core_context, GNUNET_NO);
4218 core_context->stats_get_handle =
4219 GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL,
4220 GNUNET_TIME_relative_get_forever (),
4221 &internal_stats_cont, &internal_stats_callback,
4223 if (core_context->stats_get_handle == NULL)
4224 internal_stats_cont (core_context, GNUNET_NO);
4229 struct DuplicateStats
4232 * Next item in the list
4234 struct DuplicateStats *next;
4237 * Nasty string, concatenation of relevant information.
4239 char *unique_string;
4243 * Check whether the combination of port/host/unix domain socket
4244 * already exists in the list of peers being checked for statistics.
4246 * @param pg the peergroup in question
4247 * @param specific_peer the peer we're concerned with
4248 * @param stats_list the list to return to the caller
4250 * @return GNUNET_YES if the statistics instance has been seen already,
4251 * GNUNET_NO if not (and we may have added it to the list)
4254 stats_check_existing (struct GNUNET_TESTING_PeerGroup *pg,
4255 struct PeerData *specific_peer,
4256 struct DuplicateStats **stats_list)
4258 struct DuplicateStats *pos;
4259 char *unix_domain_socket;
4260 unsigned long long port;
4263 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing",
4264 "single_statistics_per_host"))
4265 return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
4269 GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg, "statistics",
4270 "unixpath", &unix_domain_socket))
4274 GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg, "statistics",
4277 GNUNET_free(unix_domain_socket);
4281 if (specific_peer->daemon->hostname != NULL)
4282 GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
4283 unix_domain_socket, port);
4285 GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port);
4289 if (0 == strcmp (to_match, pos->unique_string))
4291 GNUNET_free (unix_domain_socket);
4292 GNUNET_free (to_match);
4297 pos = GNUNET_malloc (sizeof (struct DuplicateStats));
4298 pos->unique_string = to_match;
4299 pos->next = *stats_list;
4301 GNUNET_free (unix_domain_socket);
4306 * Iterate over all (running) peers in the peer group, retrieve
4307 * all statistics from each.
4310 GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
4311 GNUNET_STATISTICS_Callback cont,
4312 GNUNET_TESTING_STATISTICS_Iterator proc,
4315 struct StatsIterateContext *stats_context;
4316 struct StatsCoreContext *core_ctx;
4318 unsigned int total_count;
4319 struct DuplicateStats *stats_list;
4320 struct DuplicateStats *pos;
4323 /* Allocate a single stats iteration context */
4324 stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext));
4325 stats_context->cont = cont;
4326 stats_context->proc = proc;
4327 stats_context->cls = cls;
4328 stats_context->pg = pg;
4331 for (i = 0; i < pg->total; i++)
4333 if ((pg->peers[i].daemon->running == GNUNET_YES)
4335 stats_check_existing (pg, &pg->peers[i], &stats_list)))
4337 /* Allocate one core context per core we need to connect to */
4338 core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext));
4339 core_ctx->daemon = pg->peers[i].daemon;
4340 /* Set back pointer to topology iteration context */
4341 core_ctx->iter_context = stats_context;
4342 GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx);
4347 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4348 "Retrieving stats from %u total instances.\n", total_count);
4349 stats_context->total = total_count;
4350 if (stats_list != NULL)
4355 GNUNET_free (pos->unique_string);
4356 stats_list = pos->next;
4358 pos = stats_list->next;
4365 * There are many ways to connect peers that are supported by this function.
4366 * To connect peers in the same topology that was created via the
4367 * GNUNET_TESTING_create_topology, the topology variable must be set to
4368 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
4369 * a new instance of that topology will be generated and attempted to be
4370 * connected. This could result in some connections being impossible,
4371 * because some topologies are non-deterministic.
4373 * @param pg the peer group struct representing the running peers
4374 * @param topology which topology to connect the peers in
4375 * @param options options for connecting the topology
4376 * @param option_modifier modifier for options that take a parameter
4377 * @param connect_timeout how long to wait before giving up on connecting
4379 * @param connect_attempts how many times to attempt to connect two peers
4380 * over the connect_timeout duration
4381 * @param notify_callback notification to be called once all connections completed
4382 * @param notify_cls closure for notification callback
4384 * @return the number of connections that will be attempted, GNUNET_SYSERR on error
4387 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
4388 enum GNUNET_TESTING_Topology topology,
4389 enum GNUNET_TESTING_TopologyOption options,
4390 double option_modifier,
4391 struct GNUNET_TIME_Relative connect_timeout,
4392 unsigned int connect_attempts,
4393 GNUNET_TESTING_NotifyCompletion
4394 notify_callback, void *notify_cls)
4398 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
4399 #if VERBOSE_TOPOLOGY
4400 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4401 _("Creating clique CONNECT topology\n"));
4403 create_clique (pg, &add_connections, CONNECT);
4405 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
4406 #if VERBOSE_TOPOLOGY
4407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4408 _("Creating small world (ring) CONNECT topology\n"));
4410 create_small_world_ring (pg, &add_connections, CONNECT);
4412 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
4413 #if VERBOSE_TOPOLOGY
4414 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4415 _("Creating small world (2d-torus) CONNECT topology\n"));
4417 create_small_world (pg, &add_connections, CONNECT);
4419 case GNUNET_TESTING_TOPOLOGY_RING:
4420 #if VERBOSE_TOPOLOGY
4421 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4422 _("Creating ring CONNECT topology\n"));
4424 create_ring (pg, &add_connections, CONNECT);
4426 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
4427 #if VERBOSE_TOPOLOGY
4428 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4429 _("Creating 2d torus CONNECT topology\n"));
4431 create_2d_torus (pg, &add_connections, CONNECT);
4433 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
4434 #if VERBOSE_TOPOLOGY
4435 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4436 _("Creating Erdos-Renyi CONNECT topology\n"));
4438 create_erdos_renyi (pg, &add_connections, CONNECT);
4440 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
4441 #if VERBOSE_TOPOLOGY
4442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4443 _("Creating InterNAT CONNECT topology\n"));
4445 create_nated_internet (pg, &add_connections, CONNECT);
4447 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
4448 #if VERBOSE_TOPOLOGY
4449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4450 _("Creating Scale Free CONNECT topology\n"));
4452 create_scale_free (pg, &add_connections, CONNECT);
4454 case GNUNET_TESTING_TOPOLOGY_LINE:
4455 #if VERBOSE_TOPOLOGY
4456 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4457 _("Creating straight line CONNECT topology\n"));
4459 create_line (pg, &add_connections, CONNECT);
4461 case GNUNET_TESTING_TOPOLOGY_NONE:
4462 #if VERBOSE_TOPOLOGY
4463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4464 _("Creating no CONNECT topology\n"));
4466 copy_allowed_topology (pg);
4469 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4471 ("Unknown topology specification, can't connect peers!\n"));
4472 return GNUNET_SYSERR;
4477 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
4478 #if VERBOSE_TOPOLOGY
4479 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4481 ("Connecting random subset (%'.2f percent) of possible peers\n"),
4482 100 * option_modifier);
4484 choose_random_connections (pg, option_modifier);
4486 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
4487 #if VERBOSE_TOPOLOGY
4488 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4489 _("Connecting a minimum of %u peers each (if possible)\n"),
4490 (unsigned int) option_modifier);
4492 choose_minimum (pg, (unsigned int) option_modifier);
4494 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
4495 #if VERBOSE_TOPOLOGY
4496 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4498 ("Using DFS to connect a minimum of %u peers each (if possible)\n"),
4499 (unsigned int) option_modifier);
4502 perform_dfs (pg, (int) option_modifier);
4505 case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST:
4506 #if VERBOSE_TOPOLOGY
4507 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4509 ("Finding additional %u closest peers each (if possible)\n"),
4510 (unsigned int) option_modifier);
4513 add_closest (pg, (unsigned int) option_modifier,
4514 &add_connections, CONNECT);
4517 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
4519 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
4525 return connect_topology (pg, connect_timeout, connect_attempts, notify_callback, notify_cls);
4529 * Callback that is called whenever a hostkey is generated
4530 * for a peer. Call the real callback and decrement the
4531 * starting counter for the peergroup.
4533 * @param cls closure
4534 * @param id identifier for the daemon, NULL on error
4535 * @param d handle for the daemon
4536 * @param emsg error message (NULL on success)
4539 internal_hostkey_callback (void *cls,
4540 const struct GNUNET_PeerIdentity *id,
4541 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4543 struct InternalStartContext *internal_context = cls;
4544 internal_context->peer->pg->starting--;
4545 internal_context->peer->pg->started++;
4546 if (internal_context->hostkey_callback != NULL)
4547 internal_context->hostkey_callback (internal_context->hostkey_cls, id, d,
4549 else if (internal_context->peer->pg->started ==
4550 internal_context->peer->pg->total)
4552 internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
4553 GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg);
4558 * Callback that is called whenever a peer has finished starting.
4559 * Call the real callback and decrement the starting counter
4560 * for the peergroup.
4562 * @param cls closure
4563 * @param id identifier for the daemon, NULL on error
4565 * @param d handle for the daemon
4566 * @param emsg error message (NULL on success)
4569 internal_startup_callback (void *cls,
4570 const struct GNUNET_PeerIdentity *id,
4571 const struct GNUNET_CONFIGURATION_Handle *cfg,
4572 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4574 struct InternalStartContext *internal_context = cls;
4575 internal_context->peer->pg->starting--;
4576 if (internal_context->start_cb != NULL)
4577 internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d,
4582 internal_continue_startup (void *cls,
4583 const struct GNUNET_SCHEDULER_TaskContext *tc)
4585 struct InternalStartContext *internal_context = cls;
4587 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4592 if (internal_context->peer->pg->starting < internal_context->peer->pg->max_outstanding_connections)
4594 internal_context->peer->pg->starting++;
4595 GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
4599 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4600 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4601 &internal_continue_startup,
4608 * Callback for informing us about a successful
4609 * or unsuccessful churn start call.
4611 * @param cls a ChurnContext
4612 * @param id the peer identity of the started peer
4613 * @param cfg the handle to the configuration of the peer
4614 * @param d handle to the daemon for the peer
4615 * @param emsg NULL on success, non-NULL on failure
4619 churn_start_callback (void *cls,
4620 const struct GNUNET_PeerIdentity *id,
4621 const struct GNUNET_CONFIGURATION_Handle *cfg,
4622 struct GNUNET_TESTING_Daemon *d, const char *emsg)
4624 struct ChurnRestartContext *startup_ctx = cls;
4625 struct ChurnContext *churn_ctx = startup_ctx->churn_ctx;
4627 unsigned int total_left;
4628 char *error_message;
4630 error_message = NULL;
4633 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4634 "Churn stop callback failed with error `%s'\n", emsg);
4635 churn_ctx->num_failed_start++;
4639 churn_ctx->num_to_start--;
4643 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4644 "Started peer, %d left.\n", churn_ctx->num_to_start);
4648 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
4649 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
4651 if (total_left == 0)
4653 if ((churn_ctx->num_failed_stop > 0)
4654 || (churn_ctx->num_failed_start > 0))
4655 GNUNET_asprintf (&error_message,
4656 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
4657 churn_ctx->num_failed_start,
4658 churn_ctx->num_failed_stop);
4659 churn_ctx->cb (churn_ctx->cb_cls, error_message);
4660 GNUNET_free_non_null (error_message);
4661 GNUNET_free (churn_ctx);
4662 GNUNET_free (startup_ctx);
4668 schedule_churn_restart (void *cls,
4669 const struct GNUNET_SCHEDULER_TaskContext *tc)
4671 struct PeerRestartContext *peer_restart_ctx = cls;
4672 struct ChurnRestartContext *startup_ctx =
4673 peer_restart_ctx->churn_restart_ctx;
4675 if (startup_ctx->outstanding > startup_ctx->pg->max_outstanding_connections)
4676 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4677 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4678 &schedule_churn_restart, peer_restart_ctx);
4681 GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon,
4682 startup_ctx->timeout,
4683 &churn_start_callback,
4685 GNUNET_free (peer_restart_ctx);
4690 internal_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
4692 struct InternalStartContext *internal_context = cls;
4694 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4699 if (internal_context->peer->pg->starting < MAX_CONCURRENT_HOSTKEYS)
4701 internal_context->peer->pg->starting++;
4702 internal_context->peer->daemon =
4703 GNUNET_TESTING_daemon_start (internal_context->peer->cfg,
4704 internal_context->timeout,
4705 internal_context->hostname,
4706 internal_context->username,
4707 internal_context->sshport,
4708 internal_context->hostkey,
4709 &internal_hostkey_callback,
4711 &internal_startup_callback,
4716 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4717 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4718 &internal_start, internal_context);
4723 * Function which continues a peer group starting up
4724 * after successfully generating hostkeys for each peer.
4726 * @param pg the peer group to continue starting
4730 GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg)
4735 for (i = 0; i < pg->total; i++)
4737 GNUNET_SCHEDULER_add_now (&internal_continue_startup,
4738 &pg->peers[i].internal_context);
4739 //GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
4744 * Start count gnunet instances with the same set of transports and
4745 * applications. The port numbers (any option called "PORT") will be
4746 * adjusted to ensure that no two peers running on the same system
4747 * have the same port(s) in their respective configurations.
4749 * @param cfg configuration template to use
4750 * @param total number of daemons to start
4751 * @param max_concurrent_connections for testing, how many peers can
4752 * we connect to simultaneously
4753 * @param timeout total time allowed for peers to start
4754 * @param hostkey_callback function to call on each peers hostkey generation
4755 * if NULL, peers will be started by this call, if non-null,
4756 * GNUNET_TESTING_daemons_continue_startup must be called after
4757 * successful hostkey generation
4758 * @param hostkey_cls closure for hostkey callback
4759 * @param cb function to call on each daemon that was started
4760 * @param cb_cls closure for cb
4761 * @param connect_callback function to call each time two hosts are connected
4762 * @param connect_callback_cls closure for connect_callback
4763 * @param hostnames linked list of host structs to use to start peers on
4764 * (NULL to run on localhost only)
4766 * @return NULL on error, otherwise handle to control peer group
4768 struct GNUNET_TESTING_PeerGroup *
4769 GNUNET_TESTING_daemons_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
4771 unsigned int max_concurrent_connections,
4772 struct GNUNET_TIME_Relative timeout,
4773 GNUNET_TESTING_NotifyHostkeyCreated
4774 hostkey_callback, void *hostkey_cls,
4775 GNUNET_TESTING_NotifyDaemonRunning cb,
4777 GNUNET_TESTING_NotifyConnection
4778 connect_callback, void *connect_callback_cls,
4779 const struct GNUNET_TESTING_Host *hostnames)
4781 struct GNUNET_TESTING_PeerGroup *pg;
4782 const struct GNUNET_TESTING_Host *hostpos;
4788 const char *hostname;
4789 const char *username;
4790 char *baseservicehome;
4791 char *newservicehome;
4793 char *hostkeys_file;
4796 struct GNUNET_DISK_FileHandle *fd;
4797 struct GNUNET_CONFIGURATION_Handle *pcfg;
4799 unsigned int hostcnt;
4806 uint64_t total_hostkeys;
4807 struct GNUNET_OS_Process *proc;
4817 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
4819 pg->notify_connection = connect_callback;
4820 pg->notify_connection_cls = connect_callback_cls;
4822 pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
4823 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
4824 pg->max_outstanding_connections = max_concurrent_connections;
4825 if (NULL != hostnames)
4828 hostpos = hostnames;
4829 while (hostpos != NULL)
4831 hostpos = hostpos->next;
4834 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
4837 hostpos = hostnames;
4838 while (hostpos != NULL)
4840 pg->hosts[off].minport = LOW_PORT;
4841 pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname);
4842 if (hostpos->username != NULL)
4843 pg->hosts[off].username = GNUNET_strdup (hostpos->username);
4844 pg->hosts[off].sshport = hostpos->port;
4845 hostpos = hostpos->next;
4855 pg->num_hosts = off;
4859 /* skip leading spaces */
4860 while ((0 != *hostnames) && (isspace ((unsigned char) *hostnames)))
4863 while ('\0' != *rpos)
4865 if (isspace ((unsigned char) *rpos))
4869 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
4871 start = GNUNET_strdup (hostnames);
4873 while ('\0' != *pos)
4875 if (isspace ((unsigned char) *pos))
4878 if (strlen (start) > 0)
4880 pg->hosts[off].minport = LOW_PORT;
4881 pg->hosts[off++].hostname = start;
4887 if (strlen (start) > 0)
4889 pg->hosts[off].minport = LOW_PORT;
4890 pg->hosts[off++].hostname = start;
4894 GNUNET_free (start);
4895 GNUNET_free (pg->hosts);
4899 minport = 0; /* make gcc happy */
4908 /* Create the servicehome directory for each remote peer */
4909 GNUNET_assert(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
4911 for (i = 0; i < pg->num_hosts; i++)
4914 if (NULL != pg->hosts[i].username)
4915 GNUNET_asprintf (&arg, "%s@%s", pg->hosts[i].username, pg->hosts[i].hostname);
4917 GNUNET_asprintf (&arg, "%s", pg->hosts[i].hostname);
4918 if (pg->hosts[i].sshport != 0)
4920 GNUNET_asprintf (&ssh_port_str, "%d", pg->hosts[i].sshport);
4921 proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
4922 "ssh", "-P", ssh_port_str,
4926 arg, "mkdir -p", baseservicehome, NULL);
4929 proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
4930 "ssh", arg, "mkdir -p", baseservicehome, NULL);
4931 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating remote dir with command ssh %s %s %s\n", arg, " mkdir -p ", baseservicehome);
4932 GNUNET_OS_process_wait(proc);
4934 GNUNET_free(baseservicehome);
4936 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "HOSTKEYSFILE",
4939 if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file))
4940 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Couldn't read hostkeys file!\n");
4943 /* Check hostkey file size, read entire thing into memory */
4944 fd = GNUNET_DISK_file_open (hostkeys_file, GNUNET_DISK_OPEN_READ,
4945 GNUNET_DISK_PERM_NONE);
4948 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", hostkeys_file);
4952 if (GNUNET_YES != GNUNET_DISK_file_size (hostkeys_file, &fs, GNUNET_YES))
4955 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Found file size %llu for hostkeys, expect hostkeys to be size %d\n", fs, HOSTKEYFILESIZE);
4957 if (fs % HOSTKEYFILESIZE != 0)
4959 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "File size %llu seems incorrect for hostkeys...\n", fs);
4963 total_hostkeys = fs / HOSTKEYFILESIZE;
4964 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Will read %llu hostkeys from file\n", total_hostkeys);
4965 pg->hostkey_data = GNUNET_malloc_large (fs);
4966 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, pg->hostkey_data, fs));
4967 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fd));
4970 GNUNET_free(hostkeys_file);
4973 for (off = 0; off < total; off++)
4977 hostname = pg->hosts[off % hostcnt].hostname;
4978 username = pg->hosts[off % hostcnt].username;
4979 sshport = pg->hosts[off % hostcnt].sshport;
4980 pcfg = make_config (cfg,
4981 &pg->hosts[off % hostcnt].minport,
4982 &upnum, hostname, &fdnum);
4989 pcfg = make_config (cfg, &minport, &upnum, hostname, &fdnum);
4994 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
4996 ("Could not create configuration for peer number %u on `%s'!\n"),
4997 off, hostname == NULL ? "localhost" : hostname);
5002 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
5005 GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
5006 GNUNET_free (baseservicehome);
5010 tmpdir = getenv ("TMPDIR");
5011 tmpdir = tmpdir ? tmpdir : "/tmp";
5012 GNUNET_asprintf (&newservicehome,
5014 tmpdir, "gnunet-testing-test-test", off);
5016 GNUNET_CONFIGURATION_set_value_string (pcfg,
5018 "SERVICEHOME", newservicehome);
5019 GNUNET_free (newservicehome);
5020 pg->peers[off].cfg = pcfg;
5022 /* Can we do this later? */
5023 pg->peers[off].allowed_peers =
5024 GNUNET_CONTAINER_multihashmap_create (total);
5025 pg->peers[off].connect_peers =
5026 GNUNET_CONTAINER_multihashmap_create (total);
5027 pg->peers[off].blacklisted_peers =
5028 GNUNET_CONTAINER_multihashmap_create (total);
5031 pg->peers[off].pg = pg;
5032 pg->peers[off].internal_context.peer = &pg->peers[off];
5033 pg->peers[off].internal_context.timeout = timeout;
5034 pg->peers[off].internal_context.hostname = hostname;
5035 pg->peers[off].internal_context.username = username;
5036 pg->peers[off].internal_context.sshport = sshport;
5037 if (pg->hostkey_data != NULL)
5038 pg->peers[off].internal_context.hostkey = &pg->hostkey_data[off * HOSTKEYFILESIZE];
5039 pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
5040 pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
5041 pg->peers[off].internal_context.start_cb = cb;
5042 pg->peers[off].internal_context.start_cb_cls = cb_cls;
5044 GNUNET_SCHEDULER_add_now (&internal_start,
5045 &pg->peers[off].internal_context);
5052 * Get a daemon by number, so callers don't have to do nasty
5053 * offsetting operation.
5055 struct GNUNET_TESTING_Daemon *
5056 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg,
5057 unsigned int position)
5059 if (position < pg->total)
5060 return pg->peers[position].daemon;
5066 * Get a daemon by peer identity, so callers can
5067 * retrieve the daemon without knowing it's offset.
5069 * @param pg the peer group to retrieve the daemon from
5070 * @param peer_id the peer identity of the daemon to retrieve
5072 * @return the daemon on success, or NULL if no such peer identity is found
5074 struct GNUNET_TESTING_Daemon *
5075 GNUNET_TESTING_daemon_get_by_id (struct GNUNET_TESTING_PeerGroup *pg,
5076 struct GNUNET_PeerIdentity *peer_id)
5080 for (i = 0; i < pg->total; i++)
5083 memcmp (&pg->peers[i].daemon->id, peer_id,
5084 sizeof (struct GNUNET_PeerIdentity)))
5085 return pg->peers[i].daemon;
5092 * Prototype of a function that will be called when a
5093 * particular operation was completed the testing library.
5095 * @param cls closure (a struct RestartContext)
5096 * @param id id of the peer that was restarted
5097 * @param cfg handle to the configuration of the peer
5098 * @param d handle to the daemon that was restarted
5099 * @param emsg NULL on success
5102 restart_callback (void *cls,
5103 const struct GNUNET_PeerIdentity *id,
5104 const struct GNUNET_CONFIGURATION_Handle *cfg,
5105 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5107 struct RestartContext *restart_context = cls;
5111 restart_context->peers_restarted++;
5115 restart_context->peers_restart_failed++;
5118 if (restart_context->peers_restarted == restart_context->peer_group->total)
5120 restart_context->callback (restart_context->callback_cls, NULL);
5121 GNUNET_free (restart_context);
5123 else if (restart_context->peers_restart_failed +
5124 restart_context->peers_restarted ==
5125 restart_context->peer_group->total)
5127 restart_context->callback (restart_context->callback_cls,
5128 "Failed to restart peers!");
5129 GNUNET_free (restart_context);
5135 * Callback for informing us about a successful
5136 * or unsuccessful churn stop call.
5138 * @param cls a ChurnContext
5139 * @param emsg NULL on success, non-NULL on failure
5143 churn_stop_callback (void *cls, const char *emsg)
5145 struct ShutdownContext *shutdown_ctx = cls;
5146 struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls;
5147 unsigned int total_left;
5148 char *error_message;
5150 error_message = NULL;
5151 shutdown_ctx->outstanding--;
5155 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5156 "Churn stop callback failed with error `%s'\n", emsg);
5157 churn_ctx->num_failed_stop++;
5161 churn_ctx->num_to_stop--;
5165 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5166 "Stopped peer, %d left.\n", churn_ctx->num_to_stop);
5169 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
5170 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
5172 if (total_left == 0)
5174 if ((churn_ctx->num_failed_stop > 0)
5175 || (churn_ctx->num_failed_start > 0))
5177 GNUNET_asprintf (&error_message,
5178 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
5179 churn_ctx->num_failed_start,
5180 churn_ctx->num_failed_stop);
5182 churn_ctx->cb (churn_ctx->cb_cls, error_message);
5183 GNUNET_free_non_null (error_message);
5184 GNUNET_free (churn_ctx);
5185 GNUNET_free (shutdown_ctx);
5190 * Count the number of running peers.
5192 * @param pg handle for the peer group
5194 * @return the number of currently running peers in the peer group
5197 GNUNET_TESTING_daemons_running (struct GNUNET_TESTING_PeerGroup *pg)
5200 unsigned int running = 0;
5201 for (i = 0; i < pg->total; i++)
5203 if (pg->peers[i].daemon->running == GNUNET_YES)
5205 GNUNET_assert (running != -1);
5213 * Task to rate limit the number of outstanding peer shutdown
5214 * requests. This is necessary for making sure we don't do
5215 * too many ssh connections at once, but is generally nicer
5216 * to any system as well (graduated task starts, as opposed
5217 * to calling gnunet-arm N times all at once).
5220 schedule_churn_shutdown_task (void *cls,
5221 const struct GNUNET_SCHEDULER_TaskContext *tc)
5223 struct PeerShutdownContext *peer_shutdown_ctx = cls;
5224 struct ShutdownContext *shutdown_ctx;
5225 struct ChurnContext *churn_ctx;
5226 GNUNET_assert (peer_shutdown_ctx != NULL);
5227 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
5228 GNUNET_assert (shutdown_ctx != NULL);
5229 churn_ctx = (struct ChurnContext *)shutdown_ctx->cb_cls;
5230 if (shutdown_ctx->outstanding > churn_ctx->pg->max_outstanding_connections)
5231 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5232 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5233 &schedule_churn_shutdown_task,
5237 shutdown_ctx->outstanding++;
5238 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
5239 shutdown_ctx->timeout, shutdown_ctx->cb,
5240 shutdown_ctx, GNUNET_NO, GNUNET_YES);
5241 GNUNET_free (peer_shutdown_ctx);
5246 * Simulate churn by stopping some peers (and possibly
5247 * re-starting others if churn is called multiple times). This
5248 * function can only be used to create leave-join churn (peers "never"
5249 * leave for good). First "voff" random peers that are currently
5250 * online will be taken offline; then "von" random peers that are then
5251 * offline will be put back online. No notifications will be
5252 * generated for any of these operations except for the callback upon
5255 * @param pg handle for the peer group
5256 * @param voff number of peers that should go offline
5257 * @param von number of peers that should come back online;
5258 * must be zero on first call (since "testbed_start"
5259 * always starts all of the peers)
5260 * @param timeout how long to wait for operations to finish before
5262 * @param cb function to call at the end
5263 * @param cb_cls closure for cb
5266 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
5269 struct GNUNET_TIME_Relative timeout,
5270 GNUNET_TESTING_NotifyCompletion cb,
5273 struct ChurnContext *churn_ctx;
5274 struct ShutdownContext *shutdown_ctx;
5275 struct PeerShutdownContext *peer_shutdown_ctx;
5276 struct PeerRestartContext *peer_restart_ctx;
5277 struct ChurnRestartContext *churn_startup_ctx;
5279 unsigned int running;
5280 unsigned int stopped;
5281 unsigned int total_running;
5282 unsigned int total_stopped;
5284 unsigned int *running_arr;
5285 unsigned int *stopped_arr;
5286 unsigned int *running_permute;
5287 unsigned int *stopped_permute;
5292 if ((von == 0) && (voff == 0)) /* No peers at all? */
5298 for (i = 0; i < pg->total; i++)
5300 if (pg->peers[i].daemon->running == GNUNET_YES)
5302 GNUNET_assert (running != -1);
5307 GNUNET_assert (stopped != -1);
5314 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5315 "Trying to stop more peers than are currently running!\n");
5316 cb (cb_cls, "Trying to stop more peers than are currently running!");
5322 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5323 "Trying to start more peers than are currently stopped!\n");
5324 cb (cb_cls, "Trying to start more peers than are currently stopped!");
5328 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
5332 running_arr = GNUNET_malloc (running * sizeof (unsigned int));
5336 stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int));
5338 running_permute = NULL;
5339 stopped_permute = NULL;
5343 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, running);
5346 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, stopped);
5348 total_running = running;
5349 total_stopped = stopped;
5353 churn_ctx->num_to_start = von;
5354 churn_ctx->num_to_stop = voff;
5356 churn_ctx->cb_cls = cb_cls;
5359 for (i = 0; i < pg->total; i++)
5361 if (pg->peers[i].daemon->running == GNUNET_YES)
5363 GNUNET_assert ((running_arr != NULL) && (total_running > running));
5364 running_arr[running] = i;
5369 GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
5370 stopped_arr[stopped] = i;
5375 GNUNET_assert (running >= voff);
5378 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
5379 shutdown_ctx->cb = &churn_stop_callback;
5380 shutdown_ctx->cb_cls = churn_ctx;
5381 shutdown_ctx->total_peers = voff;
5382 shutdown_ctx->timeout = timeout;
5385 for (i = 0; i < voff; i++)
5388 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n",
5389 running_permute[i]);
5391 GNUNET_assert (running_arr != NULL);
5392 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
5393 peer_shutdown_ctx->daemon =
5394 pg->peers[running_arr[running_permute[i]]].daemon;
5395 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
5396 GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task,
5400 GNUNET_TESTING_daemon_stop (pg->peers[running_arr[running_permute[i]]].daemon,
5402 &churn_stop_callback, churn_ctx,
5403 GNUNET_NO, GNUNET_YES); */
5406 GNUNET_assert (stopped >= von);
5409 churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
5410 churn_startup_ctx->churn_ctx = churn_ctx;
5411 churn_startup_ctx->timeout = timeout;
5412 churn_startup_ctx->pg = pg;
5414 for (i = 0; i < von; i++)
5417 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n",
5418 stopped_permute[i]);
5420 GNUNET_assert (stopped_arr != NULL);
5421 peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext));
5422 peer_restart_ctx->churn_restart_ctx = churn_startup_ctx;
5423 peer_restart_ctx->daemon =
5424 pg->peers[stopped_arr[stopped_permute[i]]].daemon;
5425 GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx);
5427 GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon,
5428 timeout, &churn_start_callback, churn_ctx); */
5431 GNUNET_free_non_null (running_arr);
5432 GNUNET_free_non_null (stopped_arr);
5433 GNUNET_free_non_null (running_permute);
5434 GNUNET_free_non_null (stopped_permute);
5439 * Restart all peers in the given group.
5441 * @param pg the handle to the peer group
5442 * @param callback function to call on completion (or failure)
5443 * @param callback_cls closure for the callback function
5446 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg,
5447 GNUNET_TESTING_NotifyCompletion callback,
5450 struct RestartContext *restart_context;
5455 restart_context = GNUNET_malloc (sizeof (struct RestartContext));
5456 restart_context->peer_group = pg;
5457 restart_context->peers_restarted = 0;
5458 restart_context->callback = callback;
5459 restart_context->callback_cls = callback_cls;
5461 for (off = 0; off < pg->total; off++)
5463 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon,
5464 &restart_callback, restart_context);
5470 * Start or stop an individual peer from the given group.
5472 * @param pg handle to the peer group
5473 * @param offset which peer to start or stop
5474 * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
5475 * @param timeout how long to wait for shutdown
5476 * @param cb function to call at the end
5477 * @param cb_cls closure for cb
5480 GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg,
5481 unsigned int offset,
5483 struct GNUNET_TIME_Relative timeout,
5484 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
5486 struct ShutdownContext *shutdown_ctx;
5487 struct ChurnRestartContext *startup_ctx;
5488 struct ChurnContext *churn_ctx;
5490 if (GNUNET_NO == desired_status)
5492 if (NULL != pg->peers[offset].daemon)
5494 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
5495 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
5496 churn_ctx->num_to_start = 0;
5497 churn_ctx->num_to_stop = 1;
5499 churn_ctx->cb_cls = cb_cls;
5500 shutdown_ctx->cb_cls = churn_ctx;
5501 GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon,
5502 timeout, &churn_stop_callback,
5503 shutdown_ctx, GNUNET_NO, GNUNET_YES);
5506 else if (GNUNET_YES == desired_status)
5508 if (NULL == pg->peers[offset].daemon)
5510 startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
5511 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
5512 churn_ctx->num_to_start = 1;
5513 churn_ctx->num_to_stop = 0;
5515 churn_ctx->cb_cls = cb_cls;
5516 startup_ctx->churn_ctx = churn_ctx;
5517 GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon,
5518 timeout, &churn_start_callback,
5528 * Callback for shutting down peers in a peer group.
5530 * @param cls closure (struct ShutdownContext)
5531 * @param emsg NULL on success
5534 internal_shutdown_callback (void *cls, const char *emsg)
5536 struct ShutdownContext *shutdown_ctx = cls;
5538 shutdown_ctx->outstanding--;
5541 shutdown_ctx->peers_down++;
5545 shutdown_ctx->peers_failed++;
5548 if ((shutdown_ctx->cb != NULL)
5549 && (shutdown_ctx->peers_down + shutdown_ctx->peers_failed ==
5550 shutdown_ctx->total_peers))
5552 if (shutdown_ctx->peers_failed > 0)
5553 shutdown_ctx->cb (shutdown_ctx->cb_cls,
5554 "Not all peers successfully shut down!");
5556 shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL);
5557 GNUNET_free (shutdown_ctx);
5563 * Task to rate limit the number of outstanding peer shutdown
5564 * requests. This is necessary for making sure we don't do
5565 * too many ssh connections at once, but is generally nicer
5566 * to any system as well (graduated task starts, as opposed
5567 * to calling gnunet-arm N times all at once).
5570 schedule_shutdown_task (void *cls,
5571 const struct GNUNET_SCHEDULER_TaskContext *tc)
5573 struct PeerShutdownContext *peer_shutdown_ctx = cls;
5574 struct ShutdownContext *shutdown_ctx;
5576 GNUNET_assert (peer_shutdown_ctx != NULL);
5577 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
5578 GNUNET_assert (shutdown_ctx != NULL);
5580 if (shutdown_ctx->outstanding > shutdown_ctx->pg->max_outstanding_connections)
5581 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5582 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5583 &schedule_shutdown_task, peer_shutdown_ctx);
5586 shutdown_ctx->outstanding++;
5587 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
5588 shutdown_ctx->timeout,
5589 &internal_shutdown_callback, shutdown_ctx,
5590 GNUNET_YES, GNUNET_NO);
5591 GNUNET_free (peer_shutdown_ctx);
5596 * Shutdown all peers started in the given group.
5598 * @param pg handle to the peer group
5599 * @param timeout how long to wait for shutdown
5600 * @param cb callback to notify upon success or failure
5601 * @param cb_cls closure for cb
5604 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg,
5605 struct GNUNET_TIME_Relative timeout,
5606 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
5609 struct ShutdownContext *shutdown_ctx;
5610 struct PeerShutdownContext *peer_shutdown_ctx;
5612 struct PeerConnection *conn_iter;
5613 struct PeerConnection *temp_conn;
5616 GNUNET_assert (pg->total > 0);
5618 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
5619 shutdown_ctx->cb = cb;
5620 shutdown_ctx->cb_cls = cb_cls;
5621 shutdown_ctx->total_peers = pg->total;
5622 shutdown_ctx->timeout = timeout;
5623 shutdown_ctx->pg = pg;
5624 /* shtudown_ctx->outstanding = 0; */
5626 for (off = 0; off < pg->total; off++)
5628 GNUNET_assert (NULL != pg->peers[off].daemon);
5629 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
5630 peer_shutdown_ctx->daemon = pg->peers[off].daemon;
5631 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
5632 GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx);
5634 if (NULL != pg->peers[off].cfg)
5635 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
5637 conn_iter = pg->peers[off].allowed_peers_head;
5638 while (conn_iter != NULL)
5640 temp_conn = conn_iter->next;
5641 GNUNET_free(conn_iter);
5642 conn_iter = temp_conn;
5645 conn_iter = pg->peers[off].connect_peers_head;
5646 while (conn_iter != NULL)
5648 temp_conn = conn_iter->next;
5649 GNUNET_free(conn_iter);
5650 conn_iter = temp_conn;
5653 conn_iter = pg->peers[off].blacklisted_peers_head;
5654 while (conn_iter != NULL)
5656 temp_conn = conn_iter->next;
5657 GNUNET_free(conn_iter);
5658 conn_iter = temp_conn;
5661 conn_iter = pg->peers[off].connect_peers_working_set_head;
5662 while (conn_iter != NULL)
5664 temp_conn = conn_iter->next;
5665 GNUNET_free(conn_iter);
5666 conn_iter = temp_conn;
5669 if (pg->peers[off].allowed_peers != NULL)
5670 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers);
5671 if (pg->peers[off].connect_peers != NULL)
5672 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers);
5673 if (pg->peers[off].blacklisted_peers != NULL)
5674 GNUNET_CONTAINER_multihashmap_destroy (pg->
5675 peers[off].blacklisted_peers);
5678 GNUNET_free (pg->peers);
5679 GNUNET_free_non_null(pg->hostkey_data);
5680 for (off = 0; off < pg->num_hosts; off++)
5682 GNUNET_free (pg->hosts[off].hostname);
5683 GNUNET_free_non_null (pg->hosts[off].username);
5685 GNUNET_free_non_null (pg->hosts);
5690 /* end of testing_group.c */