2 This file is part of GNUnet
3 (C) 2008, 2009 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testing/testing_group.c
23 * @brief convenience API for writing testcases for GNUnet
24 * @author Nathan Evans
25 * @author Christian Grothoff
29 #include "gnunet_arm_service.h"
30 #include "gnunet_testing_lib.h"
31 #include "gnunet_core_service.h"
33 #define VERBOSE_TESTING GNUNET_NO
35 #define VERBOSE_TOPOLOGY GNUNET_YES
37 #define DEBUG_CHURN GNUNET_NO
41 #define USE_SEND_HELLOS GNUNET_NO
43 #define TOPOLOGY_HACK GNUNET_YES
46 * Lowest port used for GNUnet testing. Should be high enough to not
47 * conflict with other applications running on the hosts but be low
48 * enough to not conflict with client-ports (typically starting around
51 #define LOW_PORT 12000
54 * Highest port used for GNUnet testing. Should be low enough to not
55 * conflict with the port range for "local" ports (client apps; see
56 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
58 #define HIGH_PORT 56000
60 /* Maximum time to delay connect attempt */
61 #define MAX_CONNECT_DELAY 300
64 * Which list of peers do we need to modify?
68 /** Modify allowed peers */
71 /** Modify connect peers */
74 /** Modify blacklist peers */
77 /** Modify workingset peers */
82 * Prototype of a function called whenever two peers would be connected
83 * in a certain topology.
85 typedef unsigned int (*GNUNET_TESTING_ConnectionProcessor) (struct
86 GNUNET_TESTING_PeerGroup
96 * Context for handling churning a peer group
101 * The peergroup we are dealing with.
103 struct GNUNET_TESTING_PeerGroup *pg;
106 * Callback used to notify of churning finished
108 GNUNET_TESTING_NotifyCompletion cb;
111 * Closure for callback
116 * Number of peers that still need to be started
118 unsigned int num_to_start;
121 * Number of peers that still need to be stopped
123 unsigned int num_to_stop;
126 * Number of peers that failed to start
128 unsigned int num_failed_start;
131 * Number of peers that failed to stop
133 unsigned int num_failed_stop;
136 struct RestartContext
139 * The group of peers being restarted
141 struct GNUNET_TESTING_PeerGroup *peer_group;
144 * How many peers have been restarted thus far
146 unsigned int peers_restarted;
149 * How many peers got an error when restarting
151 unsigned int peers_restart_failed;
154 * The function to call once all peers have been restarted
156 GNUNET_TESTING_NotifyCompletion callback;
159 * Closure for callback function
166 struct SendHelloContext
169 * Global handle to the peer group.
171 struct GNUNET_TESTING_PeerGroup *pg;
174 * The data about this specific peer.
176 struct PeerData *peer;
179 * The next HELLO that needs sent to this peer.
181 struct PeerConnection *peer_pos;
184 * Are we connected to CORE yet?
186 unsigned int core_ready;
189 * How many attempts should we make for failed connections?
191 unsigned int connect_attempts;
194 * Task for scheduling core connect requests to be sent.
196 GNUNET_SCHEDULER_TaskIdentifier core_connect_task;
200 struct ShutdownContext
202 struct GNUNET_TESTING_PeerGroup *pg;
204 * Total peers to wait for
206 unsigned int total_peers;
209 * Number of peers successfully shut down
211 unsigned int peers_down;
214 * Number of peers failed to shut down
216 unsigned int peers_failed;
219 * Number of peers we have started shutting
220 * down. If too many, wait on them.
222 unsigned int outstanding;
225 * Timeout for shutdown.
227 struct GNUNET_TIME_Relative timeout;
230 * Callback to call when all peers either
231 * shutdown or failed to shutdown
233 GNUNET_TESTING_NotifyCompletion cb;
242 * Individual shutdown context for a particular peer.
244 struct PeerShutdownContext
247 * Pointer to the high level shutdown context.
249 struct ShutdownContext *shutdown_ctx;
252 * The daemon handle for the peer to shut down.
254 struct GNUNET_TESTING_Daemon *daemon;
258 * Individual shutdown context for a particular peer.
260 struct PeerRestartContext
263 * Pointer to the high level restart context.
265 struct ChurnRestartContext *churn_restart_ctx;
268 * The daemon handle for the peer to shut down.
270 struct GNUNET_TESTING_Daemon *daemon;
274 struct CreateTopologyContext
278 * Function to call with number of connections
280 GNUNET_TESTING_NotifyConnections cont;
283 * Closure for connection notification
290 /** Waiting to read number of peers */
293 /** Should find next peer index */
296 /** Should find colon */
299 /** Should read other peer index, space, or endline */
305 struct PeerConnection
310 struct PeerConnection *prev;
315 struct PeerConnection *next;
319 * Index of daemon in pg->peers
326 struct InternalStartContext
329 * Pointer to peerdata
331 struct PeerData *peer;
334 * Timeout for peer startup
336 struct GNUNET_TIME_Relative timeout;
339 * Client callback for hostkey notification
341 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
344 * Closure for hostkey_callback
349 * Client callback for peer start notification
351 GNUNET_TESTING_NotifyDaemonRunning start_cb;
359 * Hostname, where to start the peer
361 const char *hostname;
364 * Username to use when connecting to the
367 const char *username;
370 * Pointer to starting memory location of a hostkey
375 * Port to use for ssh.
381 struct ChurnRestartContext
384 * PeerGroup that we are working with.
386 struct GNUNET_TESTING_PeerGroup *pg;
389 * Number of restarts currently in flight.
391 unsigned int outstanding;
394 * Handle to the underlying churn context.
396 struct ChurnContext *churn_ctx;
399 * How long to allow the operation to take.
401 struct GNUNET_TIME_Relative timeout;
404 struct OutstandingSSH
406 struct OutstandingSSH *next;
408 struct OutstandingSSH *prev;
411 * Number of current ssh connections.
413 uint32_t outstanding;
416 * The hostname of this peer.
418 const char *hostname;
422 * Data we keep per peer.
427 * (Initial) configuration of the host.
428 * (initial because clients could change
429 * it and we would not know about those
432 struct GNUNET_CONFIGURATION_Handle *cfg;
435 * Handle for controlling the daemon.
437 struct GNUNET_TESTING_Daemon *daemon;
440 * The peergroup this peer belongs to.
442 struct GNUNET_TESTING_PeerGroup *pg;
446 * Linked list of allowed peer connections.
448 struct PeerConnection *allowed_peers_head;
451 * Linked list of allowed peer connections.
453 struct PeerConnection *allowed_peers_tail;
456 * Linked list of blacklisted peer connections.
458 struct PeerConnection *blacklisted_peers_head;
461 * Linked list of blacklisted peer connections.
463 struct PeerConnection *blacklisted_peers_tail;
466 * Linked list of connect peer connections.
468 struct PeerConnection *connect_peers_head;
471 * Linked list of connect peer connections.
473 struct PeerConnection *connect_peers_tail;
476 * Linked list of connect peer connections.
478 struct PeerConnection *connect_peers_working_set_head;
481 * Linked list of connect peer connections.
483 struct PeerConnection *connect_peers_working_set_tail;
487 * Hash map of allowed peer connections (F2F created topology)
489 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
492 * Hash map of blacklisted peers
494 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
497 * Hash map of peer connections
499 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
502 * Temporary hash map of peer connections
504 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
508 * Temporary variable for topology creation, should be reset before
509 * creating any topology so the count is valid once finished.
514 * Context to keep track of peers being started, to
515 * stagger hostkey generation and peer startup.
517 struct InternalStartContext internal_context;
522 * Linked list of per-host data.
532 * SSH username to use when connecting to this host.
537 * SSH port to use when connecting to this host.
542 * Lowest port that we have not yet used
548 struct TopologyIterateContext
551 * The peergroup we are working with.
553 struct GNUNET_TESTING_PeerGroup *pg;
556 * Callback for notifying of two connected peers.
558 GNUNET_TESTING_NotifyTopology topology_cb;
561 * Closure for topology_cb
566 * Number of peers currently connected to.
568 unsigned int connected;
571 * Number of peers we have finished iterating.
573 unsigned int completed;
576 * Number of peers total.
581 struct StatsIterateContext
584 * The peergroup that we are dealing with.
586 struct GNUNET_TESTING_PeerGroup *pg;
589 * Continuation to call once all stats information has been retrieved.
591 GNUNET_STATISTICS_Callback cont;
594 * Proc function to call on each value received.
596 GNUNET_TESTING_STATISTICS_Iterator proc;
599 * Closure for topology_cb
604 * Number of peers currently connected to.
606 unsigned int connected;
609 * Number of peers we have finished iterating.
611 unsigned int completed;
614 * Number of peers total.
622 struct GNUNET_TESTING_Daemon *daemon;
625 struct StatsCoreContext
628 struct GNUNET_TESTING_Daemon *daemon;
630 * Handle to the statistics service.
632 struct GNUNET_STATISTICS_Handle *stats_handle;
635 * Handle for getting statistics.
637 struct GNUNET_STATISTICS_GetHandle *stats_get_handle;
641 struct ConnectTopologyContext
644 * How many connections are left to create.
646 unsigned int remaining_connections;
649 * Handle to group of peers.
651 struct GNUNET_TESTING_PeerGroup *pg;
654 * How long to try this connection before timing out.
656 struct GNUNET_TIME_Relative connect_timeout;
659 * How many times to retry connecting the two peers.
661 unsigned int connect_attempts;
664 * Temp value set for each iteration.
666 //struct PeerData *first;
669 * Notification that all peers are connected.
671 GNUNET_TESTING_NotifyCompletion notify_connections_done;
674 * Closure for notify.
681 * Handle to a group of GNUnet peers.
683 struct GNUNET_TESTING_PeerGroup
686 * Configuration template.
688 const struct GNUNET_CONFIGURATION_Handle *cfg;
691 * Function to call on each started daemon.
693 //GNUNET_TESTING_NotifyDaemonRunning cb;
701 * Function to call on each topology connection created
703 GNUNET_TESTING_NotifyConnection notify_connection;
706 * Callback for notify_connection
708 void *notify_connection_cls;
711 * Array of information about hosts.
713 struct HostData *hosts;
716 * Number of hosts (size of HostData)
718 unsigned int num_hosts;
721 * Array of "total" peers.
723 struct PeerData *peers;
726 * Number of peers in this group.
731 * At what time should we fail the peer startup process?
733 struct GNUNET_TIME_Absolute max_timeout;
736 * How many peers are being started right now?
738 unsigned int starting;
741 * How many peers have already been started?
743 unsigned int started;
746 * Number of possible connections to peers
749 unsigned int max_outstanding_connections;
752 * Number of ssh connections to peers (max).
754 unsigned int max_concurrent_ssh;
757 * Number of connects we are waiting on, allows us to rate limit
760 unsigned int outstanding_connects;
763 * Number of HELLOs we have yet to send.
765 unsigned int remaining_hellos;
768 * How many connects have already been scheduled?
770 unsigned int total_connects_scheduled;
773 * Hostkeys loaded from a file.
778 * Head of DLL to keep track of the number of outstanding
779 * ssh connections per peer.
781 struct OutstandingSSH *ssh_head;
784 * Tail of DLL to keep track of the number of outstanding
785 * ssh connections per peer.
787 struct OutstandingSSH *ssh_tail;
790 * Stop scheduling peers connecting.
792 unsigned int stop_connects;
795 * Connection context for peer group.
797 struct ConnectTopologyContext ct_ctx;
803 * The altered configuration.
805 struct GNUNET_CONFIGURATION_Handle *ret;
808 * The original configuration to alter.
810 const struct GNUNET_CONFIGURATION_Handle *orig;
813 * The hostname that this peer will run on.
815 const char *hostname;
818 * The next possible port to assign.
823 * Unique number for unix domain sockets.
828 * Unique number for this peer/host to offset
829 * things that are grouped by host.
835 struct ConnectContext
838 * Index of peer to connect second to.
840 uint32_t first_index;
843 * Index of peer to connect first to.
845 uint32_t second_index;
848 * Higher level topology connection context.
850 struct ConnectTopologyContext *ct_ctx;
853 * Whether this connection has been accounted for in the schedule_connect call.
858 struct UnblacklistContext
863 struct GNUNET_TESTING_PeerGroup *pg;
866 * uid of the first peer
876 struct GNUNET_TESTING_PeerGroup *pg;
879 * uid of the first peer
884 * Peer data for first peer.
886 struct PeerData *first;
889 * Random percentage to use
894 struct MinimumContext
899 struct GNUNET_TESTING_PeerGroup *pg;
902 * uid of the first peer
907 * Peer data for first peer.
909 struct PeerData *first;
912 * Number of conns per peer
914 unsigned int num_to_add;
917 * Permuted array of all possible connections. Only add the Nth
918 * peer if it's in the Nth position.
920 unsigned int *pg_array;
923 * What number is the current element we are iterating over?
925 unsigned int current;
933 struct GNUNET_TESTING_PeerGroup *pg;
936 * uid of the first peer
941 * uid of the second peer
946 * Peer data for first peer.
948 struct PeerData *first;
951 * Which peer has been chosen as the one to add?
956 * What number is the current element we are iterating over?
958 unsigned int current;
963 * Convert unique ID to hash code.
965 * @param uid unique ID to convert
966 * @param hash set to uid (extended with zeros)
969 hash_from_uid (uint32_t uid, GNUNET_HashCode * hash)
971 memset (hash, 0, sizeof (GNUNET_HashCode));
972 *((uint32_t *) hash) = uid;
976 * Convert hash code to unique ID.
978 * @param uid unique ID to convert
979 * @param hash set to uid (extended with zeros)
982 uid_from_hash (const GNUNET_HashCode * hash, uint32_t * uid)
984 memcpy (uid, hash, sizeof (uint32_t));
989 static struct GNUNET_CORE_MessageHandler no_handlers[] = { {NULL, 0, 0} };
993 * Get a topology from a string input.
995 * @param topology where to write the retrieved topology
996 * @param topology_string The string to attempt to
997 * get a configuration value from
998 * @return GNUNET_YES if topology string matched a
999 * known topology, GNUNET_NO if not
1002 GNUNET_TESTING_topology_get (enum GNUNET_TESTING_Topology *topology,
1003 const char *topology_string)
1006 * Strings representing topologies in enum
1008 static const char *topology_strings[] = {
1010 * A clique (everyone connected to everyone else).
1015 * Small-world network (2d torus plus random links).
1020 * Small-world network (ring plus random links).
1040 * Certain percentage of peers are unable to communicate directly
1041 * replicating NAT conditions
1046 * Scale free topology.
1051 * Straight line topology.
1056 * All peers are disconnected.
1061 * Read the topology from a file.
1069 if (topology_string == NULL)
1071 while (topology_strings[curr] != NULL)
1073 if (strcasecmp (topology_strings[curr], topology_string) == 0)
1080 *topology = GNUNET_TESTING_TOPOLOGY_NONE;
1086 * Get connect topology option from string input.
1088 * @param topology_option where to write the retrieved topology
1089 * @param topology_string The string to attempt to
1090 * get a configuration value from
1091 * @return GNUNET_YES if string matched a known
1092 * topology option, GNUNET_NO if not
1095 GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption
1097 const char *topology_string)
1100 * Options for connecting a topology as strings.
1102 static const char *topology_option_strings[] = {
1104 * Try to connect all peers specified in the topology.
1109 * Choose a random subset of connections to create.
1111 "CONNECT_RANDOM_SUBSET",
1114 * Create at least X connections for each peer.
1119 * Using a depth first search, create one connection
1120 * per peer. If any are missed (graph disconnected)
1121 * start over at those peers until all have at least one
1127 * Find the N closest peers to each allowed peer in the
1128 * topology and make sure a connection to those peers
1129 * exists in the connect topology.
1134 * No options specified.
1142 if (topology_string == NULL)
1144 while (NULL != topology_option_strings[curr])
1146 if (strcasecmp (topology_option_strings[curr], topology_string) == 0)
1148 *topology_option = curr;
1153 *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
1158 * Function to iterate over options. Copies
1159 * the options to the target configuration,
1160 * updating PORT values as needed.
1162 * @param cls closure
1163 * @param section name of the section
1164 * @param option name of the option
1165 * @param value value of the option
1168 update_config (void *cls,
1169 const char *section, const char *option, const char *value)
1171 struct UpdateContext *ctx = cls;
1175 char *single_variable;
1176 char *per_host_variable;
1177 unsigned long long num_per_host;
1179 GNUNET_asprintf (&single_variable, "single_%s_per_host", section);
1180 GNUNET_asprintf (&per_host_variable, "num_%s_per_host", section);
1182 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
1186 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1189 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
1192 else if ((ival != 0) &&
1194 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1195 single_variable)) &&
1196 GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1200 GNUNET_snprintf (cval, sizeof (cval), "%u", ival + ctx->fdnum % num_per_host);
1205 if (0 == strcmp (option, "UNIXPATH"))
1208 GNUNET_CONFIGURATION_get_value_yesno (ctx->orig, "testing",
1211 GNUNET_snprintf (uval,
1213 "/tmp/test-service-%s-%u", section, ctx->upnum++);
1216 else if ((GNUNET_YES ==
1217 GNUNET_CONFIGURATION_get_value_number (ctx->orig, "testing",
1219 &num_per_host)) && (num_per_host > 0))
1222 GNUNET_snprintf (uval,
1224 "/tmp/test-service-%s-%u",
1225 section, ctx->fdnum % num_per_host);
1230 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
1232 value = ctx->hostname;
1234 GNUNET_free (single_variable);
1235 GNUNET_free (per_host_variable);
1236 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
1241 * Create a new configuration using the given configuration
1242 * as a template; however, each PORT in the existing cfg
1243 * must be renumbered by incrementing "*port". If we run
1244 * out of "*port" numbers, return NULL.
1246 * @param cfg template configuration
1247 * @param off the current peer offset
1248 * @param port port numbers to use, update to reflect
1249 * port numbers that were used
1250 * @param upnum number to make unix domain socket names unique
1251 * @param hostname hostname of the controlling host, to allow control connections from
1252 * @param fdnum number used to offset the unix domain socket for grouped processes
1253 * (such as statistics or peerinfo, which can be shared among others)
1255 * @return new configuration, NULL on error
1257 static struct GNUNET_CONFIGURATION_Handle *
1258 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
1261 uint32_t * upnum, const char *hostname,
1264 struct UpdateContext uc;
1267 char *allowed_hosts;
1273 uc.ret = GNUNET_CONFIGURATION_create ();
1274 uc.hostname = hostname;
1277 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
1278 if (uc.nport >= HIGH_PORT)
1281 GNUNET_CONFIGURATION_destroy (uc.ret);
1285 if (GNUNET_CONFIGURATION_get_value_string
1286 (cfg, "testing", "control_host", &control_host) == GNUNET_OK)
1288 if (hostname != NULL)
1289 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1; %s;", control_host,
1292 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", control_host);
1294 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "ACCEPT_FROM",
1296 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport",
1297 "ACCEPT_FROM", allowed_hosts);
1298 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "ACCEPT_FROM",
1300 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics",
1301 "ACCEPT_FROM", allowed_hosts);
1303 GNUNET_CONFIGURATION_set_value_string (uc.ret, "core", "UNIXPATH", "");
1304 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport", "UNIXPATH", "");
1305 GNUNET_CONFIGURATION_set_value_string (uc.ret, "dht", "UNIXPATH", "");
1306 GNUNET_CONFIGURATION_set_value_string (uc.ret, "statistics", "UNIXPATH", "");
1308 GNUNET_free_non_null (control_host);
1309 GNUNET_free (allowed_hosts);
1313 /* arm needs to know to allow connections from the host on which it is running,
1314 * otherwise gnunet-arm is unable to connect to it in some instances */
1315 if (hostname != NULL)
1317 GNUNET_asprintf (&allowed_hosts, "%s; 127.0.0.1;", hostname);
1318 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1319 "BINDTO", hostname);
1320 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1321 "BINDTO", hostname);
1322 GNUNET_CONFIGURATION_set_value_string (uc.ret, "arm", "ACCEPT_FROM",
1324 GNUNET_free (allowed_hosts);
1328 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-tcp",
1329 "BINDTO", "127.0.0.1");
1330 GNUNET_CONFIGURATION_set_value_string (uc.ret, "transport-udp",
1331 "BINDTO", "127.0.0.1");
1334 *port = (uint16_t) uc.nport;
1342 * Add entries to the some list
1344 * @param pg the peer group we are working with
1345 * @param first index of the first peer
1346 * @param second index of the second peer
1347 * @param list the peer list to use
1349 * @return the number of connections added (can be 0, 1 or 2)
1353 remove_connections (struct GNUNET_TESTING_PeerGroup *pg,
1354 unsigned int first, unsigned int second,
1355 enum PeerLists list)
1359 struct PeerConnection **first_list;
1360 struct PeerConnection **second_list;
1361 struct PeerConnection *first_iter;
1362 struct PeerConnection *second_iter;
1363 struct PeerConnection **first_tail;
1364 struct PeerConnection **second_tail;
1367 GNUNET_HashCode hash_first;
1368 GNUNET_HashCode hash_second;
1370 hash_from_uid (first, &hash_first);
1371 hash_from_uid (second, &hash_second);
1379 first_list = &pg->peers[first].allowed_peers_head;
1380 second_list = &pg->peers[second].allowed_peers_head;
1381 first_tail = &pg->peers[first].allowed_peers_tail;
1382 second_tail = &pg->peers[second].allowed_peers_tail;
1385 first_list = &pg->peers[first].connect_peers_head;
1386 second_list = &pg->peers[second].connect_peers_head;
1387 first_tail = &pg->peers[first].connect_peers_tail;
1388 second_tail = &pg->peers[second].connect_peers_tail;
1391 first_list = &pg->peers[first].blacklisted_peers_head;
1392 second_list = &pg->peers[second].blacklisted_peers_head;
1393 first_tail = &pg->peers[first].blacklisted_peers_tail;
1394 second_tail = &pg->peers[second].blacklisted_peers_tail;
1397 first_list = &pg->peers[first].connect_peers_working_set_head;
1398 second_list = &pg->peers[second].connect_peers_working_set_head;
1399 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1400 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1407 first_iter = *first_list;
1408 while (first_iter != NULL)
1410 if (first_iter->index == second)
1412 GNUNET_CONTAINER_DLL_remove(*first_list, *first_tail, first_iter);
1413 GNUNET_free(first_iter);
1417 first_iter = first_iter->next;
1420 second_iter = *second_list;
1421 while (second_iter != NULL)
1423 if (second_iter->index == first)
1425 GNUNET_CONTAINER_DLL_remove(*second_list, *second_tail, second_iter);
1426 GNUNET_free(second_iter);
1430 second_iter = second_iter->next;
1434 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1437 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[first].blacklisted_peers,
1442 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1445 GNUNET_CONTAINER_multihashmap_remove_all (pg->peers[second].blacklisted_peers,
1454 * Add entries to the some list
1456 * @param pg the peer group we are working with
1457 * @param first index of the first peer
1458 * @param second index of the second peer
1459 * @param list the list type that we should modify
1461 * @return the number of connections added (can be 0, 1 or 2)
1465 add_connections (struct GNUNET_TESTING_PeerGroup *pg,
1466 unsigned int first, unsigned int second,
1467 enum PeerLists list)
1473 struct PeerConnection **first_list;
1474 struct PeerConnection **second_list;
1475 struct PeerConnection *first_iter;
1476 struct PeerConnection *second_iter;
1477 struct PeerConnection *new_first;
1478 struct PeerConnection *new_second;
1479 struct PeerConnection **first_tail;
1480 struct PeerConnection **second_tail;
1482 GNUNET_HashCode hash_first;
1483 GNUNET_HashCode hash_second;
1485 hash_from_uid (first, &hash_first);
1486 hash_from_uid (second, &hash_second);
1493 first_list = &pg->peers[first].allowed_peers_head;
1494 second_list = &pg->peers[second].allowed_peers_head;
1495 first_tail = &pg->peers[first].allowed_peers_tail;
1496 second_tail = &pg->peers[second].allowed_peers_tail;
1499 first_list = &pg->peers[first].connect_peers_head;
1500 second_list = &pg->peers[second].connect_peers_head;
1501 first_tail = &pg->peers[first].connect_peers_tail;
1502 second_tail = &pg->peers[second].connect_peers_tail;
1505 first_list = &pg->peers[first].blacklisted_peers_head;
1506 second_list = &pg->peers[second].blacklisted_peers_head;
1507 first_tail = &pg->peers[first].blacklisted_peers_tail;
1508 second_tail = &pg->peers[second].blacklisted_peers_tail;
1511 first_list = &pg->peers[first].connect_peers_working_set_head;
1512 second_list = &pg->peers[second].connect_peers_working_set_head;
1513 first_tail = &pg->peers[first].connect_peers_working_set_tail;
1514 second_tail = &pg->peers[second].connect_peers_working_set_tail;
1521 add_first = GNUNET_YES;
1522 add_second = GNUNET_YES;
1524 first_iter = *first_list;
1525 while (first_iter != NULL)
1527 if (first_iter->index == second)
1529 add_first = GNUNET_NO;
1532 first_iter = first_iter->next;
1535 second_iter = *second_list;
1536 while (second_iter != NULL)
1538 if (second_iter->index == first)
1540 add_second = GNUNET_NO;
1543 second_iter = second_iter->next;
1547 GNUNET_CONTAINER_multihashmap_contains (pg->peers[first].blacklisted_peers,
1550 add_first = GNUNET_YES;
1554 GNUNET_CONTAINER_multihashmap_contains (pg->peers[second].blacklisted_peers,
1557 add_second = GNUNET_YES;
1565 new_first = GNUNET_malloc (sizeof (struct PeerConnection));
1566 new_first->index = second;
1567 GNUNET_CONTAINER_DLL_insert(*first_list, *first_tail, new_first);
1569 GNUNET_assert (GNUNET_OK ==
1570 GNUNET_CONTAINER_multihashmap_put (pg->
1572 [first].blacklisted_peers,
1575 peers[second].daemon,
1576 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1578 pg->peers[first].num_connections++;
1585 new_second = GNUNET_malloc (sizeof (struct PeerConnection));
1586 new_second->index = first;
1587 GNUNET_CONTAINER_DLL_insert(*second_list, *second_tail, new_second);
1589 GNUNET_assert (GNUNET_OK ==
1590 GNUNET_CONTAINER_multihashmap_put (pg->
1592 [second].blacklisted_peers,
1595 peers[first].daemon,
1596 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1598 pg->peers[second].num_connections++;
1607 * Scale free network construction as described in:
1609 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
1611 * Start with a network of "one" peer, then progressively add
1612 * peers up to the total number. At each step, iterate over
1613 * all possible peers and connect new peer based on number of
1614 * existing connections of the target peer.
1616 * @param pg the peer group we are dealing with
1617 * @param proc the connection processor to use
1618 * @param list the peer list to use
1620 * @return the number of connections created
1623 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg,
1624 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1627 unsigned int total_connections;
1628 unsigned int outer_count;
1630 unsigned int previous_total_connections;
1634 GNUNET_assert (pg->total > 1);
1636 /* Add a connection between the first two nodes */
1637 total_connections = proc (pg, 0, 1, list);
1639 for (outer_count = 1; outer_count < pg->total; outer_count++)
1641 previous_total_connections = total_connections;
1642 for (i = 0; i < outer_count; i++)
1645 pg->peers[i].num_connections /
1646 (double) previous_total_connections;
1649 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1650 UINT64_MAX)) / ((double) UINT64_MAX);
1652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1653 "Considering connecting peer %d to peer %d\n",
1656 if (random < probability)
1659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1660 "Connecting peer %d to peer %d\n", outer_count, i);
1662 total_connections += proc (pg, outer_count, i, list);
1667 return total_connections;
1671 * Create a topology given a peer group (set of running peers)
1672 * and a connection processor. Creates a small world topology
1673 * according to the rewired ring construction. The basic
1674 * behavior is that a ring topology is created, but with some
1675 * probability instead of connecting a peer to the next
1676 * neighbor in the ring a connection will be created to a peer
1677 * selected uniformly at random. We use the TESTING
1678 * PERCENTAGE option to specify what number of
1679 * connections each peer should have. Default is 2,
1680 * which makes the ring, any given number is multiplied by
1681 * the log of the network size; i.e. a PERCENTAGE of 2 makes
1682 * each peer have on average 2logn connections. The additional
1683 * connections are made at increasing distance around the ring
1684 * from the original peer, or to random peers based on the re-
1685 * wiring probability. The TESTING
1686 * PROBABILITY option is used as the probability that a given
1687 * connection is rewired.
1689 * @param pg the peergroup to create the topology on
1690 * @param proc the connection processor to call to actually set
1691 * up connections between two peers
1692 * @param list the peer list to use
1694 * @return the number of connections that were set up
1698 create_small_world_ring (struct GNUNET_TESTING_PeerGroup *pg,
1699 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1703 unsigned int natLog;
1704 unsigned int randomPeer;
1705 double random, logNModifier, probability;
1706 unsigned int smallWorldConnections;
1711 unsigned int useAnd;
1712 int connect_attempts;
1714 logNModifier = 0.5; /* FIXME: default value? */
1715 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1720 if (sscanf (p_string, "%lf", &logNModifier) != 1)
1721 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1723 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1724 p_string, "LOGNMODIFIER", "TESTING");
1725 GNUNET_free (p_string);
1727 probability = 0.5; /* FIXME: default percentage? */
1728 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1733 if (sscanf (p_string, "%lf", &probability) != 1)
1734 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1736 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1737 p_string, "PERCENTAGE", "TESTING");
1738 GNUNET_free (p_string);
1740 natLog = log (pg->total);
1741 connsPerPeer = ceil (natLog * logNModifier);
1743 if (connsPerPeer % 2 == 1)
1746 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Target is %d connections per peer."), connsPerPeer);
1748 smallWorldConnections = 0;
1749 connect_attempts = 0;
1750 for (i = 0; i < pg->total; i++)
1753 max = i + connsPerPeer / 2;
1754 min = i - connsPerPeer / 2;
1756 if (max > pg->total - 1)
1758 max = max - pg->total;
1764 min = pg->total - 1 + min;
1768 for (j = 0; j < connsPerPeer / 2; j++)
1772 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
1773 UINT64_MAX) / ((double) UINT64_MAX));
1774 if (random < probability)
1776 /* Connect to uniformly selected random peer */
1778 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1780 while ((((randomPeer < max) && (randomPeer > min))
1781 && (useAnd == 0)) || (((randomPeer > min)
1782 || (randomPeer < max))
1786 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1789 smallWorldConnections += proc (pg, i, randomPeer, list);
1793 nodeToConnect = i + j + 1;
1794 if (nodeToConnect > pg->total - 1)
1796 nodeToConnect = nodeToConnect - pg->total;
1798 connect_attempts += proc (pg, i, nodeToConnect, list);
1804 connect_attempts += smallWorldConnections;
1806 return connect_attempts;
1810 * Create a topology given a peer group (set of running peers)
1811 * and a connection processor.
1813 * @param pg the peergroup to create the topology on
1814 * @param proc the connection processor to call to actually set
1815 * up connections between two peers
1816 * @param list the peer list to use
1818 * @return the number of connections that were set up
1822 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg,
1823 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1825 unsigned int outer_count, inner_count;
1826 unsigned int cutoff;
1827 int connect_attempts;
1828 double nat_percentage;
1831 nat_percentage = 0.6; /* FIXME: default percentage? */
1832 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1837 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1838 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1840 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1841 p_string, "PERCENTAGE", "TESTING");
1842 GNUNET_free (p_string);
1845 cutoff = (unsigned int) (nat_percentage * pg->total);
1846 connect_attempts = 0;
1847 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1849 for (inner_count = outer_count + 1; inner_count < pg->total;
1852 if ((outer_count > cutoff) || (inner_count > cutoff))
1855 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1856 "Connecting peer %d to peer %d\n",
1857 outer_count, inner_count);
1859 connect_attempts += proc (pg, outer_count, inner_count, list);
1863 return connect_attempts;
1868 * Create a topology given a peer group (set of running peers)
1869 * and a connection processor.
1871 * @param pg the peergroup to create the topology on
1872 * @param proc the connection processor to call to actually set
1873 * up connections between two peers
1874 * @param list the peer list to use
1876 * @return the number of connections that were set up
1880 create_nated_internet_copy (struct GNUNET_TESTING_PeerGroup *pg,
1881 GNUNET_TESTING_ConnectionProcessor proc,
1882 enum PeerLists list)
1884 unsigned int outer_count, inner_count;
1885 unsigned int cutoff;
1886 int connect_attempts;
1887 double nat_percentage;
1890 nat_percentage = 0.6; /* FIXME: default percentage? */
1891 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1896 if (sscanf (p_string, "%lf", &nat_percentage) != 1)
1897 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1899 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1900 p_string, "PERCENTAGE", "TESTING");
1901 GNUNET_free (p_string);
1904 cutoff = (unsigned int) (nat_percentage * pg->total);
1905 connect_attempts = 0;
1906 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1908 for (inner_count = outer_count + 1; inner_count < pg->total;
1911 if ((outer_count > cutoff) || (inner_count > cutoff))
1914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1915 "Connecting peer %d to peer %d\n",
1916 outer_count, inner_count);
1918 connect_attempts += proc (pg, outer_count, inner_count, list);
1919 add_connections(pg, outer_count, inner_count, ALLOWED);
1924 return connect_attempts;
1929 * Create a topology given a peer group (set of running peers)
1930 * and a connection processor.
1932 * @param pg the peergroup to create the topology on
1933 * @param proc the connection processor to call to actually set
1934 * up connections between two peers
1935 * @param list the peer list to use
1937 * @return the number of connections that were set up
1941 create_small_world (struct GNUNET_TESTING_PeerGroup *pg,
1942 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
1944 unsigned int i, j, k;
1945 unsigned int square;
1948 unsigned int toggle = 1;
1949 unsigned int nodeToConnect;
1950 unsigned int natLog;
1951 unsigned int node1Row;
1952 unsigned int node1Col;
1953 unsigned int node2Row;
1954 unsigned int node2Col;
1955 unsigned int distance;
1956 double probability, random, percentage;
1957 unsigned int smallWorldConnections;
1958 unsigned int small_world_it;
1960 int connect_attempts;
1961 square = floor (sqrt (pg->total));
1965 percentage = 0.5; /* FIXME: default percentage? */
1966 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1971 if (sscanf (p_string, "%lf", &percentage) != 1)
1972 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1974 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1975 p_string, "PERCENTAGE", "TESTING");
1976 GNUNET_free (p_string);
1978 if (percentage < 0.0)
1980 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1982 ("Invalid value `%s' for option `%s' in section `%s': got %f, needed value greater than 0\n"),
1983 "PERCENTAGE", "TESTING", percentage);
1986 probability = 0.5; /* FIXME: default percentage? */
1987 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
1992 if (sscanf (p_string, "%lf", &probability) != 1)
1993 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1995 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1996 p_string, "PROBABILITY", "TESTING");
1997 GNUNET_free (p_string);
1999 if (square * square != pg->total)
2001 while (rows * cols < pg->total)
2003 if (toggle % 2 == 0)
2012 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2014 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2018 connect_attempts = 0;
2019 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2020 * to the node to its right and above. Once this is over, we'll have our torus!
2021 * Special case for the last node (if the rows and columns are not equal), connect
2022 * to the first in the row to maintain topology.
2024 for (i = 0; i < pg->total; i++)
2026 /* First connect to the node to the right */
2027 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2028 nodeToConnect = i + 1;
2029 else if (i + 1 == pg->total)
2030 nodeToConnect = rows * cols - cols;
2032 nodeToConnect = i - cols + 1;
2034 connect_attempts += proc (pg, i, nodeToConnect, list);
2038 nodeToConnect = (rows * cols) - cols + i;
2039 if (nodeToConnect >= pg->total)
2040 nodeToConnect -= cols;
2043 nodeToConnect = i - cols;
2045 if (nodeToConnect < pg->total)
2046 connect_attempts += proc (pg, i, nodeToConnect, list);
2048 natLog = log (pg->total);
2049 #if VERBOSE_TESTING > 2
2050 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2051 _("natural log of %d is %d, will run %d iterations\n"),
2052 pg->total, natLog, (int) (natLog * percentage));
2053 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2054 _("Total connections added thus far: %u!\n"), connect_attempts);
2056 smallWorldConnections = 0;
2057 small_world_it = (unsigned int) (natLog * percentage);
2058 if (small_world_it < 1)
2060 GNUNET_assert (small_world_it > 0 && small_world_it < (unsigned int) -1);
2061 for (i = 0; i < small_world_it; i++)
2063 for (j = 0; j < pg->total; j++)
2065 /* Determine the row and column of node at position j on the 2d torus */
2066 node1Row = j / cols;
2067 node1Col = j - (node1Row * cols);
2068 for (k = 0; k < pg->total; k++)
2070 /* Determine the row and column of node at position k on the 2d torus */
2071 node2Row = k / cols;
2072 node2Col = k - (node2Row * cols);
2073 /* Simple Cartesian distance */
2075 abs (node1Row - node2Row) + abs (node1Col - node2Col);
2078 /* Calculate probability as 1 over the square of the distance */
2079 probability = 1.0 / (distance * distance);
2080 /* Choose a random value between 0 and 1 */
2083 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2085 ((double) UINT64_MAX);
2086 /* If random < probability, then connect the two nodes */
2087 if (random < probability)
2088 smallWorldConnections += proc (pg, j, k, list);
2094 connect_attempts += smallWorldConnections;
2095 #if VERBOSE_TESTING > 2
2096 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2097 _("Total connections added for small world: %d!\n"),
2098 smallWorldConnections);
2100 return connect_attempts;
2104 * Create a topology given a peer group (set of running peers)
2105 * and a connection processor.
2107 * @param pg the peergroup to create the topology on
2108 * @param proc the connection processor to call to actually set
2109 * up connections between two peers
2110 * @param list the peer list to use
2112 * @return the number of connections that were set up
2116 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg,
2117 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2120 unsigned int outer_count;
2121 unsigned int inner_count;
2122 int connect_attempts;
2126 probability = 0.5; /* FIXME: default percentage? */
2127 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (pg->cfg,
2132 if (sscanf (p_string, "%lf", &probability) != 1)
2133 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2135 ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
2136 p_string, "PROBABILITY", "TESTING");
2137 GNUNET_free (p_string);
2139 connect_attempts = 0;
2140 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2142 for (inner_count = outer_count + 1; inner_count < pg->total;
2147 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
2148 UINT64_MAX)) / ((double) UINT64_MAX);
2150 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2151 _("rand is %f probability is %f\n"), temp_rand,
2154 if (temp_rand < probability)
2156 connect_attempts += proc (pg, outer_count, inner_count, list);
2161 return connect_attempts;
2165 * Create a topology given a peer group (set of running peers)
2166 * and a connection processor. This particular function creates
2167 * the connections for a 2d-torus, plus additional "closest"
2168 * connections per peer.
2170 * @param pg the peergroup to create the topology on
2171 * @param proc the connection processor to call to actually set
2172 * up connections between two peers
2173 * @param list the peer list to use
2175 * @return the number of connections that were set up
2179 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg,
2180 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2183 unsigned int square;
2186 unsigned int toggle = 1;
2187 unsigned int nodeToConnect;
2188 int connect_attempts;
2190 connect_attempts = 0;
2192 square = floor (sqrt (pg->total));
2196 if (square * square != pg->total)
2198 while (rows * cols < pg->total)
2200 if (toggle % 2 == 0)
2209 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2211 ("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
2214 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
2215 * to the node to its right and above. Once this is over, we'll have our torus!
2216 * Special case for the last node (if the rows and columns are not equal), connect
2217 * to the first in the row to maintain topology.
2219 for (i = 0; i < pg->total; i++)
2221 /* First connect to the node to the right */
2222 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
2223 nodeToConnect = i + 1;
2224 else if (i + 1 == pg->total)
2225 nodeToConnect = rows * cols - cols;
2227 nodeToConnect = i - cols + 1;
2229 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2230 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2232 connect_attempts += proc (pg, i, nodeToConnect, list);
2234 /* Second connect to the node immediately above */
2237 nodeToConnect = (rows * cols) - cols + i;
2238 if (nodeToConnect >= pg->total)
2239 nodeToConnect -= cols;
2242 nodeToConnect = i - cols;
2244 if (nodeToConnect < pg->total)
2247 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2248 "Connecting peer %d to peer %d\n", i, nodeToConnect);
2250 connect_attempts += proc (pg, i, nodeToConnect, list);
2255 return connect_attempts;
2260 * Create a topology given a peer group (set of running peers)
2261 * and a connection processor.
2263 * @param pg the peergroup to create the topology on
2264 * @param proc the connection processor to call to actually set
2265 * up connections between two peers
2266 * @param list the peer list to use
2268 * @return the number of connections that were set up
2272 create_clique (struct GNUNET_TESTING_PeerGroup *pg,
2273 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2275 unsigned int outer_count;
2276 unsigned int inner_count;
2277 int connect_attempts;
2279 connect_attempts = 0;
2281 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
2283 for (inner_count = outer_count + 1; inner_count < pg->total;
2287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2288 "Connecting peer %d to peer %d\n",
2289 outer_count, inner_count);
2291 connect_attempts += proc (pg, outer_count, inner_count, list);
2295 return connect_attempts;
2300 * Iterator over hash map entries.
2302 * @param cls closure the peer group
2303 * @param key the key stored in the hashmap is the
2304 * index of the peer to connect to
2305 * @param value value in the hash map, handle to the peer daemon
2306 * @return GNUNET_YES if we should continue to
2311 unblacklist_iterator (void *cls,
2312 const GNUNET_HashCode * key,
2315 struct UnblacklistContext *un_ctx = cls;
2316 uint32_t second_pos;
2318 uid_from_hash (key, &second_pos);
2320 unblacklist_connections(un_ctx->pg, un_ctx->first_uid, second_pos);
2327 * Create a blacklist topology based on the allowed topology
2328 * which disallows any connections not in the allowed topology
2329 * at the transport level.
2331 * @param pg the peergroup to create the topology on
2332 * @param proc the connection processor to call to allow
2333 * up connections between two peers
2335 * @return the number of connections that were set up
2339 copy_allowed (struct GNUNET_TESTING_PeerGroup *pg,
2340 GNUNET_TESTING_ConnectionProcessor proc)
2342 struct UnblacklistContext un_ctx;
2345 struct PeerConnection *iter;
2349 for (count = 0; count < pg->total - 1; count++)
2351 un_ctx.first_uid = count;
2353 iter = pg->peers[count].allowed_peers_head;
2354 while (iter != NULL)
2356 remove_connections(pg, count, iter->index, BLACKLIST);
2357 //unblacklist_connections(pg, count, iter->index);
2361 total += GNUNET_CONTAINER_multihashmap_iterate(pg->peers[count].allowed_peers, &unblacklist_iterator, &un_ctx);
2364 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Unblacklisted %u peers\n", total);
2370 * Create a topology given a peer group (set of running peers)
2371 * and a connection processor.
2373 * @param pg the peergroup to create the topology on
2374 * @param proc the connection processor to call to actually set
2375 * up connections between two peers
2376 * @param list which list should be modified
2378 * @return the number of connections that were set up
2382 create_line (struct GNUNET_TESTING_PeerGroup *pg,
2383 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2386 int connect_attempts;
2388 connect_attempts = 0;
2390 /* Connect each peer to the next highest numbered peer */
2391 for (count = 0; count < pg->total - 1; count++)
2394 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2395 "Connecting peer %d to peer %d\n", count, count + 1);
2397 connect_attempts += proc (pg, count, count + 1, list);
2400 return connect_attempts;
2404 * Create a topology given a peer group (set of running peers)
2405 * and a connection processor.
2407 * @param pg the peergroup to create the topology on
2408 * @param filename the file to read topology information from
2409 * @param proc the connection processor to call to actually set
2410 * up connections between two peers
2411 * @param list the peer list to use
2413 * @return the number of connections that were set up
2417 create_from_file (struct GNUNET_TESTING_PeerGroup *pg,
2419 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2421 int connect_attempts;
2422 unsigned int first_peer_index;
2423 unsigned int second_peer_index;
2424 connect_attempts = 0;
2429 unsigned int total_peers;
2431 enum States curr_state;
2433 if (GNUNET_OK != GNUNET_DISK_file_test (filename))
2434 GNUNET_DISK_fn_write (filename, NULL, 0, GNUNET_DISK_PERM_USER_READ);
2436 if ((0 != STAT (filename, &frstat)) || (frstat.st_size == 0))
2438 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2439 "Could not open file `%s' specified for topology!", filename);
2440 return connect_attempts;
2443 data = GNUNET_malloc_large (frstat.st_size);
2444 GNUNET_assert(data != NULL);
2445 if (frstat.st_size !=
2446 GNUNET_DISK_fn_read (filename, data, frstat.st_size))
2448 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2449 "Could not read file %s specified for host list, ending test!", filename);
2451 return connect_attempts;
2456 /* First line should contain a single integer, specifying the number of peers */
2457 /* Each subsequent line should contain this format PEER_INDEX:OTHER_PEER_INDEX[,...] */
2458 curr_state = NUM_PEERS;
2459 while (count < frstat.st_size - 1)
2461 if ((buf[count] == '\n') || (buf[count] == ' '))
2470 if (1 != sscanf(&buf[count], "%u", &total_peers))
2472 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read number of peers from topology file!\n");
2473 GNUNET_free_non_null(data);
2474 return connect_attempts;
2476 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Read %u total peers in topology\n", total_peers);
2477 GNUNET_assert(total_peers == pg->total);
2478 curr_state = PEER_INDEX;
2479 while((buf[count] != '\n') && (count < frstat.st_size - 1))
2484 if (1 != sscanf(&buf[count], "%u", &first_peer_index))
2486 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to read peer index from topology file!\n");
2487 GNUNET_free_non_null(data);
2488 return connect_attempts;
2490 while((buf[count] != ':') && (count < frstat.st_size - 1))
2493 curr_state = OTHER_PEER_INDEX;
2496 if (1 == sscanf(&buf[count], ":"))
2497 curr_state = OTHER_PEER_INDEX;
2500 case OTHER_PEER_INDEX:
2501 if (1 != sscanf(&buf[count], "%u", &second_peer_index))
2503 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Failed to peer index from topology file!\n");
2504 GNUNET_free_non_null(data);
2505 return connect_attempts;
2507 /* Assume file is written with first peer 1, but array index is 0 */
2508 connect_attempts += proc (pg, first_peer_index - 1, second_peer_index - 1, list);
2509 while((buf[count] != '\n') && (buf[count] != ',') && (count < frstat.st_size - 1))
2511 if (buf[count] == '\n')
2513 curr_state = PEER_INDEX;
2515 else if (buf[count] != ',')
2517 curr_state = OTHER_PEER_INDEX;
2522 GNUNET_log(GNUNET_ERROR_TYPE_ERROR, "Found bad data in topology file while in state %d!\n", curr_state);
2524 return connect_attempts;
2529 /* Connect each peer to the next highest numbered peer */
2530 for (count = 0; count < pg->total - 1; count++)
2533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2534 "Connecting peer %d to peer %d\n", first_peer_index, second_peer_index);
2536 connect_attempts += proc (pg, first_peer_index, second_peer_index);
2539 return connect_attempts;
2543 * Create a topology given a peer group (set of running peers)
2544 * and a connection processor.
2546 * @param pg the peergroup to create the topology on
2547 * @param proc the connection processor to call to actually set
2548 * up connections between two peers
2549 * @param list the peer list to use
2551 * @return the number of connections that were set up
2555 create_ring (struct GNUNET_TESTING_PeerGroup *pg,
2556 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
2559 int connect_attempts;
2561 connect_attempts = 0;
2563 /* Connect each peer to the next highest numbered peer */
2564 for (count = 0; count < pg->total - 1; count++)
2567 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2568 "Connecting peer %d to peer %d\n", count, count + 1);
2570 connect_attempts += proc (pg, count, count + 1, list);
2573 /* Connect the last peer to the first peer */
2574 connect_attempts += proc (pg, pg->total - 1, 0, list);
2576 return connect_attempts;
2581 * Iterator for writing friends of a peer to a file.
2583 * @param cls closure, an open writable file handle
2584 * @param key the key the daemon was stored under
2585 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2587 * @return GNUNET_YES to continue iteration
2589 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
2590 * with a single file_iterator that takes a closure which contains
2591 * the prefix to write before the peer. Then this could be used
2592 * for blacklisting multiple transports and writing the friend
2593 * file. I'm sure *someone* will complain loudly about other
2594 * things that negate these functions even existing so no point in
2598 friend_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2600 FILE *temp_friend_handle = cls;
2601 struct GNUNET_TESTING_Daemon *peer = value;
2602 struct GNUNET_PeerIdentity *temppeer;
2603 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2605 temppeer = &peer->id;
2606 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2607 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2612 struct BlacklistContext
2615 * The (open) file handle to write to
2617 FILE *temp_file_handle;
2620 * The transport that this peer will be blacklisted on.
2626 * Iterator for writing blacklist data to appropriate files.
2628 * @param cls closure, an open writable file handle
2629 * @param key the key the daemon was stored under
2630 * @param value the GNUNET_TESTING_Daemon that needs to be written.
2632 * @return GNUNET_YES to continue iteration
2635 blacklist_file_iterator (void *cls, const GNUNET_HashCode * key, void *value)
2637 struct BlacklistContext *blacklist_ctx = cls;
2638 struct GNUNET_TESTING_Daemon *peer = value;
2639 struct GNUNET_PeerIdentity *temppeer;
2640 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2642 temppeer = &peer->id;
2643 GNUNET_CRYPTO_hash_to_enc (&temppeer->hashPubKey, &peer_enc);
2644 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Writing entry %s:%s to file\n", blacklist_ctx->transport, (char *) &peer_enc);
2645 fprintf (blacklist_ctx->temp_file_handle, "%s:%s\n",
2646 blacklist_ctx->transport, (char *) &peer_enc);
2654 * Create the friend files based on the PeerConnection's
2655 * of each peer in the peer group, and copy the files
2656 * to the appropriate place
2658 * @param pg the peer group we are dealing with
2661 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
2663 FILE *temp_friend_handle;
2664 unsigned int pg_iter;
2665 char *temp_service_path;
2666 struct GNUNET_OS_Process **procarr;
2670 enum GNUNET_OS_ProcessStatusType type;
2671 unsigned long return_code;
2679 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2680 struct PeerConnection *conn_iter;
2682 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2683 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2685 mytemp = GNUNET_DISK_mktemp ("friends");
2686 GNUNET_assert (mytemp != NULL);
2687 temp_friend_handle = fopen (mytemp, "wt");
2688 GNUNET_assert (temp_friend_handle != NULL);
2690 conn_iter = pg->peers[pg_iter].allowed_peers_head;
2691 while (conn_iter != NULL)
2693 GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon->id.hashPubKey, &peer_enc);
2694 fprintf (temp_friend_handle, "%s\n", (char *) &peer_enc);
2695 conn_iter = conn_iter->next;
2698 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].allowed_peers,
2699 &friend_file_iterator,
2700 temp_friend_handle);
2702 fclose (temp_friend_handle);
2705 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2706 daemon->cfg, "PATHS",
2708 &temp_service_path))
2710 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2712 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2713 "SERVICEHOME", "PATHS");
2714 if (UNLINK (mytemp) != 0)
2715 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2717 GNUNET_free (mytemp);
2721 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2723 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
2724 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2728 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2729 _("Copying file with command cp %s %s\n"), mytemp, arg);
2731 ret = GNUNET_OS_process_wait(procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2732 GNUNET_OS_process_close (procarr[pg_iter]);
2735 else /* Remote, scp the file to the correct place */
2737 if (NULL != pg->peers[pg_iter].daemon->username)
2738 GNUNET_asprintf (&arg, "%s@%s:%s/friends",
2739 pg->peers[pg_iter].daemon->username,
2740 pg->peers[pg_iter].daemon->hostname,
2743 GNUNET_asprintf (&arg, "%s:%s/friends",
2744 pg->peers[pg_iter].daemon->hostname,
2747 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2750 ret = GNUNET_OS_process_wait(procarr[pg_iter]); /* FIXME: schedule this, throttle! */
2751 GNUNET_OS_process_close (procarr[pg_iter]);
2752 if (ret != GNUNET_OK)
2754 procarr[pg_iter] = NULL;
2756 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2757 _("Copying file with command scp %s %s\n"), mytemp,
2762 GNUNET_free (temp_service_path);
2763 GNUNET_free (mytemp);
2768 ret = GNUNET_SYSERR;
2769 while ((count < max_wait) && (ret != GNUNET_OK))
2772 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2775 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2776 _("Checking copy status of file %d\n"), pg_iter);
2778 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2780 if (GNUNET_OS_process_status
2781 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2783 ret = GNUNET_SYSERR;
2785 else if ((type != GNUNET_OS_PROCESS_EXITED)
2786 || (return_code != 0))
2788 ret = GNUNET_SYSERR;
2792 GNUNET_OS_process_close (procarr[pg_iter]);
2793 procarr[pg_iter] = NULL;
2795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2796 _("File %d copied\n"), pg_iter);
2802 if (ret == GNUNET_SYSERR)
2804 /* FIXME: why sleep here? -CG */
2810 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2811 _("Finished copying all friend files!\n"));
2814 GNUNET_free (procarr);
2820 * Create the blacklist files based on the PeerConnection's
2821 * of each peer in the peer group, and copy the files
2822 * to the appropriate place.
2824 * @param pg the peer group we are dealing with
2825 * @param transports space delimited list of transports to blacklist
2828 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg,
2829 const char *transports)
2831 FILE *temp_file_handle;
2832 unsigned int pg_iter;
2833 char *temp_service_path;
2834 struct GNUNET_OS_Process **procarr;
2837 enum GNUNET_OS_ProcessStatusType type;
2838 unsigned long return_code;
2845 char *temp_transports;
2848 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
2849 struct PeerConnection *conn_iter;
2851 static struct BlacklistContext blacklist_ctx;
2854 procarr = GNUNET_malloc (sizeof (struct GNUNET_OS_Process *) * pg->total);
2855 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2857 mytemp = GNUNET_DISK_mktemp ("blacklist");
2858 GNUNET_assert (mytemp != NULL);
2859 temp_file_handle = fopen (mytemp, "wt");
2860 GNUNET_assert (temp_file_handle != NULL);
2861 temp_transports = GNUNET_strdup (transports);
2863 blacklist_ctx.temp_file_handle = temp_file_handle;
2865 transport_len = strlen (temp_transports) + 1;
2868 for (i = 0; i < transport_len; i++)
2870 if ((temp_transports[i] == ' ') && (pos == NULL))
2871 continue; /* At start of string (whitespace) */
2872 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
2874 temp_transports[i] = '\0';
2876 conn_iter = pg->peers[pg_iter].blacklisted_peers_head;
2877 while (conn_iter != NULL)
2879 GNUNET_CRYPTO_hash_to_enc (&pg->peers[conn_iter->index].daemon->id.hashPubKey, &peer_enc);
2880 fprintf (temp_file_handle, "%s:%s\n", pos, (char *) &peer_enc);
2881 conn_iter = conn_iter->next;
2885 blacklist_ctx.transport = pos;
2886 entry_count = GNUNET_CONTAINER_multihashmap_iterate (pg->
2888 [pg_iter].blacklisted_peers,
2889 &blacklist_file_iterator,
2893 } /* At beginning of actual string */
2894 else if (pos == NULL)
2896 pos = &temp_transports[i];
2900 GNUNET_free (temp_transports);
2901 fclose (temp_file_handle);
2904 GNUNET_CONFIGURATION_get_value_string (pg->peers[pg_iter].
2905 daemon->cfg, "PATHS",
2907 &temp_service_path))
2909 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2911 ("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
2912 "SERVICEHOME", "PATHS");
2913 if (UNLINK (mytemp) != 0)
2914 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink",
2916 GNUNET_free (mytemp);
2920 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
2922 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
2923 procarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
2927 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2928 _("Copying file with command cp %s %s\n"), mytemp, arg);
2933 else /* Remote, scp the file to the correct place */
2935 if (NULL != pg->peers[pg_iter].daemon->username)
2936 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist",
2937 pg->peers[pg_iter].daemon->username,
2938 pg->peers[pg_iter].daemon->hostname,
2941 GNUNET_asprintf (&arg, "%s:%s/blacklist",
2942 pg->peers[pg_iter].daemon->hostname,
2945 GNUNET_OS_start_process (NULL, NULL, "scp", "scp", mytemp, arg,
2948 GNUNET_OS_process_wait(procarr[pg_iter]); /* FIXME: add scheduled blacklist file copy that parallelizes file copying! */
2951 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2952 _("Copying file with command scp %s %s\n"), mytemp,
2957 GNUNET_free (temp_service_path);
2958 GNUNET_free (mytemp);
2962 ret = GNUNET_SYSERR;
2963 while ((count < max_wait) && (ret != GNUNET_OK))
2966 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2969 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2970 _("Checking copy status of file %d\n"), pg_iter);
2972 if (procarr[pg_iter] != NULL) /* Check for already completed! */
2974 if (GNUNET_OS_process_status
2975 (procarr[pg_iter], &type, &return_code) != GNUNET_OK)
2977 ret = GNUNET_SYSERR;
2979 else if ((type != GNUNET_OS_PROCESS_EXITED)
2980 || (return_code != 0))
2982 ret = GNUNET_SYSERR;
2986 GNUNET_OS_process_close (procarr[pg_iter]);
2987 procarr[pg_iter] = NULL;
2989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2990 _("File %d copied\n"), pg_iter);
2996 if (ret == GNUNET_SYSERR)
2998 /* FIXME: why sleep here? -CG */
3004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3005 _("Finished copying all blacklist files!\n"));
3007 GNUNET_free (procarr);
3011 /* Forward Declaration */
3013 schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3016 * Choose a random peer's next connection to create, and
3017 * call schedule_connect to set up the connect task.
3019 * @param ct_ctx the overall connection context
3021 static void preschedule_connect(struct GNUNET_TESTING_PeerGroup *pg)
3023 struct ConnectTopologyContext *ct_ctx = &pg->ct_ctx;
3024 struct PeerConnection *connection_iter;
3025 struct ConnectContext *connect_context;
3026 uint32_t random_peer;
3028 if (ct_ctx->remaining_connections == 0)
3030 random_peer = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, pg->total);
3031 while (pg->peers[random_peer].connect_peers_head == NULL)
3032 random_peer = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, pg->total);
3034 connection_iter = pg->peers[random_peer].connect_peers_head;
3035 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3036 connect_context->first_index = random_peer;
3037 connect_context->second_index = connection_iter->index;
3038 connect_context->ct_ctx = ct_ctx;
3039 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3040 GNUNET_CONTAINER_DLL_remove(pg->peers[random_peer].connect_peers_head, pg->peers[random_peer].connect_peers_tail, connection_iter);
3041 GNUNET_free(connection_iter);
3042 ct_ctx->remaining_connections--;
3046 /* Forward declaration */
3047 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
3051 * Close connections and free the hello context.
3053 * @param cls the 'struct SendHelloContext *'
3054 * @param tc scheduler context
3057 free_hello_context (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3059 struct SendHelloContext *send_hello_context = cls;
3060 if (send_hello_context->peer->daemon->server != NULL)
3062 GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3063 send_hello_context->peer->daemon->server = NULL;
3065 if (send_hello_context->peer->daemon->th != NULL)
3067 GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3068 send_hello_context->peer->daemon->th = NULL;
3070 if (send_hello_context->core_connect_task != GNUNET_SCHEDULER_NO_TASK)
3072 GNUNET_SCHEDULER_cancel(send_hello_context->core_connect_task);
3073 send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3075 send_hello_context->pg->outstanding_connects--;
3076 GNUNET_free(send_hello_context);
3080 * For peers that haven't yet connected, notify
3081 * the caller that they have failed (timeout).
3083 * @param cls the 'struct SendHelloContext *'
3084 * @param tc scheduler context
3087 notify_remaining_connections_failed (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3089 struct SendHelloContext *send_hello_context = cls;
3090 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3091 struct PeerConnection *connection;
3093 GNUNET_CORE_disconnect(send_hello_context->peer->daemon->server);
3094 send_hello_context->peer->daemon->server = NULL;
3096 connection = send_hello_context->peer->connect_peers_head;
3098 while (connection != NULL)
3100 if (pg->notify_connection != NULL)
3102 pg->notify_connection(pg->notify_connection_cls,
3103 &send_hello_context->peer->daemon->id,
3104 &pg->peers[connection->index].daemon->id,
3106 send_hello_context->peer->daemon->cfg,
3107 pg->peers[connection->index].daemon->cfg,
3108 send_hello_context->peer->daemon,
3109 pg->peers[connection->index].daemon,
3110 "Peers failed to connect (timeout)");
3112 GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3113 GNUNET_free(connection);
3114 connection = connection->next;
3116 GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3118 other_peer = &pg->peers[connection->index];
3124 * For peers that haven't yet connected, send
3125 * CORE connect requests.
3127 * @param cls the 'struct SendHelloContext *'
3128 * @param tc scheduler context
3131 send_core_connect_requests (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3133 struct SendHelloContext *send_hello_context = cls;
3134 struct PeerConnection *conn;
3135 GNUNET_assert(send_hello_context->peer->daemon->server != NULL);
3137 send_hello_context->core_connect_task = GNUNET_SCHEDULER_NO_TASK;
3139 send_hello_context->connect_attempts++;
3140 if (send_hello_context->connect_attempts < send_hello_context->pg->ct_ctx.connect_attempts)
3142 conn = send_hello_context->peer->connect_peers_head;
3143 while (conn != NULL)
3145 GNUNET_CORE_peer_request_connect(send_hello_context->peer->daemon->server,
3146 GNUNET_TIME_relative_get_forever(),
3147 &send_hello_context->pg->peers[conn->index].daemon->id,
3152 send_hello_context->core_connect_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_divide(send_hello_context->pg->ct_ctx.connect_timeout, send_hello_context->pg->ct_ctx.connect_attempts) ,
3153 &send_core_connect_requests,
3154 send_hello_context);
3158 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Timeout before all connections created, marking rest as failed!\n");
3159 GNUNET_SCHEDULER_add_now(¬ify_remaining_connections_failed, send_hello_context);
3166 * Success, connection is up. Signal client our success.
3168 * @param cls our "struct SendHelloContext"
3169 * @param peer identity of the peer that has connected
3170 * @param atsi performance information
3172 * FIXME: remove peers from BOTH lists, call notify twice, should
3173 * double the speed of connections as long as the list iteration
3174 * doesn't take too long!
3177 core_connect_notify (void *cls,
3178 const struct GNUNET_PeerIdentity *peer,
3179 const struct GNUNET_TRANSPORT_ATS_Information *atsi)
3181 struct SendHelloContext *send_hello_context = cls;
3182 struct PeerConnection *connection;
3183 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3185 struct PeerData *other_peer;
3188 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3189 "Connected peer %s to peer %s\n",
3190 ctx->d1->shortname, GNUNET_i2s(peer));
3193 if (0 == memcmp(&send_hello_context->peer->daemon->id, peer, sizeof(struct GNUNET_PeerIdentity)))
3196 connection = send_hello_context->peer->connect_peers_head;
3201 while ((connection != NULL) &&
3202 (0 != memcmp(&pg->peers[connection->index].daemon->id, peer, sizeof(struct GNUNET_PeerIdentity))))
3204 connection = connection->next;
3207 if (connection == NULL)
3209 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Connected peer %s to %s, not in list (no problem(?))\n", GNUNET_i2s(peer), send_hello_context->peer->daemon->shortname);
3214 other_peer = &pg->peers[connection->index];
3216 if (pg->notify_connection != NULL)
3218 pg->notify_connection(pg->notify_connection_cls,
3219 &send_hello_context->peer->daemon->id,
3222 send_hello_context->peer->daemon->cfg,
3223 pg->peers[connection->index].daemon->cfg,
3224 send_hello_context->peer->daemon,
3225 pg->peers[connection->index].daemon,
3228 GNUNET_CONTAINER_DLL_remove(send_hello_context->peer->connect_peers_head, send_hello_context->peer->connect_peers_tail, connection);
3229 GNUNET_free(connection);
3233 /* Notify of reverse connection and remove from other peers list of outstanding */
3234 if (other_peer != NULL)
3236 connection = other_peer->connect_peers_head;
3237 while ((connection != NULL) &&
3238 (0 != memcmp(&send_hello_context->peer->daemon->id, &pg->peers[connection->index].daemon->id, sizeof(struct GNUNET_PeerIdentity))))
3240 connection = connection->next;
3242 if (connection != NULL)
3244 if (pg->notify_connection != NULL)
3246 pg->notify_connection(pg->notify_connection_cls,
3248 &send_hello_context->peer->daemon->id,
3250 pg->peers[connection->index].daemon->cfg,
3251 send_hello_context->peer->daemon->cfg,
3252 pg->peers[connection->index].daemon,
3253 send_hello_context->peer->daemon,
3257 GNUNET_CONTAINER_DLL_remove(other_peer->connect_peers_head, other_peer->connect_peers_tail, connection);
3258 GNUNET_free(connection);
3263 if (send_hello_context->peer->connect_peers_head == NULL)
3265 GNUNET_SCHEDULER_add_now(&free_hello_context, send_hello_context);
3270 * Notify of a successful connection to the core service.
3272 * @param cls a struct SendHelloContext *
3273 * @param server handle to the core service
3274 * @param my_identity the peer identity of this peer
3275 * @param publicKey the public key of the peer
3278 core_init (void *cls,
3279 struct GNUNET_CORE_Handle * server,
3280 const struct GNUNET_PeerIdentity *
3283 GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *
3286 struct SendHelloContext *send_hello_context = cls;
3287 send_hello_context->core_ready = GNUNET_YES;
3292 * Function called once a hello has been sent
3293 * to the transport, move on to the next one
3294 * or go away forever.
3296 * @param cls the 'struct SendHelloContext *'
3297 * @param tc scheduler context
3300 hello_sent_callback (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3302 struct SendHelloContext *send_hello_context = cls;
3303 //unsigned int pg_iter;
3304 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3306 GNUNET_free(send_hello_context);
3310 send_hello_context->pg->remaining_hellos--;
3312 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Sent HELLO, have %d remaining!\n", send_hello_context->pg->remaining_hellos);
3314 if (send_hello_context->peer_pos == NULL) /* All HELLOs (for this peer!) have been transmitted! */
3317 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "All hellos for this peer sent, disconnecting transport!\n");
3319 GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3320 GNUNET_TRANSPORT_disconnect(send_hello_context->peer->daemon->th);
3321 send_hello_context->peer->daemon->th = NULL;
3323 /*if (send_hello_context->pg->remaining_hellos == 0)
3325 for (pg_iter = 0; pg_iter < send_hello_context->pg->max_outstanding_connections; pg_iter++)
3327 preschedule_connect(&send_hello_context->pg->ct_ctx);
3331 GNUNET_assert (send_hello_context->peer->daemon->server == NULL);
3332 send_hello_context->peer->daemon->server = GNUNET_CORE_connect(send_hello_context->peer->cfg,
3336 &core_connect_notify,
3343 send_hello_context->core_connect_task = GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_relative_divide(send_hello_context->pg->ct_ctx.connect_timeout, send_hello_context->pg->ct_ctx.connect_attempts),
3344 &send_core_connect_requests,
3345 send_hello_context);
3348 GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3353 * Connect to a peer, give it all the HELLO's of those peers
3354 * we will later ask it to connect to.
3356 * @param ct_ctx the overall connection context
3358 static void schedule_send_hellos (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3360 struct SendHelloContext *send_hello_context = cls;
3361 struct GNUNET_TESTING_PeerGroup *pg = send_hello_context->pg;
3363 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3365 GNUNET_free(send_hello_context);
3369 GNUNET_assert(send_hello_context->peer_pos != NULL); /* All of the HELLO sends to be scheduled have been scheduled! */
3371 if (((send_hello_context->peer->daemon->th == NULL) &&
3372 (pg->outstanding_connects > pg->max_outstanding_connections)) ||
3373 (pg->stop_connects == GNUNET_YES))
3375 #if VERBOSE_TESTING > 2
3376 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3378 ("Delaying connect, we have too many outstanding connections!\n"));
3380 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3381 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3382 &schedule_send_hellos, send_hello_context);
3386 #if VERBOSE_TESTING > 2
3387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3388 _("Creating connection, outstanding_connections is %d\n"),
3389 outstanding_connects);
3391 if (send_hello_context->peer->daemon->th == NULL)
3393 pg->outstanding_connects++; /* Actual TRANSPORT, CORE connections! */
3394 send_hello_context->peer->daemon->th = GNUNET_TRANSPORT_connect(send_hello_context->peer->cfg,
3402 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3403 _("Offering Hello of peer %s to peer %s\n"),
3404 send_hello_context->peer->daemon->shortname, pg->peers[send_hello_context->peer_pos->index].daemon->shortname);
3406 GNUNET_TRANSPORT_offer_hello(send_hello_context->peer->daemon->th,
3407 (const struct GNUNET_MessageHeader *)pg->peers[send_hello_context->peer_pos->index].daemon->hello,
3408 &hello_sent_callback,
3409 send_hello_context);
3410 send_hello_context->peer_pos = send_hello_context->peer_pos->next;
3411 GNUNET_assert(send_hello_context->peer->daemon->th != NULL);
3418 * Internal notification of a connection, kept so that we can ensure some connections
3419 * happen instead of flooding all testing daemons with requests to connect.
3422 internal_connect_notify (void *cls,
3423 const struct GNUNET_PeerIdentity *first,
3424 const struct GNUNET_PeerIdentity *second,
3426 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
3427 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
3428 struct GNUNET_TESTING_Daemon *first_daemon,
3429 struct GNUNET_TESTING_Daemon *second_daemon,
3432 struct ConnectContext *connect_ctx = cls;
3433 struct ConnectTopologyContext *ct_ctx = connect_ctx->ct_ctx;
3434 struct GNUNET_TESTING_PeerGroup *pg = ct_ctx->pg;
3435 struct PeerConnection *connection;
3436 pg->outstanding_connects--;
3439 * Check whether the inverse connection has been scheduled yet,
3440 * if not, we can remove it from the other peers list and avoid
3441 * even trying to connect them again!
3443 connection = pg->peers[connect_ctx->second_index].connect_peers_head;
3448 while ((connection != NULL) &&
3449 (0 != memcmp(first, &pg->peers[connection->index].daemon->id, sizeof(struct GNUNET_PeerIdentity))))
3451 connection = connection->next;
3454 if (connection != NULL) /* Can safely remove! */
3456 ct_ctx->remaining_connections--;
3457 if (pg->notify_connection != NULL) /* Notify of reverse connection */
3458 pg->notify_connection (pg->notify_connection_cls, second, first, distance,
3459 second_cfg, first_cfg, second_daemon, first_daemon,
3462 GNUNET_CONTAINER_DLL_remove(pg->peers[connect_ctx->second_index].connect_peers_head, pg->peers[connect_ctx->second_index].connect_peers_tail, connection);
3463 GNUNET_free(connection);
3466 if (ct_ctx->remaining_connections == 0)
3468 if (ct_ctx->notify_connections_done != NULL)
3469 ct_ctx->notify_connections_done (ct_ctx->notify_cls, NULL);
3472 preschedule_connect(pg);
3474 if (pg->notify_connection != NULL)
3475 pg->notify_connection (pg->notify_connection_cls, first, second, distance,
3476 first_cfg, second_cfg, first_daemon, second_daemon,
3479 GNUNET_free(connect_ctx);
3484 * Either delay a connection (because there are too many outstanding)
3485 * or schedule it for right now.
3487 * @param cls a connection context
3488 * @param tc the task runtime context
3491 schedule_connect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
3493 struct ConnectContext *connect_context = cls;
3494 struct GNUNET_TESTING_PeerGroup *pg = connect_context->ct_ctx->pg;
3496 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3499 if ((pg->outstanding_connects > pg->max_outstanding_connections) || (pg->stop_connects == GNUNET_YES))
3502 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3504 ("Delaying connect, we have too many outstanding connections!\n"));
3506 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
3507 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
3508 &schedule_connect, connect_context);
3513 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3514 _("Creating connection, outstanding_connections is %d (max %d)\n"),
3515 pg->outstanding_connects, pg->max_outstanding_connections);
3517 pg->outstanding_connects++;
3518 pg->total_connects_scheduled++;
3519 GNUNET_TESTING_daemons_connect (pg->peers[connect_context->first_index].daemon,
3520 pg->peers[connect_context->second_index].daemon,
3521 connect_context->ct_ctx->connect_timeout,
3522 connect_context->ct_ctx->connect_attempts,
3528 &internal_connect_notify,
3529 connect_context); /* FIXME: free connect context! */
3535 * Iterator for actually scheduling connections to be created
3536 * between two peers.
3538 * @param cls closure, a GNUNET_TESTING_Daemon
3539 * @param key the key the second Daemon was stored under
3540 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3542 * @return GNUNET_YES to continue iteration
3545 connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3547 struct ConnectTopologyContext *ct_ctx = cls;
3548 struct PeerData *first = ct_ctx->first;
3549 struct GNUNET_TESTING_Daemon *second = value;
3550 struct ConnectContext *connect_context;
3552 connect_context = GNUNET_malloc (sizeof (struct ConnectContext));
3553 connect_context->first = first->daemon;
3554 connect_context->second = second;
3555 connect_context->ct_ctx = ct_ctx;
3556 GNUNET_SCHEDULER_add_now (&schedule_connect, connect_context);
3564 * Iterator for copying all entries in the allowed hashmap to the
3567 * @param cls closure, a GNUNET_TESTING_Daemon
3568 * @param key the key the second Daemon was stored under
3569 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
3571 * @return GNUNET_YES to continue iteration
3574 copy_topology_iterator (void *cls, const GNUNET_HashCode * key, void *value)
3576 struct PeerData *first = cls;
3578 GNUNET_assert (GNUNET_OK ==
3579 GNUNET_CONTAINER_multihashmap_put (first->connect_peers, key,
3581 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
3588 * Make the peers to connect the same as those that are allowed to be
3591 * @param pg the peer group
3594 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
3596 unsigned int pg_iter;
3600 struct PeerConnection *iter;
3604 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3607 iter = pg->peers[pg_iter].allowed_peers_head;
3608 while (iter != NULL)
3610 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating connection between %d and %d\n", pg_iter, iter->index);
3611 total += add_connections(pg, pg_iter, iter->index, CONNECT);
3612 //total += add_actual_connections(pg, pg_iter, iter->index);
3617 GNUNET_CONTAINER_multihashmap_iterate (pg->
3618 peers[pg_iter].allowed_peers,
3619 ©_topology_iterator,
3620 &pg->peers[pg_iter]);
3622 if (GNUNET_SYSERR == ret)
3623 return GNUNET_SYSERR;
3625 total = total + ret;
3633 * Connect the topology as specified by the PeerConnection's
3634 * of each peer in the peer group
3636 * @param pg the peer group we are dealing with
3637 * @param connect_timeout how long try connecting two peers
3638 * @param connect_attempts how many times (max) to attempt
3639 * @param notify_callback callback to notify when finished
3640 * @param notify_cls closure for notify callback
3642 * @return the number of connections that will be attempted
3645 connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
3646 struct GNUNET_TIME_Relative connect_timeout,
3647 unsigned int connect_attempts,
3648 GNUNET_TESTING_NotifyCompletion notify_callback,
3651 unsigned int pg_iter;
3655 struct PeerConnection *connection_iter;
3658 struct SendHelloContext *send_hello_context
3662 pg->ct_ctx.notify_connections_done = notify_callback;
3663 pg->ct_ctx.notify_cls = notify_cls;
3666 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3669 connection_iter = pg->peers[pg_iter].connect_peers_head;
3670 while (connection_iter != NULL)
3672 connection_iter = connection_iter->next;
3677 GNUNET_CONTAINER_multihashmap_size (pg->peers[pg_iter].connect_peers);
3684 pg->ct_ctx.connect_timeout = connect_timeout;
3685 pg->ct_ctx.connect_attempts = connect_attempts;
3686 pg->ct_ctx.remaining_connections = total;
3689 /* First give all peers the HELLO's of other peers (connect to first peer's transport service, give HELLO's of other peers, continue...) */
3690 pg->remaining_hellos = total;
3691 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
3693 send_hello_context = GNUNET_malloc(sizeof(struct SendHelloContext));
3694 send_hello_context->peer = &pg->peers[pg_iter];
3695 send_hello_context->peer_pos = pg->peers[pg_iter].connect_peers_head;
3696 send_hello_context->pg = pg;
3697 GNUNET_SCHEDULER_add_now(&schedule_send_hellos, send_hello_context);
3700 for (pg_iter = 0; pg_iter < pg->max_outstanding_connections; pg_iter++)
3702 preschedule_connect(pg);
3711 * Takes a peer group and creates a topology based on the
3712 * one specified. Creates a topology means generates friend
3713 * files for the peers so they can only connect to those allowed
3714 * by the topology. This will only have an effect once peers
3715 * are started if the FRIENDS_ONLY option is set in the base
3716 * config. Also takes an optional restrict topology which
3717 * disallows connections based on particular transports
3718 * UNLESS they are specified in the restricted topology.
3720 * @param pg the peer group struct representing the running peers
3721 * @param topology which topology to connect the peers in
3722 * @param restrict_topology disallow restrict_transports transport
3723 * connections to peers NOT in this topology
3724 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
3725 * @param restrict_transports space delimited list of transports to blacklist
3726 * to create restricted topology
3728 * @return the maximum number of connections were all allowed peers
3729 * connected to each other
3732 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
3733 enum GNUNET_TESTING_Topology topology,
3734 enum GNUNET_TESTING_Topology
3736 const char *restrict_transports)
3740 unsigned int num_connections;
3741 int unblacklisted_connections;
3743 struct PeerConnection *conn_iter;
3744 struct PeerConnection *temp_conn;
3749 for (i = 0; i < pg->total; i++)
3751 pg->peers[i].allowed_peers =
3752 GNUNET_CONTAINER_multihashmap_create (100);
3753 pg->peers[i].connect_peers =
3754 GNUNET_CONTAINER_multihashmap_create (100);
3755 pg->peers[i].blacklisted_peers =
3756 GNUNET_CONTAINER_multihashmap_create (100);
3757 pg->peers[i].pg = pg;
3763 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3765 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating clique topology\n"));
3767 num_connections = create_clique (pg, &add_connections, ALLOWED);
3769 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3771 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3772 _("Creating small world (ring) topology\n"));
3775 create_small_world_ring (pg, &add_connections, ALLOWED);
3777 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3780 _("Creating small world (2d-torus) topology\n"));
3782 num_connections = create_small_world (pg, &add_connections, ALLOWED);
3784 case GNUNET_TESTING_TOPOLOGY_RING:
3786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating ring topology\n"));
3788 num_connections = create_ring (pg, &add_connections, ALLOWED);
3790 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3792 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating 2d torus topology\n"));
3794 num_connections = create_2d_torus (pg, &add_connections, ALLOWED);
3796 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3798 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3799 _("Creating Erdos-Renyi topology\n"));
3801 num_connections = create_erdos_renyi (pg, &add_connections, ALLOWED);
3803 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3805 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Creating InterNAT topology\n"));
3807 num_connections = create_nated_internet (pg, &add_connections, ALLOWED);
3809 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3811 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3812 _("Creating Scale Free topology\n"));
3814 num_connections = create_scale_free (pg, &add_connections, ALLOWED);
3816 case GNUNET_TESTING_TOPOLOGY_LINE:
3818 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3819 _("Creating straight line topology\n"));
3821 num_connections = create_line (pg, &add_connections, ALLOWED);
3823 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3825 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3826 _("Creating topology from file!\n"));
3829 GNUNET_CONFIGURATION_get_value_string (pg->cfg, "testing", "topology_file",
3831 num_connections = create_from_file (pg, filename, &add_connections, ALLOWED);
3834 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Missing configuration option TESTING:TOPOLOGY_FILE for creating topology from file!\n");
3835 num_connections = 0;
3838 case GNUNET_TESTING_TOPOLOGY_NONE:
3840 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3842 ("Creating no allowed topology (all peers can connect at core level)\n"));
3844 num_connections = pg->total * pg->total; /* Clique is allowed! */
3847 num_connections = 0;
3852 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
3854 ret = create_and_copy_friend_files (pg);
3855 if (ret != GNUNET_OK)
3858 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3859 _("Failed during friend file copying!\n"));
3861 return GNUNET_SYSERR;
3866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3867 _("Friend files created/copied successfully!\n"));
3872 /* Use the create clique method to initially set all connections as blacklisted. */
3873 if ((restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE) && (restrict_topology != GNUNET_TESTING_TOPOLOGY_FROM_FILE))
3874 create_clique (pg, &add_connections, BLACKLIST);
3876 unblacklisted_connections = 0;
3877 /* Un-blacklist connections as per the topology specified */
3878 switch (restrict_topology)
3880 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
3882 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3883 _("Blacklisting all but clique topology\n"));
3885 unblacklisted_connections =
3886 create_clique (pg, &remove_connections, BLACKLIST);
3888 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
3890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3891 _("Blacklisting all but small world (ring) topology\n"));
3893 unblacklisted_connections =
3894 create_small_world_ring (pg, &remove_connections, BLACKLIST);
3896 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
3898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3900 ("Blacklisting all but small world (2d-torus) topology\n"));
3902 unblacklisted_connections =
3903 create_small_world (pg, &remove_connections, BLACKLIST);
3905 case GNUNET_TESTING_TOPOLOGY_RING:
3907 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3908 _("Blacklisting all but ring topology\n"));
3910 unblacklisted_connections = create_ring (pg, &remove_connections, BLACKLIST);
3912 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
3914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3915 _("Blacklisting all but 2d torus topology\n"));
3917 unblacklisted_connections =
3918 create_2d_torus (pg, &remove_connections, BLACKLIST);
3920 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
3922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3923 _("Blacklisting all but Erdos-Renyi topology\n"));
3925 unblacklisted_connections =
3926 create_erdos_renyi (pg, &remove_connections, BLACKLIST);
3928 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
3930 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3931 _("Blacklisting all but InterNAT topology\n"));
3935 for (off = 0; off < pg->total; off++)
3937 conn_iter = pg->peers[off].allowed_peers_head;
3938 while (conn_iter != NULL)
3940 temp_conn = conn_iter->next;
3941 GNUNET_free(conn_iter);
3942 conn_iter = temp_conn;
3944 pg->peers[off].allowed_peers_head = NULL;
3945 pg->peers[off].allowed_peers_tail = NULL;
3947 conn_iter = pg->peers[off].connect_peers_head;
3948 while (conn_iter != NULL)
3950 temp_conn = conn_iter->next;
3951 GNUNET_free(conn_iter);
3952 conn_iter = temp_conn;
3954 pg->peers[off].connect_peers_head = NULL;
3955 pg->peers[off].connect_peers_tail = NULL;
3957 unblacklisted_connections = create_nated_internet_copy(pg, &remove_connections, BLACKLIST);
3959 unblacklisted_connections =
3960 create_nated_internet (pg, &remove_connections, BLACKLIST);
3964 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
3966 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3967 _("Blacklisting all but Scale Free topology\n"));
3969 unblacklisted_connections =
3970 create_scale_free (pg, &remove_connections, BLACKLIST);
3972 case GNUNET_TESTING_TOPOLOGY_LINE:
3974 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3975 _("Blacklisting all but straight line topology\n"));
3977 unblacklisted_connections = create_line (pg, &remove_connections, BLACKLIST);
3979 case GNUNET_TESTING_TOPOLOGY_NONE: /* Fall through */
3980 case GNUNET_TESTING_TOPOLOGY_FROM_FILE:
3982 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3984 ("Creating no blacklist topology (all peers can connect at transport level)\n"));
3986 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
3988 ("Creating blacklist topology from allowed\n"));
3989 unblacklisted_connections = copy_allowed (pg, &remove_connections);
3994 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
3996 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Creating blacklist with `%s'\n", restrict_transports);
3997 ret = create_and_copy_blacklist_files (pg, restrict_transports);
3998 if (ret != GNUNET_OK)
4001 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4002 _("Failed during blacklist file copying!\n"));
4009 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4010 _("Blacklist files created/copied successfully!\n"));
4014 return num_connections;
4020 * Iterator for choosing random peers to connect.
4022 * @param cls closure, a RandomContext
4023 * @param key the key the second Daemon was stored under
4024 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4026 * @return GNUNET_YES to continue iteration
4029 random_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4031 struct RandomContext *random_ctx = cls;
4032 double random_number;
4033 uint32_t second_pos;
4034 GNUNET_HashCode first_hash;
4037 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4038 UINT64_MAX)) / ((double) UINT64_MAX);
4039 if (random_number < random_ctx->percentage)
4041 GNUNET_assert (GNUNET_OK ==
4042 GNUNET_CONTAINER_multihashmap_put (random_ctx->
4043 first->connect_peers_working_set,
4045 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4048 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
4049 uid_from_hash (key, &second_pos);
4050 hash_from_uid (random_ctx->first_uid, &first_hash);
4051 GNUNET_assert (random_ctx->pg->total > second_pos);
4052 GNUNET_assert (GNUNET_YES ==
4053 GNUNET_CONTAINER_multihashmap_remove (random_ctx->
4055 [second_pos].connect_peers,
4065 * Iterator for adding at least X peers to a peers connection set.
4067 * @param cls closure, MinimumContext
4068 * @param key the key the second Daemon was stored under
4069 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4071 * @return GNUNET_YES to continue iteration
4074 minimum_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4076 struct MinimumContext *min_ctx = cls;
4077 uint32_t second_pos;
4078 GNUNET_HashCode first_hash;
4081 if (GNUNET_CONTAINER_multihashmap_size
4082 (min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
4084 for (i = 0; i < min_ctx->num_to_add; i++)
4086 if (min_ctx->pg_array[i] == min_ctx->current)
4088 GNUNET_assert (GNUNET_OK ==
4089 GNUNET_CONTAINER_multihashmap_put
4090 (min_ctx->first->connect_peers_working_set, key,
4092 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4093 uid_from_hash (key, &second_pos);
4094 hash_from_uid (min_ctx->first_uid, &first_hash);
4095 GNUNET_assert (min_ctx->pg->total > second_pos);
4096 GNUNET_assert (GNUNET_OK ==
4097 GNUNET_CONTAINER_multihashmap_put (min_ctx->
4099 [second_pos].connect_peers_working_set,
4103 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4104 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
4105 GNUNET_assert (GNUNET_YES ==
4106 GNUNET_CONTAINER_multihashmap_remove
4107 (min_ctx->pg->peers[second_pos].connect_peers,
4108 &first_hash, min_ctx->first->daemon));
4115 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
4121 * Iterator for adding peers to a connection set based on a depth first search.
4123 * @param cls closure, MinimumContext
4124 * @param key the key the second daemon was stored under
4125 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
4127 * @return GNUNET_YES to continue iteration
4130 dfs_connect_iterator (void *cls, const GNUNET_HashCode * key, void *value)
4132 struct DFSContext *dfs_ctx = cls;
4133 GNUNET_HashCode first_hash;
4135 if (dfs_ctx->current == dfs_ctx->chosen)
4137 GNUNET_assert (GNUNET_OK ==
4138 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4139 first->connect_peers_working_set,
4141 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4142 uid_from_hash (key, &dfs_ctx->second_uid);
4143 hash_from_uid (dfs_ctx->first_uid, &first_hash);
4144 GNUNET_assert (GNUNET_OK ==
4145 GNUNET_CONTAINER_multihashmap_put (dfs_ctx->
4147 [dfs_ctx->second_uid].connect_peers_working_set,
4151 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
4152 GNUNET_assert (GNUNET_YES ==
4153 GNUNET_CONTAINER_multihashmap_remove (dfs_ctx->
4155 [dfs_ctx->second_uid].connect_peers,
4159 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
4160 return GNUNET_NO; /* We have found our peer, don't iterate more */
4169 * From the set of connections possible, choose percentage percent of connections
4170 * to actually connect.
4172 * @param pg the peergroup we are dealing with
4173 * @param percentage what percent of total connections to make
4176 choose_random_connections (struct GNUNET_TESTING_PeerGroup *pg,
4179 struct RandomContext random_ctx;
4182 struct PeerConnection *temp_peers;
4183 struct PeerConnection *conn_iter;
4184 double random_number;
4187 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4189 random_ctx.first_uid = pg_iter;
4190 random_ctx.first = &pg->peers[pg_iter];
4191 random_ctx.percentage = percentage;
4195 conn_iter = pg->peers[pg_iter].connect_peers_head;
4196 while (conn_iter != NULL)
4200 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
4201 UINT64_MAX)) / ((double) UINT64_MAX);
4202 if (random_number < percentage)
4204 add_connections(pg, pg_iter, conn_iter->index, WORKING_SET);
4206 conn_iter = conn_iter->next;
4209 pg->peers[pg_iter].connect_peers_working_set =
4210 GNUNET_CONTAINER_multihashmap_create (pg->total);
4211 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4212 &random_connect_iterator,
4214 /* Now remove the old connections */
4215 GNUNET_CONTAINER_multihashmap_destroy (pg->
4216 peers[pg_iter].connect_peers);
4217 /* And replace with the random set */
4218 pg->peers[pg_iter].connect_peers =
4219 pg->peers[pg_iter].connect_peers_working_set;
4223 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4225 conn_iter = pg->peers[pg_iter].connect_peers_head;
4226 while (pg->peers[pg_iter].connect_peers_head != NULL)
4227 remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT);
4229 pg->peers[pg_iter].connect_peers_head = pg->peers[pg_iter].connect_peers_working_set_head;
4230 pg->peers[pg_iter].connect_peers_tail = pg->peers[pg_iter].connect_peers_working_set_tail;
4231 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4232 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4238 * Count the number of connections in a linked list of connections.
4240 * @param conn_list the connection list to get the count of
4242 * @return the number of elements in the list
4245 count_connections (struct PeerConnection *conn_list)
4247 struct PeerConnection *iter;
4251 while (iter != NULL)
4261 count_workingset_connections (struct GNUNET_TESTING_PeerGroup *pg)
4264 unsigned int pg_iter;
4266 struct PeerConnection *conn_iter;
4270 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4273 conn_iter = pg->peers[pg_iter].connect_peers_working_set_head;
4274 while (conn_iter != NULL)
4277 conn_iter = conn_iter->next;
4281 GNUNET_CONTAINER_multihashmap_size (pg->
4283 [pg_iter].connect_peers_working_set);
4292 count_allowed_connections (struct GNUNET_TESTING_PeerGroup *pg)
4295 unsigned int pg_iter;
4297 struct PeerConnection *conn_iter;
4301 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4304 conn_iter = pg->peers[pg_iter].allowed_peers_head;
4305 while (conn_iter != NULL)
4308 conn_iter = conn_iter->next;
4312 GNUNET_CONTAINER_multihashmap_size (pg->
4314 [pg_iter].allowed_peers);
4323 * From the set of connections possible, choose at least num connections per
4326 * @param pg the peergroup we are dealing with
4327 * @param num how many connections at least should each peer have (if possible)?
4330 choose_minimum (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4333 struct MinimumContext minimum_ctx;
4335 struct PeerConnection *conn_iter;
4336 unsigned int temp_list_size;
4339 uint32_t random; /* Random list entry to connect peer to */
4344 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4346 temp_list_size = count_connections(pg->peers[pg_iter].connect_peers_head);
4347 if (temp_list_size == 0)
4349 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Peer %d has 0 connections!?!?\n", pg_iter);
4352 for (i = 0; i < num; i++)
4354 random = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, temp_list_size);
4355 conn_iter = pg->peers[pg_iter].connect_peers_head;
4356 for (count = 0; count < random; count++)
4357 conn_iter = conn_iter->next;
4358 /* We now have a random connection, connect it! */
4359 GNUNET_assert(conn_iter != NULL);
4360 add_connections(pg, pg_iter, conn_iter->index, WORKING_SET);
4364 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4366 pg->peers[pg_iter].connect_peers_working_set =
4367 GNUNET_CONTAINER_multihashmap_create (num);
4370 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4372 minimum_ctx.first_uid = pg_iter;
4373 minimum_ctx.pg_array =
4374 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK,
4375 GNUNET_CONTAINER_multihashmap_size
4376 (pg->peers[pg_iter].connect_peers));
4377 minimum_ctx.first = &pg->peers[pg_iter];
4378 minimum_ctx.pg = pg;
4379 minimum_ctx.num_to_add = num;
4380 minimum_ctx.current = 0;
4381 GNUNET_CONTAINER_multihashmap_iterate (pg->peers[pg_iter].connect_peers,
4382 &minimum_connect_iterator,
4386 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4388 /* Remove the "old" connections */
4389 GNUNET_CONTAINER_multihashmap_destroy (pg->
4390 peers[pg_iter].connect_peers);
4391 /* And replace with the working set */
4392 pg->peers[pg_iter].connect_peers =
4393 pg->peers[pg_iter].connect_peers_working_set;
4396 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4398 while (pg->peers[pg_iter].connect_peers_head != NULL)
4399 remove_connections(pg, pg_iter, pg->peers[pg_iter].connect_peers_head->index, CONNECT);
4401 pg->peers[pg_iter].connect_peers_head = pg->peers[pg_iter].connect_peers_working_set_head;
4402 pg->peers[pg_iter].connect_peers_tail = pg->peers[pg_iter].connect_peers_working_set_tail;
4403 pg->peers[pg_iter].connect_peers_working_set_head = NULL;
4404 pg->peers[pg_iter].connect_peers_working_set_tail = NULL;
4409 struct FindClosestContext
4412 * The currently known closest peer.
4414 struct GNUNET_TESTING_Daemon *closest;
4417 * The info for the peer we are adding connections for.
4419 struct PeerData *curr_peer;
4422 * The distance (bits) between the current
4423 * peer and the currently known closest.
4425 unsigned int closest_dist;
4428 * The offset of the closest known peer in
4431 unsigned int closest_num;
4435 * Iterator over hash map entries of the allowed
4436 * peer connections. Find the closest, not already
4437 * connected peer and return it.
4439 * @param cls closure (struct FindClosestContext)
4440 * @param key current key code (hash of offset in pg)
4441 * @param value value in the hash map - a GNUNET_TESTING_Daemon
4442 * @return GNUNET_YES if we should continue to
4447 find_closest_peers (void *cls, const GNUNET_HashCode * key, void *value)
4449 struct FindClosestContext *closest_ctx = cls;
4450 struct GNUNET_TESTING_Daemon *daemon = value;
4452 if (((closest_ctx->closest == NULL) ||
4453 (GNUNET_CRYPTO_hash_matching_bits
4454 (&daemon->id.hashPubKey,
4455 &closest_ctx->curr_peer->daemon->id.hashPubKey) >
4456 closest_ctx->closest_dist))
4458 GNUNET_CONTAINER_multihashmap_contains (closest_ctx->
4459 curr_peer->connect_peers,
4462 closest_ctx->closest_dist =
4463 GNUNET_CRYPTO_hash_matching_bits (&daemon->id.hashPubKey,
4464 &closest_ctx->curr_peer->daemon->
4466 closest_ctx->closest = daemon;
4467 uid_from_hash (key, &closest_ctx->closest_num);
4474 * From the set of connections possible, choose at num connections per
4475 * peer based on depth which are closest out of those allowed. Guaranteed
4476 * to add num peers to connect to, provided there are that many peers
4477 * in the underlay topology to connect to.
4479 * @param pg the peergroup we are dealing with
4480 * @param num how many connections at least should each peer have (if possible)?
4481 * @param proc processor to actually add the connections
4482 * @param list the peer list to use
4485 add_closest (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num,
4486 GNUNET_TESTING_ConnectionProcessor proc, enum PeerLists list)
4491 struct FindClosestContext closest_ctx;
4496 for (i = 0; i < num; i++) /* Each time find a closest peer (from those available) */
4498 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4500 closest_ctx.curr_peer = &pg->peers[pg_iter];
4501 closest_ctx.closest = NULL;
4502 closest_ctx.closest_dist = 0;
4503 closest_ctx.closest_num = 0;
4504 GNUNET_CONTAINER_multihashmap_iterate (pg->
4505 peers[pg_iter].allowed_peers,
4506 &find_closest_peers,
4508 if (closest_ctx.closest != NULL)
4510 GNUNET_assert (closest_ctx.closest_num < pg->total);
4511 proc (pg, pg_iter, closest_ctx.closest_num, list);
4519 * From the set of connections possible, choose at least num connections per
4520 * peer based on depth first traversal of peer connections. If DFS leaves
4521 * peers unconnected, ensure those peers get connections.
4523 * @param pg the peergroup we are dealing with
4524 * @param num how many connections at least should each peer have (if possible)?
4527 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
4531 uint32_t starting_peer;
4532 uint32_t least_connections;
4533 uint32_t random_connection;
4535 unsigned int temp_count;
4536 struct PeerConnection *peer_iter;
4538 struct DFSContext dfs_ctx;
4539 GNUNET_HashCode second_hash;
4545 while ((count_workingset_connections (pg) < num * pg->total)
4546 && (count_allowed_connections (pg) > 0))
4548 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4550 least_connections = -1; /* Set to very high number */
4551 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4553 temp_count = count_connections(pg->peers[pg_iter].connect_peers_working_set_head);
4554 if (temp_count < least_connections)
4556 starting_peer = pg_iter;
4557 least_connections = temp_count;
4562 temp_count = count_connections(pg->peers[starting_peer].connect_peers_head);
4563 if (temp_count == 0)
4564 continue; /* FIXME: infinite loop? */
4566 random_connection = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, temp_count);
4568 peer_iter = pg->peers[starting_peer].connect_peers_head;
4569 while (temp_count < random_connection)
4571 peer_iter = peer_iter->next;
4574 GNUNET_assert(peer_iter != NULL);
4575 add_connections(pg, starting_peer, peer_iter->index, WORKING_SET);
4576 remove_connections(pg, starting_peer, peer_iter->index, CONNECT);
4577 starting_peer = peer_iter->index;
4582 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4584 pg->peers[pg_iter].connect_peers_working_set =
4585 GNUNET_CONTAINER_multihashmap_create (num);
4590 while ((count_workingset_connections (pg) < num * pg->total)
4591 && (count_allowed_connections (pg) > 0))
4593 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
4595 least_connections = -1; /* Set to very high number */
4596 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4598 if (GNUNET_CONTAINER_multihashmap_size
4599 (pg->peers[pg_iter].connect_peers_working_set) <
4602 starting_peer = pg_iter;
4604 GNUNET_CONTAINER_multihashmap_size (pg->
4606 [pg_iter].connect_peers_working_set);
4611 if (GNUNET_CONTAINER_multihashmap_size (pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
4617 /* Choose a random peer from the chosen peers set of connections to add */
4619 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
4620 GNUNET_CONTAINER_multihashmap_size
4621 (pg->peers[starting_peer].connect_peers));
4622 dfs_ctx.first_uid = starting_peer;
4623 dfs_ctx.first = &pg->peers[starting_peer];
4625 dfs_ctx.current = 0;
4627 GNUNET_CONTAINER_multihashmap_iterate (pg->
4629 [starting_peer].connect_peers,
4630 &dfs_connect_iterator, &dfs_ctx);
4631 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
4632 hash_from_uid (dfs_ctx.second_uid, &second_hash);
4633 GNUNET_assert (GNUNET_YES ==
4634 GNUNET_CONTAINER_multihashmap_remove (pg->peers
4635 [starting_peer].connect_peers,
4639 [dfs_ctx.second_uid].daemon));
4640 starting_peer = dfs_ctx.second_uid;
4643 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
4645 /* Remove the "old" connections */
4646 GNUNET_CONTAINER_multihashmap_destroy (pg->
4647 peers[pg_iter].connect_peers);
4648 /* And replace with the working set */
4649 pg->peers[pg_iter].connect_peers =
4650 pg->peers[pg_iter].connect_peers_working_set;
4657 * Internal callback for topology information for a particular peer.
4660 internal_topology_callback (void *cls,
4661 const struct GNUNET_PeerIdentity *peer,
4662 const struct GNUNET_TRANSPORT_ATS_Information
4665 struct CoreContext *core_ctx = cls;
4666 struct TopologyIterateContext *iter_ctx = core_ctx->iter_context;
4668 if (peer == NULL) /* Either finished, or something went wrong */
4670 iter_ctx->completed++;
4671 iter_ctx->connected--;
4672 /* One core context allocated per iteration, must free! */
4673 GNUNET_free (core_ctx);
4677 iter_ctx->topology_cb (iter_ctx->cls, &core_ctx->daemon->id,
4681 if (iter_ctx->completed == iter_ctx->total)
4683 iter_ctx->topology_cb (iter_ctx->cls, NULL, NULL, NULL);
4684 /* Once all are done, free the iteration context */
4685 GNUNET_free (iter_ctx);
4691 * Check running topology iteration tasks, if below max start a new one, otherwise
4692 * schedule for some time in the future.
4695 schedule_get_topology (void *cls,
4696 const struct GNUNET_SCHEDULER_TaskContext *tc)
4698 struct CoreContext *core_context = cls;
4699 struct TopologyIterateContext *topology_context =
4700 (struct TopologyIterateContext *) core_context->iter_context;
4701 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4704 if (topology_context->connected > topology_context->pg->max_outstanding_connections)
4706 #if VERBOSE_TESTING > 2
4707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4709 ("Delaying connect, we have too many outstanding connections!\n"));
4711 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4712 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4713 &schedule_get_topology, core_context);
4717 #if VERBOSE_TESTING > 2
4718 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4719 _("Creating connection, outstanding_connections is %d\n"),
4720 outstanding_connects);
4722 topology_context->connected++;
4725 GNUNET_CORE_iterate_peers (core_context->daemon->cfg,
4726 &internal_topology_callback,
4729 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Topology iteration failed.\n");
4730 internal_topology_callback (core_context, NULL, NULL);
4736 * Iterate over all (running) peers in the peer group, retrieve
4737 * all connections that each currently has.
4740 GNUNET_TESTING_get_topology (struct GNUNET_TESTING_PeerGroup *pg,
4741 GNUNET_TESTING_NotifyTopology cb, void *cls)
4743 struct TopologyIterateContext *topology_context;
4744 struct CoreContext *core_ctx;
4746 unsigned int total_count;
4748 /* Allocate a single topology iteration context */
4749 topology_context = GNUNET_malloc (sizeof (struct TopologyIterateContext));
4750 topology_context->topology_cb = cb;
4751 topology_context->cls = cls;
4752 topology_context->pg = pg;
4754 for (i = 0; i < pg->total; i++)
4756 if (pg->peers[i].daemon->running == GNUNET_YES)
4758 /* Allocate one core context per core we need to connect to */
4759 core_ctx = GNUNET_malloc (sizeof (struct CoreContext));
4760 core_ctx->daemon = pg->peers[i].daemon;
4761 /* Set back pointer to topology iteration context */
4762 core_ctx->iter_context = topology_context;
4763 GNUNET_SCHEDULER_add_now (&schedule_get_topology, core_ctx);
4767 if (total_count == 0)
4769 cb (cls, NULL, NULL, "Cannot iterate over topology, no running peers!");
4770 GNUNET_free (topology_context);
4773 topology_context->total = total_count;
4778 * Callback function to process statistic values.
4779 * This handler is here only really to insert a peer
4780 * identity (or daemon) so the statistics can be uniquely
4781 * tied to a single running peer.
4783 * @param cls closure
4784 * @param subsystem name of subsystem that created the statistic
4785 * @param name the name of the datum
4786 * @param value the current value
4787 * @param is_persistent GNUNET_YES if the value is persistent, GNUNET_NO if not
4788 * @return GNUNET_OK to continue, GNUNET_SYSERR to abort iteration
4791 internal_stats_callback (void *cls,
4792 const char *subsystem,
4793 const char *name, uint64_t value, int is_persistent)
4795 struct StatsCoreContext *core_context = cls;
4796 struct StatsIterateContext *stats_context =
4797 (struct StatsIterateContext *) core_context->iter_context;
4799 return stats_context->proc (stats_context->cls, &core_context->daemon->id,
4800 subsystem, name, value, is_persistent);
4804 * Internal continuation call for statistics iteration.
4806 * @param cls closure, the CoreContext for this iteration
4807 * @param success whether or not the statistics iterations
4808 * was canceled or not (we don't care)
4811 internal_stats_cont (void *cls, int success)
4813 struct StatsCoreContext *core_context = cls;
4814 struct StatsIterateContext *stats_context =
4815 (struct StatsIterateContext *) core_context->iter_context;
4817 stats_context->connected--;
4818 stats_context->completed++;
4820 if (stats_context->completed == stats_context->total)
4822 stats_context->cont (stats_context->cls, GNUNET_YES);
4823 GNUNET_free (stats_context);
4826 if (core_context->stats_handle != NULL)
4827 GNUNET_STATISTICS_destroy (core_context->stats_handle, GNUNET_NO);
4829 GNUNET_free (core_context);
4833 * Check running topology iteration tasks, if below max start a new one, otherwise
4834 * schedule for some time in the future.
4837 schedule_get_statistics (void *cls,
4838 const struct GNUNET_SCHEDULER_TaskContext *tc)
4840 struct StatsCoreContext *core_context = cls;
4841 struct StatsIterateContext *stats_context =
4842 (struct StatsIterateContext *) core_context->iter_context;
4844 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
4847 if (stats_context->connected > stats_context->pg->max_outstanding_connections)
4849 #if VERBOSE_TESTING > 2
4850 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4852 ("Delaying connect, we have too many outstanding connections!\n"));
4854 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
4855 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
4856 &schedule_get_statistics, core_context);
4860 #if VERBOSE_TESTING > 2
4861 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
4862 _("Creating connection, outstanding_connections is %d\n"),
4863 outstanding_connects);
4866 stats_context->connected++;
4867 core_context->stats_handle =
4868 GNUNET_STATISTICS_create ("testing", core_context->daemon->cfg);
4869 if (core_context->stats_handle == NULL)
4871 internal_stats_cont (core_context, GNUNET_NO);
4875 core_context->stats_get_handle =
4876 GNUNET_STATISTICS_get (core_context->stats_handle, NULL, NULL,
4877 GNUNET_TIME_relative_get_forever (),
4878 &internal_stats_cont, &internal_stats_callback,
4880 if (core_context->stats_get_handle == NULL)
4881 internal_stats_cont (core_context, GNUNET_NO);
4886 struct DuplicateStats
4889 * Next item in the list
4891 struct DuplicateStats *next;
4894 * Nasty string, concatenation of relevant information.
4896 char *unique_string;
4900 * Check whether the combination of port/host/unix domain socket
4901 * already exists in the list of peers being checked for statistics.
4903 * @param pg the peergroup in question
4904 * @param specific_peer the peer we're concerned with
4905 * @param stats_list the list to return to the caller
4907 * @return GNUNET_YES if the statistics instance has been seen already,
4908 * GNUNET_NO if not (and we may have added it to the list)
4911 stats_check_existing (struct GNUNET_TESTING_PeerGroup *pg,
4912 struct PeerData *specific_peer,
4913 struct DuplicateStats **stats_list)
4915 struct DuplicateStats *pos;
4916 char *unix_domain_socket;
4917 unsigned long long port;
4920 GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "testing",
4921 "single_statistics_per_host"))
4922 return GNUNET_NO; /* Each peer has its own statistics instance, do nothing! */
4926 GNUNET_CONFIGURATION_get_value_string (specific_peer->cfg, "statistics",
4927 "unixpath", &unix_domain_socket))
4931 GNUNET_CONFIGURATION_get_value_number (specific_peer->cfg, "statistics",
4934 GNUNET_free(unix_domain_socket);
4938 if (specific_peer->daemon->hostname != NULL)
4939 GNUNET_asprintf (&to_match, "%s%s%llu", specific_peer->daemon->hostname,
4940 unix_domain_socket, port);
4942 GNUNET_asprintf (&to_match, "%s%llu", unix_domain_socket, port);
4946 if (0 == strcmp (to_match, pos->unique_string))
4948 GNUNET_free (unix_domain_socket);
4949 GNUNET_free (to_match);
4954 pos = GNUNET_malloc (sizeof (struct DuplicateStats));
4955 pos->unique_string = to_match;
4956 pos->next = *stats_list;
4958 GNUNET_free (unix_domain_socket);
4963 * Iterate over all (running) peers in the peer group, retrieve
4964 * all statistics from each.
4967 GNUNET_TESTING_get_statistics (struct GNUNET_TESTING_PeerGroup *pg,
4968 GNUNET_STATISTICS_Callback cont,
4969 GNUNET_TESTING_STATISTICS_Iterator proc,
4972 struct StatsIterateContext *stats_context;
4973 struct StatsCoreContext *core_ctx;
4975 unsigned int total_count;
4976 struct DuplicateStats *stats_list;
4977 struct DuplicateStats *pos;
4980 /* Allocate a single stats iteration context */
4981 stats_context = GNUNET_malloc (sizeof (struct StatsIterateContext));
4982 stats_context->cont = cont;
4983 stats_context->proc = proc;
4984 stats_context->cls = cls;
4985 stats_context->pg = pg;
4988 for (i = 0; i < pg->total; i++)
4990 if ((pg->peers[i].daemon->running == GNUNET_YES)
4992 stats_check_existing (pg, &pg->peers[i], &stats_list)))
4994 /* Allocate one core context per core we need to connect to */
4995 core_ctx = GNUNET_malloc (sizeof (struct StatsCoreContext));
4996 core_ctx->daemon = pg->peers[i].daemon;
4997 /* Set back pointer to topology iteration context */
4998 core_ctx->iter_context = stats_context;
4999 GNUNET_SCHEDULER_add_now (&schedule_get_statistics, core_ctx);
5004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5005 "Retrieving stats from %u total instances.\n", total_count);
5006 stats_context->total = total_count;
5007 if (stats_list != NULL)
5012 GNUNET_free (pos->unique_string);
5013 stats_list = pos->next;
5015 pos = stats_list->next;
5022 * Stop the connection process temporarily.
5024 * @param pg the peer group to stop connecting
5026 void GNUNET_TESTING_stop_connections(struct GNUNET_TESTING_PeerGroup *pg)
5028 pg->stop_connects = GNUNET_YES;
5032 * Resume the connection process temporarily.
5034 * @param pg the peer group to resume connecting
5036 void GNUNET_TESTING_resume_connections(struct GNUNET_TESTING_PeerGroup *pg)
5038 pg->stop_connects = GNUNET_NO;
5042 * There are many ways to connect peers that are supported by this function.
5043 * To connect peers in the same topology that was created via the
5044 * GNUNET_TESTING_create_topology, the topology variable must be set to
5045 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
5046 * a new instance of that topology will be generated and attempted to be
5047 * connected. This could result in some connections being impossible,
5048 * because some topologies are non-deterministic.
5050 * @param pg the peer group struct representing the running peers
5051 * @param topology which topology to connect the peers in
5052 * @param options options for connecting the topology
5053 * @param option_modifier modifier for options that take a parameter
5054 * @param connect_timeout how long to wait before giving up on connecting
5056 * @param connect_attempts how many times to attempt to connect two peers
5057 * over the connect_timeout duration
5058 * @param notify_callback notification to be called once all connections completed
5059 * @param notify_cls closure for notification callback
5061 * @return the number of connections that will be attempted, GNUNET_SYSERR on error
5064 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
5065 enum GNUNET_TESTING_Topology topology,
5066 enum GNUNET_TESTING_TopologyOption options,
5067 double option_modifier,
5068 struct GNUNET_TIME_Relative connect_timeout,
5069 unsigned int connect_attempts,
5070 GNUNET_TESTING_NotifyCompletion
5071 notify_callback, void *notify_cls)
5075 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
5076 #if VERBOSE_TOPOLOGY
5077 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5078 _("Creating clique CONNECT topology\n"));
5080 create_clique (pg, &add_connections, CONNECT);
5082 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
5083 #if VERBOSE_TOPOLOGY
5084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5085 _("Creating small world (ring) CONNECT topology\n"));
5087 create_small_world_ring (pg, &add_connections, CONNECT);
5089 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
5090 #if VERBOSE_TOPOLOGY
5091 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5092 _("Creating small world (2d-torus) CONNECT topology\n"));
5094 create_small_world (pg, &add_connections, CONNECT);
5096 case GNUNET_TESTING_TOPOLOGY_RING:
5097 #if VERBOSE_TOPOLOGY
5098 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5099 _("Creating ring CONNECT topology\n"));
5101 create_ring (pg, &add_connections, CONNECT);
5103 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
5104 #if VERBOSE_TOPOLOGY
5105 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5106 _("Creating 2d torus CONNECT topology\n"));
5108 create_2d_torus (pg, &add_connections, CONNECT);
5110 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
5111 #if VERBOSE_TOPOLOGY
5112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5113 _("Creating Erdos-Renyi CONNECT topology\n"));
5115 create_erdos_renyi (pg, &add_connections, CONNECT);
5117 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
5118 #if VERBOSE_TOPOLOGY
5119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5120 _("Creating InterNAT CONNECT topology\n"));
5122 create_nated_internet (pg, &add_connections, CONNECT);
5124 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
5125 #if VERBOSE_TOPOLOGY
5126 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5127 _("Creating Scale Free CONNECT topology\n"));
5129 create_scale_free (pg, &add_connections, CONNECT);
5131 case GNUNET_TESTING_TOPOLOGY_LINE:
5132 #if VERBOSE_TOPOLOGY
5133 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5134 _("Creating straight line CONNECT topology\n"));
5136 create_line (pg, &add_connections, CONNECT);
5138 case GNUNET_TESTING_TOPOLOGY_NONE:
5139 #if VERBOSE_TOPOLOGY
5140 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5141 _("Creating no CONNECT topology\n"));
5143 copy_allowed_topology (pg);
5146 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5148 ("Unknown topology specification, can't connect peers!\n"));
5149 return GNUNET_SYSERR;
5154 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
5155 #if VERBOSE_TOPOLOGY
5156 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5158 ("Connecting random subset (%'.2f percent) of possible peers\n"),
5159 100 * option_modifier);
5161 choose_random_connections (pg, option_modifier);
5163 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
5164 #if VERBOSE_TOPOLOGY
5165 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5166 _("Connecting a minimum of %u peers each (if possible)\n"),
5167 (unsigned int) option_modifier);
5169 choose_minimum (pg, (unsigned int) option_modifier);
5171 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
5172 #if VERBOSE_TOPOLOGY
5173 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
5175 ("Using DFS to connect a minimum of %u peers each (if possible)\n"),
5176 (unsigned int) option_modifier);
5179 perform_dfs (pg, (int) option_modifier);
5182 case GNUNET_TESTING_TOPOLOGY_OPTION_ADD_CLOSEST:
5183 #if VERBOSE_TOPOLOGY
5184 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5186 ("Finding additional %u closest peers each (if possible)\n"),
5187 (unsigned int) option_modifier);
5190 add_closest (pg, (unsigned int) option_modifier,
5191 &add_connections, CONNECT);
5194 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
5196 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
5202 return connect_topology (pg, connect_timeout, connect_attempts, notify_callback, notify_cls);
5206 * Lookup and return the number of SSH connections to a host.
5208 * @param hostname the hostname to lookup in the list
5209 * @param pg the peergroup that the host belongs to
5211 * @return the number of current ssh connections to the host
5214 count_outstanding_at_host(const char *hostname, struct GNUNET_TESTING_PeerGroup *pg)
5216 struct OutstandingSSH *pos;
5218 while ((pos != NULL) && (strcmp(pos->hostname, hostname) != 0))
5220 GNUNET_assert(pos != NULL);
5221 return pos->outstanding;
5226 * Increment the number of SSH connections to a host by one.
5228 * @param hostname the hostname to lookup in the list
5229 * @param pg the peergroup that the host belongs to
5233 increment_outstanding_at_host(const char *hostname, struct GNUNET_TESTING_PeerGroup *pg)
5235 struct OutstandingSSH *pos;
5237 while ((pos != NULL) && (strcmp(pos->hostname, hostname) != 0))
5239 GNUNET_assert(pos != NULL);
5244 * Decrement the number of SSH connections to a host by one.
5246 * @param hostname the hostname to lookup in the list
5247 * @param pg the peergroup that the host belongs to
5251 decrement_outstanding_at_host(const char *hostname, struct GNUNET_TESTING_PeerGroup *pg)
5253 struct OutstandingSSH *pos;
5255 while ((pos != NULL) && (strcmp(pos->hostname, hostname) != 0))
5257 GNUNET_assert(pos != NULL);
5263 * Callback that is called whenever a hostkey is generated
5264 * for a peer. Call the real callback and decrement the
5265 * starting counter for the peergroup.
5267 * @param cls closure
5268 * @param id identifier for the daemon, NULL on error
5269 * @param d handle for the daemon
5270 * @param emsg error message (NULL on success)
5273 internal_hostkey_callback (void *cls,
5274 const struct GNUNET_PeerIdentity *id,
5275 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5277 struct InternalStartContext *internal_context = cls;
5278 internal_context->peer->pg->starting--;
5279 internal_context->peer->pg->started++;
5280 if (internal_context->hostname != NULL)
5281 decrement_outstanding_at_host(internal_context->hostname, internal_context->peer->pg);
5282 if (internal_context->hostkey_callback != NULL)
5283 internal_context->hostkey_callback (internal_context->hostkey_cls, id, d,
5285 else if (internal_context->peer->pg->started ==
5286 internal_context->peer->pg->total)
5288 internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
5289 GNUNET_TESTING_daemons_continue_startup (internal_context->peer->pg);
5294 * Callback that is called whenever a peer has finished starting.
5295 * Call the real callback and decrement the starting counter
5296 * for the peergroup.
5298 * @param cls closure
5299 * @param id identifier for the daemon, NULL on error
5301 * @param d handle for the daemon
5302 * @param emsg error message (NULL on success)
5305 internal_startup_callback (void *cls,
5306 const struct GNUNET_PeerIdentity *id,
5307 const struct GNUNET_CONFIGURATION_Handle *cfg,
5308 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5310 struct InternalStartContext *internal_context = cls;
5311 internal_context->peer->pg->starting--;
5312 if (internal_context->hostname != NULL)
5313 decrement_outstanding_at_host(internal_context->hostname, internal_context->peer->pg);
5314 if (internal_context->start_cb != NULL)
5315 internal_context->start_cb (internal_context->start_cb_cls, id, cfg, d,
5320 internal_continue_startup (void *cls,
5321 const struct GNUNET_SCHEDULER_TaskContext *tc)
5323 struct InternalStartContext *internal_context = cls;
5325 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
5330 if ((internal_context->peer->pg->starting < internal_context->peer->pg->max_concurrent_ssh) ||
5331 ((internal_context->hostname != NULL) &&
5332 (count_outstanding_at_host(internal_context->hostname, internal_context->peer->pg) < internal_context->peer->pg->max_concurrent_ssh)))
5334 if (internal_context->hostname != NULL)
5335 increment_outstanding_at_host(internal_context->hostname, internal_context->peer->pg);
5336 internal_context->peer->pg->starting++;
5337 GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
5341 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5342 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5343 &internal_continue_startup,
5350 * Callback for informing us about a successful
5351 * or unsuccessful churn start call.
5353 * @param cls a ChurnContext
5354 * @param id the peer identity of the started peer
5355 * @param cfg the handle to the configuration of the peer
5356 * @param d handle to the daemon for the peer
5357 * @param emsg NULL on success, non-NULL on failure
5361 churn_start_callback (void *cls,
5362 const struct GNUNET_PeerIdentity *id,
5363 const struct GNUNET_CONFIGURATION_Handle *cfg,
5364 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5366 struct ChurnRestartContext *startup_ctx = cls;
5367 struct ChurnContext *churn_ctx = startup_ctx->churn_ctx;
5369 unsigned int total_left;
5370 char *error_message;
5372 error_message = NULL;
5375 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5376 "Churn stop callback failed with error `%s'\n", emsg);
5377 churn_ctx->num_failed_start++;
5381 churn_ctx->num_to_start--;
5385 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5386 "Started peer, %d left.\n", churn_ctx->num_to_start);
5390 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
5391 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
5393 if (total_left == 0)
5395 if ((churn_ctx->num_failed_stop > 0)
5396 || (churn_ctx->num_failed_start > 0))
5397 GNUNET_asprintf (&error_message,
5398 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
5399 churn_ctx->num_failed_start,
5400 churn_ctx->num_failed_stop);
5401 churn_ctx->cb (churn_ctx->cb_cls, error_message);
5402 GNUNET_free_non_null (error_message);
5403 GNUNET_free (churn_ctx);
5404 GNUNET_free (startup_ctx);
5410 schedule_churn_restart (void *cls,
5411 const struct GNUNET_SCHEDULER_TaskContext *tc)
5413 struct PeerRestartContext *peer_restart_ctx = cls;
5414 struct ChurnRestartContext *startup_ctx =
5415 peer_restart_ctx->churn_restart_ctx;
5417 if (startup_ctx->outstanding > startup_ctx->pg->max_concurrent_ssh)
5418 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5419 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5420 &schedule_churn_restart, peer_restart_ctx);
5423 GNUNET_TESTING_daemon_start_stopped (peer_restart_ctx->daemon,
5424 startup_ctx->timeout,
5425 &churn_start_callback,
5427 GNUNET_free (peer_restart_ctx);
5434 internal_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
5436 struct InternalStartContext *internal_context = cls;
5438 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
5443 if ((internal_context->peer->pg->starting < internal_context->peer->pg->max_concurrent_ssh) ||
5444 ((internal_context->hostname != NULL) &&
5445 (count_outstanding_at_host(internal_context->hostname, internal_context->peer->pg) < internal_context->peer->pg->max_concurrent_ssh)))
5447 if (internal_context->hostname != NULL)
5448 increment_outstanding_at_host(internal_context->hostname, internal_context->peer->pg);
5449 internal_context->peer->pg->starting++;
5450 internal_context->peer->daemon =
5451 GNUNET_TESTING_daemon_start (internal_context->peer->cfg,
5452 internal_context->timeout,
5453 internal_context->hostname,
5454 internal_context->username,
5455 internal_context->sshport,
5456 internal_context->hostkey,
5457 &internal_hostkey_callback,
5459 &internal_startup_callback,
5464 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5465 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5466 &internal_start, internal_context);
5471 * Function which continues a peer group starting up
5472 * after successfully generating hostkeys for each peer.
5474 * @param pg the peer group to continue starting
5478 GNUNET_TESTING_daemons_continue_startup (struct GNUNET_TESTING_PeerGroup *pg)
5483 for (i = 0; i < pg->total; i++)
5485 GNUNET_SCHEDULER_add_now (&internal_continue_startup,
5486 &pg->peers[i].internal_context);
5487 //GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
5492 * Start count gnunet instances with the same set of transports and
5493 * applications. The port numbers (any option called "PORT") will be
5494 * adjusted to ensure that no two peers running on the same system
5495 * have the same port(s) in their respective configurations.
5497 * @param cfg configuration template to use
5498 * @param total number of daemons to start
5499 * @param max_concurrent_connections for testing, how many peers can
5500 * we connect to simultaneously
5501 * @param max_concurrent_ssh when starting with ssh, how many ssh
5502 * connections will we allow at once (based on remote hosts allowed!)
5503 * @param timeout total time allowed for peers to start
5504 * @param hostkey_callback function to call on each peers hostkey generation
5505 * if NULL, peers will be started by this call, if non-null,
5506 * GNUNET_TESTING_daemons_continue_startup must be called after
5507 * successful hostkey generation
5508 * @param hostkey_cls closure for hostkey callback
5509 * @param cb function to call on each daemon that was started
5510 * @param cb_cls closure for cb
5511 * @param connect_callback function to call each time two hosts are connected
5512 * @param connect_callback_cls closure for connect_callback
5513 * @param hostnames linked list of host structs to use to start peers on
5514 * (NULL to run on localhost only)
5516 * @return NULL on error, otherwise handle to control peer group
5518 struct GNUNET_TESTING_PeerGroup *
5519 GNUNET_TESTING_daemons_start (const struct GNUNET_CONFIGURATION_Handle *cfg,
5521 unsigned int max_concurrent_connections,
5522 unsigned int max_concurrent_ssh,
5523 struct GNUNET_TIME_Relative timeout,
5524 GNUNET_TESTING_NotifyHostkeyCreated
5525 hostkey_callback, void *hostkey_cls,
5526 GNUNET_TESTING_NotifyDaemonRunning cb,
5528 GNUNET_TESTING_NotifyConnection
5529 connect_callback, void *connect_callback_cls,
5530 const struct GNUNET_TESTING_Host *hostnames)
5532 struct GNUNET_TESTING_PeerGroup *pg;
5533 const struct GNUNET_TESTING_Host *hostpos;
5539 const char *hostname;
5540 const char *username;
5541 char *baseservicehome;
5542 char *newservicehome;
5544 char *hostkeys_file;
5547 struct GNUNET_DISK_FileHandle *fd;
5548 struct GNUNET_CONFIGURATION_Handle *pcfg;
5550 unsigned int hostcnt;
5557 uint64_t total_hostkeys;
5558 struct GNUNET_OS_Process *proc;
5568 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
5570 pg->notify_connection = connect_callback;
5571 pg->notify_connection_cls = connect_callback_cls;
5573 pg->max_timeout = GNUNET_TIME_relative_to_absolute (timeout);
5574 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
5575 pg->max_outstanding_connections = max_concurrent_connections;
5576 pg->max_concurrent_ssh = max_concurrent_ssh;
5577 if (NULL != hostnames)
5580 hostpos = hostnames;
5581 while (hostpos != NULL)
5583 hostpos = hostpos->next;
5586 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
5589 hostpos = hostnames;
5590 while (hostpos != NULL)
5592 pg->hosts[off].minport = LOW_PORT;
5593 pg->hosts[off].hostname = GNUNET_strdup (hostpos->hostname);
5594 if (hostpos->username != NULL)
5595 pg->hosts[off].username = GNUNET_strdup (hostpos->username);
5596 pg->hosts[off].sshport = hostpos->port;
5597 hostpos = hostpos->next;
5607 pg->num_hosts = off;
5611 /* skip leading spaces */
5612 while ((0 != *hostnames) && (isspace ((unsigned char) *hostnames)))
5615 while ('\0' != *rpos)
5617 if (isspace ((unsigned char) *rpos))
5621 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
5623 start = GNUNET_strdup (hostnames);
5625 while ('\0' != *pos)
5627 if (isspace ((unsigned char) *pos))
5630 if (strlen (start) > 0)
5632 pg->hosts[off].minport = LOW_PORT;
5633 pg->hosts[off++].hostname = start;
5639 if (strlen (start) > 0)
5641 pg->hosts[off].minport = LOW_PORT;
5642 pg->hosts[off++].hostname = start;
5646 GNUNET_free (start);
5647 GNUNET_free (pg->hosts);
5651 minport = 0; /* make gcc happy */
5660 /* Create the servicehome directory for each remote peer */
5661 GNUNET_assert(GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", "SERVICEHOME",
5663 for (i = 0; i < pg->num_hosts; i++)
5665 struct OutstandingSSH *ssh_entry;
5666 ssh_entry = GNUNET_malloc(sizeof(struct OutstandingSSH));
5667 ssh_entry->hostname = pg->hosts[i].hostname; /* Don't free! */
5668 GNUNET_CONTAINER_DLL_insert(pg->ssh_head, pg->ssh_tail, ssh_entry);
5669 if (NULL != pg->hosts[i].username)
5670 GNUNET_asprintf (&arg, "%s@%s", pg->hosts[i].username, pg->hosts[i].hostname);
5672 GNUNET_asprintf (&arg, "%s", pg->hosts[i].hostname);
5673 if (pg->hosts[i].sshport != 0)
5675 GNUNET_asprintf (&ssh_port_str, "%d", pg->hosts[i].sshport);
5676 proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
5677 "ssh", "-P", ssh_port_str,
5681 arg, "mkdir -p", baseservicehome, NULL);
5684 proc = GNUNET_OS_start_process (NULL, NULL, "ssh",
5685 "ssh", arg, "mkdir -p", baseservicehome, NULL);
5686 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Creating remote dir with command ssh %s %s %s\n", arg, " mkdir -p ", baseservicehome);
5687 GNUNET_OS_process_wait(proc);
5689 GNUNET_free(baseservicehome);
5691 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_string (cfg, "TESTING", "HOSTKEYSFILE",
5694 if (GNUNET_YES != GNUNET_DISK_file_test (hostkeys_file))
5695 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Couldn't read hostkeys file!\n");
5698 /* Check hostkey file size, read entire thing into memory */
5699 fd = GNUNET_DISK_file_open (hostkeys_file, GNUNET_DISK_OPEN_READ,
5700 GNUNET_DISK_PERM_NONE);
5703 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", hostkeys_file);
5707 if (GNUNET_YES != GNUNET_DISK_file_size (hostkeys_file, &fs, GNUNET_YES))
5710 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Found file size %llu for hostkeys, expect hostkeys to be size %d\n", fs, HOSTKEYFILESIZE);
5712 if (fs % HOSTKEYFILESIZE != 0)
5714 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "File size %llu seems incorrect for hostkeys...\n", fs);
5718 total_hostkeys = fs / HOSTKEYFILESIZE;
5719 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Will read %llu hostkeys from file\n", total_hostkeys);
5720 pg->hostkey_data = GNUNET_malloc_large (fs);
5721 GNUNET_assert (fs == GNUNET_DISK_file_read (fd, pg->hostkey_data, fs));
5722 GNUNET_assert(GNUNET_OK == GNUNET_DISK_file_close(fd));
5725 GNUNET_free(hostkeys_file);
5728 for (off = 0; off < total; off++)
5732 hostname = pg->hosts[off % hostcnt].hostname;
5733 username = pg->hosts[off % hostcnt].username;
5734 sshport = pg->hosts[off % hostcnt].sshport;
5735 pcfg = make_config (cfg,
5737 &pg->hosts[off % hostcnt].minport,
5738 &upnum, hostname, &fdnum);
5745 pcfg = make_config (cfg, off, &minport, &upnum, hostname, &fdnum);
5750 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5752 ("Could not create configuration for peer number %u on `%s'!\n"),
5753 off, hostname == NULL ? "localhost" : hostname);
5758 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
5761 GNUNET_asprintf (&newservicehome, "%s/%d/", baseservicehome, off);
5762 GNUNET_free (baseservicehome);
5766 tmpdir = getenv ("TMPDIR");
5767 tmpdir = tmpdir ? tmpdir : "/tmp";
5768 GNUNET_asprintf (&newservicehome,
5770 tmpdir, "gnunet-testing-test-test", off);
5772 GNUNET_CONFIGURATION_set_value_string (pcfg,
5774 "SERVICEHOME", newservicehome);
5775 GNUNET_free (newservicehome);
5776 pg->peers[off].cfg = pcfg;
5778 /* Can we do this later? */
5779 pg->peers[off].allowed_peers =
5780 GNUNET_CONTAINER_multihashmap_create (total);
5781 pg->peers[off].connect_peers =
5782 GNUNET_CONTAINER_multihashmap_create (total);
5783 pg->peers[off].blacklisted_peers =
5784 GNUNET_CONTAINER_multihashmap_create (total);
5787 pg->peers[off].pg = pg;
5788 pg->peers[off].internal_context.peer = &pg->peers[off];
5789 pg->peers[off].internal_context.timeout = timeout;
5790 pg->peers[off].internal_context.hostname = hostname;
5791 pg->peers[off].internal_context.username = username;
5792 pg->peers[off].internal_context.sshport = sshport;
5793 if (pg->hostkey_data != NULL)
5794 pg->peers[off].internal_context.hostkey = &pg->hostkey_data[off * HOSTKEYFILESIZE];
5795 pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
5796 pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
5797 pg->peers[off].internal_context.start_cb = cb;
5798 pg->peers[off].internal_context.start_cb_cls = cb_cls;
5800 GNUNET_SCHEDULER_add_now (&internal_start,
5801 &pg->peers[off].internal_context);
5808 * Get a daemon by number, so callers don't have to do nasty
5809 * offsetting operation.
5811 struct GNUNET_TESTING_Daemon *
5812 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg,
5813 unsigned int position)
5815 if (position < pg->total)
5816 return pg->peers[position].daemon;
5822 * Get a daemon by peer identity, so callers can
5823 * retrieve the daemon without knowing it's offset.
5825 * @param pg the peer group to retrieve the daemon from
5826 * @param peer_id the peer identity of the daemon to retrieve
5828 * @return the daemon on success, or NULL if no such peer identity is found
5830 struct GNUNET_TESTING_Daemon *
5831 GNUNET_TESTING_daemon_get_by_id (struct GNUNET_TESTING_PeerGroup *pg,
5832 struct GNUNET_PeerIdentity *peer_id)
5836 for (i = 0; i < pg->total; i++)
5839 memcmp (&pg->peers[i].daemon->id, peer_id,
5840 sizeof (struct GNUNET_PeerIdentity)))
5841 return pg->peers[i].daemon;
5848 * Prototype of a function that will be called when a
5849 * particular operation was completed the testing library.
5851 * @param cls closure (a struct RestartContext)
5852 * @param id id of the peer that was restarted
5853 * @param cfg handle to the configuration of the peer
5854 * @param d handle to the daemon that was restarted
5855 * @param emsg NULL on success
5858 restart_callback (void *cls,
5859 const struct GNUNET_PeerIdentity *id,
5860 const struct GNUNET_CONFIGURATION_Handle *cfg,
5861 struct GNUNET_TESTING_Daemon *d, const char *emsg)
5863 struct RestartContext *restart_context = cls;
5867 restart_context->peers_restarted++;
5871 restart_context->peers_restart_failed++;
5874 if (restart_context->peers_restarted == restart_context->peer_group->total)
5876 restart_context->callback (restart_context->callback_cls, NULL);
5877 GNUNET_free (restart_context);
5879 else if (restart_context->peers_restart_failed +
5880 restart_context->peers_restarted ==
5881 restart_context->peer_group->total)
5883 restart_context->callback (restart_context->callback_cls,
5884 "Failed to restart peers!");
5885 GNUNET_free (restart_context);
5891 * Callback for informing us about a successful
5892 * or unsuccessful churn stop call.
5894 * @param cls a ChurnContext
5895 * @param emsg NULL on success, non-NULL on failure
5899 churn_stop_callback (void *cls, const char *emsg)
5901 struct ShutdownContext *shutdown_ctx = cls;
5902 struct ChurnContext *churn_ctx = shutdown_ctx->cb_cls;
5903 unsigned int total_left;
5904 char *error_message;
5906 error_message = NULL;
5907 shutdown_ctx->outstanding--;
5911 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5912 "Churn stop callback failed with error `%s'\n", emsg);
5913 churn_ctx->num_failed_stop++;
5917 churn_ctx->num_to_stop--;
5921 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
5922 "Stopped peer, %d left.\n", churn_ctx->num_to_stop);
5925 (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) +
5926 (churn_ctx->num_to_start - churn_ctx->num_failed_start);
5928 if (total_left == 0)
5930 if ((churn_ctx->num_failed_stop > 0)
5931 || (churn_ctx->num_failed_start > 0))
5933 GNUNET_asprintf (&error_message,
5934 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
5935 churn_ctx->num_failed_start,
5936 churn_ctx->num_failed_stop);
5938 churn_ctx->cb (churn_ctx->cb_cls, error_message);
5939 GNUNET_free_non_null (error_message);
5940 GNUNET_free (churn_ctx);
5941 GNUNET_free (shutdown_ctx);
5946 * Count the number of running peers.
5948 * @param pg handle for the peer group
5950 * @return the number of currently running peers in the peer group
5953 GNUNET_TESTING_daemons_running (struct GNUNET_TESTING_PeerGroup *pg)
5956 unsigned int running = 0;
5957 for (i = 0; i < pg->total; i++)
5959 if (pg->peers[i].daemon->running == GNUNET_YES)
5961 GNUNET_assert (running != -1);
5969 * Task to rate limit the number of outstanding peer shutdown
5970 * requests. This is necessary for making sure we don't do
5971 * too many ssh connections at once, but is generally nicer
5972 * to any system as well (graduated task starts, as opposed
5973 * to calling gnunet-arm N times all at once).
5976 schedule_churn_shutdown_task (void *cls,
5977 const struct GNUNET_SCHEDULER_TaskContext *tc)
5979 struct PeerShutdownContext *peer_shutdown_ctx = cls;
5980 struct ShutdownContext *shutdown_ctx;
5981 struct ChurnContext *churn_ctx;
5982 GNUNET_assert (peer_shutdown_ctx != NULL);
5983 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
5984 GNUNET_assert (shutdown_ctx != NULL);
5985 churn_ctx = (struct ChurnContext *)shutdown_ctx->cb_cls;
5986 if (shutdown_ctx->outstanding > churn_ctx->pg->max_concurrent_ssh)
5987 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
5988 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
5989 &schedule_churn_shutdown_task,
5993 shutdown_ctx->outstanding++;
5994 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
5995 shutdown_ctx->timeout, shutdown_ctx->cb,
5996 shutdown_ctx, GNUNET_NO, GNUNET_YES);
5997 GNUNET_free (peer_shutdown_ctx);
6002 * Simulate churn by stopping some peers (and possibly
6003 * re-starting others if churn is called multiple times). This
6004 * function can only be used to create leave-join churn (peers "never"
6005 * leave for good). First "voff" random peers that are currently
6006 * online will be taken offline; then "von" random peers that are then
6007 * offline will be put back online. No notifications will be
6008 * generated for any of these operations except for the callback upon
6011 * @param pg handle for the peer group
6012 * @param voff number of peers that should go offline
6013 * @param von number of peers that should come back online;
6014 * must be zero on first call (since "testbed_start"
6015 * always starts all of the peers)
6016 * @param timeout how long to wait for operations to finish before
6018 * @param cb function to call at the end
6019 * @param cb_cls closure for cb
6022 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
6025 struct GNUNET_TIME_Relative timeout,
6026 GNUNET_TESTING_NotifyCompletion cb,
6029 struct ChurnContext *churn_ctx;
6030 struct ShutdownContext *shutdown_ctx;
6031 struct PeerShutdownContext *peer_shutdown_ctx;
6032 struct PeerRestartContext *peer_restart_ctx;
6033 struct ChurnRestartContext *churn_startup_ctx;
6035 unsigned int running;
6036 unsigned int stopped;
6037 unsigned int total_running;
6038 unsigned int total_stopped;
6040 unsigned int *running_arr;
6041 unsigned int *stopped_arr;
6042 unsigned int *running_permute;
6043 unsigned int *stopped_permute;
6048 if ((von == 0) && (voff == 0)) /* No peers at all? */
6054 for (i = 0; i < pg->total; i++)
6056 if (pg->peers[i].daemon->running == GNUNET_YES)
6058 GNUNET_assert (running != -1);
6063 GNUNET_assert (stopped != -1);
6070 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6071 "Trying to stop more peers than are currently running!\n");
6072 cb (cb_cls, "Trying to stop more peers than are currently running!");
6078 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
6079 "Trying to start more peers than are currently stopped!\n");
6080 cb (cb_cls, "Trying to start more peers than are currently stopped!");
6084 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6088 running_arr = GNUNET_malloc (running * sizeof (unsigned int));
6092 stopped_arr = GNUNET_malloc (stopped * sizeof (unsigned int));
6094 running_permute = NULL;
6095 stopped_permute = NULL;
6099 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, running);
6102 GNUNET_CRYPTO_random_permute (GNUNET_CRYPTO_QUALITY_WEAK, stopped);
6104 total_running = running;
6105 total_stopped = stopped;
6109 churn_ctx->num_to_start = von;
6110 churn_ctx->num_to_stop = voff;
6112 churn_ctx->cb_cls = cb_cls;
6115 for (i = 0; i < pg->total; i++)
6117 if (pg->peers[i].daemon->running == GNUNET_YES)
6119 GNUNET_assert ((running_arr != NULL) && (total_running > running));
6120 running_arr[running] = i;
6125 GNUNET_assert ((stopped_arr != NULL) && (total_stopped > stopped));
6126 stopped_arr[stopped] = i;
6131 GNUNET_assert (running >= voff);
6134 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6135 shutdown_ctx->cb = &churn_stop_callback;
6136 shutdown_ctx->cb_cls = churn_ctx;
6137 shutdown_ctx->total_peers = voff;
6138 shutdown_ctx->timeout = timeout;
6141 for (i = 0; i < voff; i++)
6144 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n",
6145 running_permute[i]);
6147 GNUNET_assert (running_arr != NULL);
6148 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6149 peer_shutdown_ctx->daemon =
6150 pg->peers[running_arr[running_permute[i]]].daemon;
6151 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6152 GNUNET_SCHEDULER_add_now (&schedule_churn_shutdown_task,
6156 GNUNET_TESTING_daemon_stop (pg->peers[running_arr[running_permute[i]]].daemon,
6158 &churn_stop_callback, churn_ctx,
6159 GNUNET_NO, GNUNET_YES); */
6162 GNUNET_assert (stopped >= von);
6165 churn_startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6166 churn_startup_ctx->churn_ctx = churn_ctx;
6167 churn_startup_ctx->timeout = timeout;
6168 churn_startup_ctx->pg = pg;
6170 for (i = 0; i < von; i++)
6173 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n",
6174 stopped_permute[i]);
6176 GNUNET_assert (stopped_arr != NULL);
6177 peer_restart_ctx = GNUNET_malloc (sizeof (struct PeerRestartContext));
6178 peer_restart_ctx->churn_restart_ctx = churn_startup_ctx;
6179 peer_restart_ctx->daemon =
6180 pg->peers[stopped_arr[stopped_permute[i]]].daemon;
6181 GNUNET_SCHEDULER_add_now (&schedule_churn_restart, peer_restart_ctx);
6183 GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon,
6184 timeout, &churn_start_callback, churn_ctx); */
6187 GNUNET_free_non_null (running_arr);
6188 GNUNET_free_non_null (stopped_arr);
6189 GNUNET_free_non_null (running_permute);
6190 GNUNET_free_non_null (stopped_permute);
6195 * Restart all peers in the given group.
6197 * @param pg the handle to the peer group
6198 * @param callback function to call on completion (or failure)
6199 * @param callback_cls closure for the callback function
6202 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg,
6203 GNUNET_TESTING_NotifyCompletion callback,
6206 struct RestartContext *restart_context;
6211 restart_context = GNUNET_malloc (sizeof (struct RestartContext));
6212 restart_context->peer_group = pg;
6213 restart_context->peers_restarted = 0;
6214 restart_context->callback = callback;
6215 restart_context->callback_cls = callback_cls;
6217 for (off = 0; off < pg->total; off++)
6219 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon,
6220 &restart_callback, restart_context);
6226 * Start or stop an individual peer from the given group.
6228 * @param pg handle to the peer group
6229 * @param offset which peer to start or stop
6230 * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
6231 * @param timeout how long to wait for shutdown
6232 * @param cb function to call at the end
6233 * @param cb_cls closure for cb
6236 GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg,
6237 unsigned int offset,
6239 struct GNUNET_TIME_Relative timeout,
6240 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6242 struct ShutdownContext *shutdown_ctx;
6243 struct ChurnRestartContext *startup_ctx;
6244 struct ChurnContext *churn_ctx;
6246 if (GNUNET_NO == desired_status)
6248 if (NULL != pg->peers[offset].daemon)
6250 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6251 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6252 churn_ctx->num_to_start = 0;
6253 churn_ctx->num_to_stop = 1;
6255 churn_ctx->cb_cls = cb_cls;
6256 shutdown_ctx->cb_cls = churn_ctx;
6257 GNUNET_TESTING_daemon_stop (pg->peers[offset].daemon,
6258 timeout, &churn_stop_callback,
6259 shutdown_ctx, GNUNET_NO, GNUNET_YES);
6262 else if (GNUNET_YES == desired_status)
6264 if (NULL == pg->peers[offset].daemon)
6266 startup_ctx = GNUNET_malloc (sizeof (struct ChurnRestartContext));
6267 churn_ctx = GNUNET_malloc (sizeof (struct ChurnContext));
6268 churn_ctx->num_to_start = 1;
6269 churn_ctx->num_to_stop = 0;
6271 churn_ctx->cb_cls = cb_cls;
6272 startup_ctx->churn_ctx = churn_ctx;
6273 GNUNET_TESTING_daemon_start_stopped (pg->peers[offset].daemon,
6274 timeout, &churn_start_callback,
6284 * Callback for shutting down peers in a peer group.
6286 * @param cls closure (struct ShutdownContext)
6287 * @param emsg NULL on success
6290 internal_shutdown_callback (void *cls, const char *emsg)
6292 struct ShutdownContext *shutdown_ctx = cls;
6295 shutdown_ctx->outstanding--;
6298 shutdown_ctx->peers_down++;
6302 shutdown_ctx->peers_failed++;
6305 if ((shutdown_ctx->cb != NULL)
6306 && (shutdown_ctx->peers_down + shutdown_ctx->peers_failed ==
6307 shutdown_ctx->total_peers))
6309 if (shutdown_ctx->peers_failed > 0)
6310 shutdown_ctx->cb (shutdown_ctx->cb_cls,
6311 "Not all peers successfully shut down!");
6313 shutdown_ctx->cb (shutdown_ctx->cb_cls, NULL);
6315 GNUNET_free (shutdown_ctx->pg->peers);
6316 GNUNET_free_non_null(shutdown_ctx->pg->hostkey_data);
6317 for (off = 0; off < shutdown_ctx->pg->num_hosts; off++)
6319 GNUNET_free (shutdown_ctx->pg->hosts[off].hostname);
6320 GNUNET_free_non_null (shutdown_ctx->pg->hosts[off].username);
6322 GNUNET_free_non_null (shutdown_ctx->pg->hosts);
6323 GNUNET_free (shutdown_ctx->pg);
6325 GNUNET_free (shutdown_ctx);
6331 * Task to rate limit the number of outstanding peer shutdown
6332 * requests. This is necessary for making sure we don't do
6333 * too many ssh connections at once, but is generally nicer
6334 * to any system as well (graduated task starts, as opposed
6335 * to calling gnunet-arm N times all at once).
6338 schedule_shutdown_task (void *cls,
6339 const struct GNUNET_SCHEDULER_TaskContext *tc)
6341 struct PeerShutdownContext *peer_shutdown_ctx = cls;
6342 struct ShutdownContext *shutdown_ctx;
6344 GNUNET_assert (peer_shutdown_ctx != NULL);
6345 shutdown_ctx = peer_shutdown_ctx->shutdown_ctx;
6346 GNUNET_assert (shutdown_ctx != NULL);
6348 if (shutdown_ctx->outstanding > shutdown_ctx->pg->max_concurrent_ssh)
6349 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
6350 (GNUNET_TIME_UNIT_MILLISECONDS, 100),
6351 &schedule_shutdown_task, peer_shutdown_ctx);
6354 shutdown_ctx->outstanding++;
6355 GNUNET_TESTING_daemon_stop (peer_shutdown_ctx->daemon,
6356 shutdown_ctx->timeout,
6357 &internal_shutdown_callback, shutdown_ctx,
6358 GNUNET_YES, GNUNET_NO);
6359 GNUNET_free (peer_shutdown_ctx);
6364 * Shutdown all peers started in the given group.
6366 * @param pg handle to the peer group
6367 * @param timeout how long to wait for shutdown
6368 * @param cb callback to notify upon success or failure
6369 * @param cb_cls closure for cb
6372 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg,
6373 struct GNUNET_TIME_Relative timeout,
6374 GNUNET_TESTING_NotifyCompletion cb, void *cb_cls)
6377 struct ShutdownContext *shutdown_ctx;
6378 struct PeerShutdownContext *peer_shutdown_ctx;
6380 struct PeerConnection *conn_iter;
6381 struct PeerConnection *temp_conn;
6384 GNUNET_assert (pg->total > 0);
6386 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext));
6387 shutdown_ctx->cb = cb;
6388 shutdown_ctx->cb_cls = cb_cls;
6389 shutdown_ctx->total_peers = pg->total;
6390 shutdown_ctx->timeout = timeout;
6391 shutdown_ctx->pg = pg;
6392 /* shtudown_ctx->outstanding = 0; */
6394 for (off = 0; off < pg->total; off++)
6396 GNUNET_assert (NULL != pg->peers[off].daemon);
6397 peer_shutdown_ctx = GNUNET_malloc (sizeof (struct PeerShutdownContext));
6398 peer_shutdown_ctx->daemon = pg->peers[off].daemon;
6399 peer_shutdown_ctx->shutdown_ctx = shutdown_ctx;
6400 GNUNET_SCHEDULER_add_now (&schedule_shutdown_task, peer_shutdown_ctx);
6402 if (NULL != pg->peers[off].cfg)
6403 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
6405 conn_iter = pg->peers[off].allowed_peers_head;
6406 while (conn_iter != NULL)
6408 temp_conn = conn_iter->next;
6409 GNUNET_free(conn_iter);
6410 conn_iter = temp_conn;
6413 conn_iter = pg->peers[off].connect_peers_head;
6414 while (conn_iter != NULL)
6416 temp_conn = conn_iter->next;
6417 GNUNET_free(conn_iter);
6418 conn_iter = temp_conn;
6421 conn_iter = pg->peers[off].blacklisted_peers_head;
6422 while (conn_iter != NULL)
6424 temp_conn = conn_iter->next;
6425 GNUNET_free(conn_iter);
6426 conn_iter = temp_conn;
6429 conn_iter = pg->peers[off].connect_peers_working_set_head;
6430 while (conn_iter != NULL)
6432 temp_conn = conn_iter->next;
6433 GNUNET_free(conn_iter);
6434 conn_iter = temp_conn;
6437 if (pg->peers[off].allowed_peers != NULL)
6438 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].allowed_peers);
6439 if (pg->peers[off].connect_peers != NULL)
6440 GNUNET_CONTAINER_multihashmap_destroy (pg->peers[off].connect_peers);
6441 if (pg->peers[off].blacklisted_peers != NULL)
6442 GNUNET_CONTAINER_multihashmap_destroy (pg->
6443 peers[off].blacklisted_peers);
6450 /* end of testing_group.c */