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 DEBUG_CHURN GNUNET_NO
38 * Lowest port used for GNUnet testing. Should be high enough to not
39 * conflict with other applications running on the hosts but be low
40 * enough to not conflict with client-ports (typically starting around
43 #define LOW_PORT 10000
46 * Highest port used for GNUnet testing. Should be low enough to not
47 * conflict with the port range for "local" ports (client apps; see
48 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
50 #define HIGH_PORT 56000
52 #define MAX_OUTSTANDING_CONNECTIONS 10
54 #define MAX_CONCURRENT_HOSTKEYS 16
56 #define MAX_CONCURRENT_STARTING 50
58 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
60 #define CONNECT_ATTEMPTS 8
63 * Prototype of a function called whenever two peers would be connected
64 * in a certain topology.
66 typedef int (*GNUNET_TESTING_ConnectionProcessor)(struct GNUNET_TESTING_PeerGroup *pg,
72 * Context for handling churning a peer group
77 * Callback used to notify of churning finished
79 GNUNET_TESTING_NotifyCompletion cb;
82 * Closure for callback
87 * Number of peers that still need to be started
89 unsigned int num_to_start;
92 * Number of peers that still need to be stopped
94 unsigned int num_to_stop;
97 * Number of peers that failed to start
99 unsigned int num_failed_start;
102 * Number of peers that failed to stop
104 unsigned int num_failed_stop;
107 struct RestartContext
110 * The group of peers being restarted
112 struct GNUNET_TESTING_PeerGroup *peer_group;
115 * How many peers have been restarted thus far
117 unsigned int peers_restarted;
120 * How many peers got an error when restarting
122 unsigned int peers_restart_failed;
125 * The function to call once all peers have been restarted
127 GNUNET_TESTING_NotifyCompletion callback;
130 * Closure for callback function
137 struct ShutdownContext
140 * Total peers to wait for
145 * Number of peers successfully shut down
150 * Number of peers failed to shut down
155 * Callback to call when all peers either
156 * shutdown or failed to shutdown
158 GNUNET_TESTING_NotifyCompletion cb;
166 struct CreateTopologyContext
170 * Function to call with number of connections
172 GNUNET_TESTING_NotifyConnections cont;
175 * Closure for connection notification
181 struct PeerConnection
186 struct PeerConnection *next;
189 * Pointer to daemon handle
191 struct GNUNET_TESTING_Daemon *daemon;
196 struct InternalStartContext
199 * Pointer to peerdata
201 struct PeerData *peer;
204 * Timeout for peer startup
206 struct GNUNET_TIME_Relative timeout;
209 * Client callback for hostkey notification
211 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback;
214 * Closure for hostkey_callback
219 * Client callback for peer start notification
221 GNUNET_TESTING_NotifyDaemonRunning start_cb;
229 * Hostname, where to start the peer
231 const char *hostname;
235 * Data we keep per peer.
240 * (Initial) configuration of the host.
241 * (initial because clients could change
242 * it and we would not know about those
245 struct GNUNET_CONFIGURATION_Handle *cfg;
248 * Handle for controlling the daemon.
250 struct GNUNET_TESTING_Daemon *daemon;
253 * The peergroup this peer belongs to.
255 struct GNUNET_TESTING_PeerGroup *pg;
258 * Hash map of allowed peer connections (F2F created topology)
260 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
263 * Hash map of blacklisted peers
265 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
268 * Hash map of peer connections
270 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
273 * Temporary hash map of peer connections
275 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
278 * Temporary variable for topology creation, should be reset before
279 * creating any topology so the count is valid once finished.
284 * Context to keep track of peers being started, to
285 * stagger hostkey generation and peer startup.
287 struct InternalStartContext internal_context;
292 * Data we keep per host.
302 * Lowest port that we have not yet used
308 struct TopologyIterateContext
311 * Callback for notifying of two connected peers.
313 GNUNET_TESTING_NotifyTopology topology_cb;
316 * Closure for topology_cb
321 * Number of peers currently connected to.
323 unsigned int connected;
326 * Number of peers we have finished iterating.
328 unsigned int completed;
331 * Number of peers total.
336 struct TopologyCoreContext
338 struct TopologyIterateContext *topology_context;
339 struct GNUNET_TESTING_Daemon *daemon;
343 * Handle to a group of GNUnet peers.
345 struct GNUNET_TESTING_PeerGroup
350 struct GNUNET_SCHEDULER_Handle *sched;
353 * Configuration template.
355 const struct GNUNET_CONFIGURATION_Handle *cfg;
358 * Function to call on each started daemon.
360 //GNUNET_TESTING_NotifyDaemonRunning cb;
368 * Function to call on each topology connection created
370 GNUNET_TESTING_NotifyConnection notify_connection;
373 * Callback for notify_connection
375 void *notify_connection_cls;
378 * NULL-terminated array of information about
381 struct HostData *hosts;
384 * Array of "total" peers.
386 struct PeerData *peers;
389 * Number of peers in this group.
394 * At what time should we fail the peer startup process?
396 struct GNUNET_TIME_Absolute max_timeout;
399 * How many peers are being started right now?
401 unsigned int starting;
404 * How many peers have already been started?
406 unsigned int started;
411 struct GNUNET_CONFIGURATION_Handle *ret;
412 const char *hostname;
418 struct ConnectContext
420 struct GNUNET_TESTING_Daemon *first;
422 struct GNUNET_TESTING_Daemon *second;
424 struct GNUNET_TESTING_PeerGroup *pg;
428 * Convert unique ID to hash code.
430 * @param uid unique ID to convert
431 * @param hash set to uid (extended with zeros)
434 hash_from_uid (uint32_t uid,
435 GNUNET_HashCode *hash)
437 memset (hash, 0, sizeof(GNUNET_HashCode));
438 *((uint32_t*)hash) = uid;
442 * Convert hash code to unique ID.
444 * @param uid unique ID to convert
445 * @param hash set to uid (extended with zeros)
448 uid_from_hash (const GNUNET_HashCode *hash, uint32_t *uid)
450 memcpy (uid, hash, sizeof(uint32_t));
454 * Number of connects we are waiting on, allows us to rate limit
457 static int outstanding_connects;
460 * Get a topology from a string input.
462 * @param topology where to write the retrieved topology
463 * @param topology_string The string to attempt to
464 * get a configuration value from
465 * @return GNUNET_YES if topology string matched a
466 * known topology, GNUNET_NO if not
469 GNUNET_TESTING_topology_get(enum GNUNET_TESTING_Topology *topology, char * topology_string)
472 * Strings representing topologies in enum
474 static const char * topology_strings[] =
477 * A clique (everyone connected to everyone else).
482 * Small-world network (2d torus plus random links).
487 * Small-world network (ring plus random links).
507 * Certain percentage of peers are unable to communicate directly
508 * replicating NAT conditions
513 * Scale free topology.
518 * Straight line topology.
523 * All peers are disconnected.
531 if (topology_string == NULL)
533 while (topology_strings[curr] != NULL)
535 if (strcasecmp(topology_strings[curr], topology_string) == 0)
542 *topology = GNUNET_TESTING_TOPOLOGY_NONE;
548 * Get connect topology option from string input.
550 * @param topology_option where to write the retrieved topology
551 * @param topology_string The string to attempt to
552 * get a configuration value from
553 * @return GNUNET_YES if string matched a known
554 * topology option, GNUNET_NO if not
557 GNUNET_TESTING_topology_option_get (enum GNUNET_TESTING_TopologyOption *topology_option,
558 char * topology_string)
561 * Options for connecting a topology as strings.
563 static const char * topology_option_strings[] =
566 * Try to connect all peers specified in the topology.
571 * Choose a random subset of connections to create.
573 "CONNECT_RANDOM_SUBSET",
576 * Create at least X connections for each peer.
581 * Using a depth first search, create one connection
582 * per peer. If any are missed (graph disconnected)
583 * start over at those peers until all have at least one
589 * No options specified.
597 if (topology_string == NULL)
599 while (NULL != topology_option_strings[curr])
601 if (strcasecmp(topology_option_strings[curr], topology_string) == 0)
603 *topology_option = curr;
608 *topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_NONE;
613 * Function to iterate over options. Copies
614 * the options to the target configuration,
615 * updating PORT values as needed.
618 * @param section name of the section
619 * @param option name of the option
620 * @param value value of the option
623 update_config (void *cls,
624 const char *section, const char *option, const char *value)
626 struct UpdateContext *ctx = cls;
631 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
635 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
640 if (0 == strcmp (option, "UNIXPATH"))
642 GNUNET_snprintf (uval,
644 "/tmp/test-service-%s-%u",
650 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
652 value = ctx->hostname;
655 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
660 * Create a new configuration using the given configuration
661 * as a template; however, each PORT in the existing cfg
662 * must be renumbered by incrementing "*port". If we run
663 * out of "*port" numbers, return NULL.
665 * @param cfg template configuration
666 * @param port port numbers to use, update to reflect
667 * port numbers that were used
668 * @param upnum number to make unix domain socket names unique
669 * @param hostname hostname of the controlling host, to allow control connections from
671 * @return new configuration, NULL on error
673 static struct GNUNET_CONFIGURATION_Handle *
674 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
677 const char *hostname)
679 struct UpdateContext uc;
687 uc.ret = GNUNET_CONFIGURATION_create ();
688 uc.hostname = hostname;
690 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
691 if (uc.nport >= HIGH_PORT)
694 GNUNET_CONFIGURATION_destroy (uc.ret);
698 if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
700 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
701 GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
702 GNUNET_CONFIGURATION_set_value_string(uc.ret, "transport", "ACCEPT_FROM", allowed_hosts);
703 GNUNET_free_non_null(control_host);
704 GNUNET_free(allowed_hosts);
708 /* arm needs to know to allow connections from the host on which it is running,
709 * otherwise gnunet-arm is unable to connect to it in some instances */
710 if (hostname != NULL)
712 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
713 GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
714 GNUNET_free(allowed_hosts);
717 *port = (uint16_t) uc.nport;
724 * Add entries to the peers connect list
726 * @param pg the peer group we are working with
727 * @param first index of the first peer
728 * @param second index of the second peer
730 * @return the number of connections added
731 * technically should only be 0 or 2
735 add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
741 GNUNET_HashCode hash_first;
742 GNUNET_HashCode hash_second;
744 hash_from_uid(first, &hash_first);
745 hash_from_uid(second, &hash_second);
747 add_first = GNUNET_NO;
748 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second))
750 add_first = GNUNET_YES;
753 add_second = GNUNET_NO;
754 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first))
756 add_second = GNUNET_YES;
762 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
763 pg->peers[first].num_connections++;
769 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
770 pg->peers[second].num_connections++;
779 * Add entries to the peers allowed connections list
781 * @param pg the peer group we are working with
782 * @param first index of the first peer
783 * @param second index of the second peer
785 * @return the number of connections added (can be 0, 1 or 2)
786 * technically should only be 0 or 2, but the small price
787 * of iterating over the lists (hashmaps in the future)
788 * for being sure doesn't bother me!
792 add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
796 struct PeerConnection *first_iter;
797 struct PeerConnection *second_iter;
798 struct PeerConnection *new_first;
799 struct PeerConnection *new_second;
804 GNUNET_HashCode hash_first;
805 GNUNET_HashCode hash_second;
807 hash_from_uid(first, &hash_first);
808 hash_from_uid(second, &hash_second);
810 add_first = GNUNET_NO;
811 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second))
813 add_first = GNUNET_YES;
816 add_second = GNUNET_NO;
817 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first))
819 add_second = GNUNET_YES;
822 first_iter = pg->peers[first].connected_peers;
823 while (first_iter != NULL)
825 if (first_iter->daemon == pg->peers[second].daemon)
826 add_first = GNUNET_NO;
827 first_iter = first_iter->next;
830 second_iter = pg->peers[second].connected_peers;
831 add_second = GNUNET_YES;
832 while (second_iter != NULL)
834 if (second_iter->daemon == pg->peers[first].daemon)
835 add_second = GNUNET_NO;
836 second_iter = second_iter->next;
843 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
845 new_first = GNUNET_malloc(sizeof(struct PeerConnection));
846 new_first->daemon = pg->peers[second].daemon;
847 new_first->next = pg->peers[first].connected_peers;
848 pg->peers[first].connected_peers = new_first;
850 pg->peers[first].num_connections++;
856 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
858 new_second = GNUNET_malloc(sizeof(struct PeerConnection));
859 new_second->daemon = pg->peers[first].daemon;
860 new_second->next = pg->peers[second].connected_peers;
861 pg->peers[second].connected_peers = new_second;
862 pg->peers[first].num_connections++;
864 pg->peers[second].num_connections++;
872 * Add entries to the peers blacklisted list
874 * @param pg the peer group we are working with
875 * @param first index of the first peer
876 * @param second index of the second peer
878 * @return the number of connections added (can be 0, 1 or 2)
882 blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
887 GNUNET_HashCode hash_first;
888 GNUNET_HashCode hash_second;
890 hash_from_uid(first, &hash_first);
891 hash_from_uid(second, &hash_second);
893 add_first = GNUNET_NO;
894 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second))
896 add_first = GNUNET_YES;
899 add_second = GNUNET_NO;
900 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first))
902 add_second = GNUNET_YES;
908 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
909 pg->peers[first].num_connections++;
915 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
916 pg->peers[second].num_connections++;
924 * Remove entries from the peers blacklisted list
926 * @param pg the peer group we are working with
927 * @param first index of the first peer
928 * @param second index of the second peer
930 * @return the number of connections removed (can be 0, 1 or 2)
934 unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
939 GNUNET_HashCode hash_first;
940 GNUNET_HashCode hash_second;
942 hash_from_uid(first, &hash_first);
943 hash_from_uid(second, &hash_second);
945 remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second);
946 remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first);
951 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon));
957 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon));
965 * Scale free network construction as described in:
967 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
969 * Start with a network of "one" peer, then progressively add
970 * peers up to the total number. At each step, iterate over
971 * all possible peers and connect new peer based on number of
972 * existing connections of the target peer.
974 * @param pg the peer group we are dealing with
975 * @param proc the connection processor to use
977 * @return the number of connections created
980 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
983 unsigned int total_connections;
984 unsigned int outer_count;
986 unsigned int previous_total_connections;
990 GNUNET_assert(pg->total > 1);
992 /* Add a connection between the first two nodes */
993 total_connections = proc(pg, 0, 1);
995 for (outer_count = 1; outer_count < pg->total; outer_count++)
997 previous_total_connections = total_connections;
998 for (i = 0; i < outer_count; i++)
1000 probability = pg->peers[i].num_connections / (double)previous_total_connections;
1001 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1002 UINT64_MAX)) / ( (double) UINT64_MAX);
1004 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1005 "Considering connecting peer %d to peer %d\n",
1008 if (random < probability)
1011 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1012 "Connecting peer %d to peer %d\n",
1015 total_connections += proc(pg, outer_count, i);
1020 return total_connections;
1024 * Create a topology given a peer group (set of running peers)
1025 * and a connection processor.
1027 * @param pg the peergroup to create the topology on
1028 * @param proc the connection processor to call to actually set
1029 * up connections between two peers
1031 * @return the number of connections that were set up
1035 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1039 unsigned int natLog;
1040 unsigned int randomPeer;
1041 double random, logNModifier, percentage;
1042 unsigned int smallWorldConnections;
1047 unsigned int useAnd;
1048 int connect_attempts;
1050 logNModifier = 0.5; /* FIXME: default value? */
1051 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1056 if (sscanf(p_string, "%lf", &logNModifier) != 1)
1057 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1058 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1062 GNUNET_free (p_string);
1064 percentage = 0.5; /* FIXME: default percentage? */
1065 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1070 if (sscanf(p_string, "%lf", &percentage) != 1)
1071 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1072 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1076 GNUNET_free (p_string);
1078 natLog = log (pg->total);
1079 connsPerPeer = ceil (natLog * logNModifier);
1081 if (connsPerPeer % 2 == 1)
1084 smallWorldConnections = 0;
1085 connect_attempts = 0;
1086 for (i = 0; i < pg->total; i++)
1089 max = i + connsPerPeer / 2;
1090 min = i - connsPerPeer / 2;
1092 if (max > pg->total - 1)
1094 max = max - pg->total;
1100 min = pg->total - 1 + min;
1104 for (j = 0; j < connsPerPeer / 2; j++)
1106 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1107 UINT64_MAX) / ( (double) UINT64_MAX));
1108 if (random < percentage)
1110 /* Connect to uniformly selected random peer */
1112 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1114 while ((((randomPeer < max) && (randomPeer > min))
1115 && (useAnd == 0)) || (((randomPeer > min)
1116 || (randomPeer < max))
1120 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1123 smallWorldConnections +=
1124 proc (pg, i, randomPeer);
1128 nodeToConnect = i + j + 1;
1129 if (nodeToConnect > pg->total - 1)
1131 nodeToConnect = nodeToConnect - pg->total;
1134 proc (pg, i, nodeToConnect);
1140 connect_attempts += smallWorldConnections;
1142 return connect_attempts;
1146 * Create a topology given a peer group (set of running peers)
1147 * and a connection processor.
1149 * @param pg the peergroup to create the topology on
1150 * @param proc the connection processor to call to actually set
1151 * up connections between two peers
1153 * @return the number of connections that were set up
1157 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1159 unsigned int outer_count, inner_count;
1160 unsigned int cutoff;
1161 int connect_attempts;
1162 double nat_percentage;
1165 nat_percentage = 0.6; /* FIXME: default percentage? */
1166 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1171 if (sscanf(p_string, "%lf", &nat_percentage) != 1)
1172 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1173 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1177 GNUNET_free (p_string);
1182 cutoff = (unsigned int) (nat_percentage * pg->total);
1184 connect_attempts = 0;
1186 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1188 for (inner_count = outer_count + 1; inner_count < pg->total;
1191 if ((outer_count > cutoff) || (inner_count > cutoff))
1194 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1195 "Connecting peer %d to peer %d\n",
1196 outer_count, inner_count);
1198 connect_attempts += proc(pg, outer_count, inner_count);
1203 return connect_attempts;
1208 * Create a topology given a peer group (set of running peers)
1209 * and a connection processor.
1211 * @param pg the peergroup to create the topology on
1212 * @param proc the connection processor to call to actually set
1213 * up connections between two peers
1215 * @return the number of connections that were set up
1219 create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1221 unsigned int i, j, k;
1222 unsigned int square;
1225 unsigned int toggle = 1;
1226 unsigned int nodeToConnect;
1227 unsigned int natLog;
1228 unsigned int node1Row;
1229 unsigned int node1Col;
1230 unsigned int node2Row;
1231 unsigned int node2Col;
1232 unsigned int distance;
1233 double probability, random, percentage;
1234 unsigned int smallWorldConnections;
1236 int connect_attempts;
1237 square = floor (sqrt (pg->total));
1241 percentage = 0.5; /* FIXME: default percentage? */
1242 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1247 if (sscanf(p_string, "%lf", &percentage) != 1)
1248 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1249 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1253 GNUNET_free (p_string);
1255 probability = 0.5; /* FIXME: default percentage? */
1256 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1261 if (sscanf(p_string, "%lf", &probability) != 1)
1262 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1263 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1267 GNUNET_free (p_string);
1269 if (square * square != pg->total)
1271 while (rows * cols < pg->total)
1273 if (toggle % 2 == 0)
1282 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1283 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1287 connect_attempts = 0;
1288 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1289 * to the node to its right and above. Once this is over, we'll have our torus!
1290 * Special case for the last node (if the rows and columns are not equal), connect
1291 * to the first in the row to maintain topology.
1293 for (i = 0; i < pg->total; i++)
1295 /* First connect to the node to the right */
1296 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1297 nodeToConnect = i + 1;
1298 else if (i + 1 == pg->total)
1299 nodeToConnect = rows * cols - cols;
1301 nodeToConnect = i - cols + 1;
1303 connect_attempts += proc (pg, i, nodeToConnect);
1306 nodeToConnect = (rows * cols) - cols + i;
1308 nodeToConnect = i - cols;
1310 if (nodeToConnect < pg->total)
1311 connect_attempts += proc (pg, i, nodeToConnect);
1313 natLog = log (pg->total);
1314 #if VERBOSE_TESTING > 2
1315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1316 _("natural log of %d is %d, will run %d iterations\n"),
1317 pg->total, natLog, (int) (natLog * percentage));
1318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
1320 smallWorldConnections = 0;
1321 for (i = 0; i < (int) (natLog * percentage); i++)
1323 for (j = 0; j < pg->total; j++)
1325 /* Determine the row and column of node at position j on the 2d torus */
1326 node1Row = j / cols;
1327 node1Col = j - (node1Row * cols);
1328 for (k = 0; k < pg->total; k++)
1330 /* Determine the row and column of node at position k on the 2d torus */
1331 node2Row = k / cols;
1332 node2Col = k - (node2Row * cols);
1333 /* Simple Cartesian distance */
1334 distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
1337 /* Calculate probability as 1 over the square of the distance */
1338 probability = 1.0 / (distance * distance);
1339 /* Choose a random value between 0 and 1 */
1340 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1341 UINT64_MAX)) / ( (double) UINT64_MAX);
1342 /* If random < probability, then connect the two nodes */
1343 if (random < probability)
1344 smallWorldConnections += proc (pg, j, k);
1350 connect_attempts += smallWorldConnections;
1351 #if VERBOSE_TESTING > 2
1352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1353 _("Total connections added for small world: %d!\n"),
1354 smallWorldConnections);
1356 return connect_attempts;
1360 * Create a topology given a peer group (set of running peers)
1361 * and a connection processor.
1363 * @param pg the peergroup to create the topology on
1364 * @param proc the connection processor to call to actually set
1365 * up connections between two peers
1367 * @return the number of connections that were set up
1371 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1374 unsigned int outer_count;
1375 unsigned int inner_count;
1376 int connect_attempts;
1380 probability = 0.5; /* FIXME: default percentage? */
1381 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1386 if (sscanf(p_string, "%lf", &probability) != 1)
1387 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1388 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1392 GNUNET_free (p_string);
1394 connect_attempts = 0;
1395 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1397 for (inner_count = outer_count + 1; inner_count < pg->total;
1400 temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1401 UINT64_MAX)) / ( (double) UINT64_MAX);
1403 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1404 _("rand is %f probability is %f\n"), temp_rand,
1407 if (temp_rand < probability)
1409 connect_attempts += proc (pg, outer_count, inner_count);
1414 return connect_attempts;
1418 * Create a topology given a peer group (set of running peers)
1419 * and a connection processor.
1421 * @param pg the peergroup to create the topology on
1422 * @param proc the connection processor to call to actually set
1423 * up connections between two peers
1425 * @return the number of connections that were set up
1429 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1432 unsigned int square;
1435 unsigned int toggle = 1;
1436 unsigned int nodeToConnect;
1437 int connect_attempts;
1439 connect_attempts = 0;
1441 square = floor (sqrt (pg->total));
1445 if (square * square != pg->total)
1447 while (rows * cols < pg->total)
1449 if (toggle % 2 == 0)
1458 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1459 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1462 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1463 * to the node to its right and above. Once this is over, we'll have our torus!
1464 * Special case for the last node (if the rows and columns are not equal), connect
1465 * to the first in the row to maintain topology.
1467 for (i = 0; i < pg->total; i++)
1469 /* First connect to the node to the right */
1470 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1471 nodeToConnect = i + 1;
1472 else if (i + 1 == pg->total)
1473 nodeToConnect = rows * cols - cols;
1475 nodeToConnect = i - cols + 1;
1477 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1478 "Connecting peer %d to peer %d\n",
1481 connect_attempts += proc(pg, i, nodeToConnect);
1483 /* Second connect to the node immediately above */
1485 nodeToConnect = (rows * cols) - cols + i;
1487 nodeToConnect = i - cols;
1489 if (nodeToConnect < pg->total)
1492 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1493 "Connecting peer %d to peer %d\n",
1496 connect_attempts += proc(pg, i, nodeToConnect);
1501 return connect_attempts;
1506 * Create a topology given a peer group (set of running peers)
1507 * and a connection processor.
1509 * @param pg the peergroup to create the topology on
1510 * @param proc the connection processor to call to actually set
1511 * up connections between two peers
1513 * @return the number of connections that were set up
1517 create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1519 unsigned int outer_count;
1520 unsigned int inner_count;
1521 int connect_attempts;
1523 connect_attempts = 0;
1525 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1527 for (inner_count = outer_count + 1; inner_count < pg->total;
1531 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1532 "Connecting peer %d to peer %d\n",
1533 outer_count, inner_count);
1535 connect_attempts += proc(pg, outer_count, inner_count);
1539 return connect_attempts;
1543 * Create a topology given a peer group (set of running peers)
1544 * and a connection processor.
1546 * @param pg the peergroup to create the topology on
1547 * @param proc the connection processor to call to actually set
1548 * up connections between two peers
1550 * @return the number of connections that were set up
1554 create_line (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1557 int connect_attempts;
1559 connect_attempts = 0;
1561 /* Connect each peer to the next highest numbered peer */
1562 for (count = 0; count < pg->total - 1; count++)
1565 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1566 "Connecting peer %d to peer %d\n",
1569 connect_attempts += proc(pg, count, count + 1);
1572 return connect_attempts;
1576 * Create a topology given a peer group (set of running peers)
1577 * and a connection processor.
1579 * @param pg the peergroup to create the topology on
1580 * @param proc the connection processor to call to actually set
1581 * up connections between two peers
1583 * @return the number of connections that were set up
1587 create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1590 int connect_attempts;
1592 connect_attempts = 0;
1594 /* Connect each peer to the next highest numbered peer */
1595 for (count = 0; count < pg->total - 1; count++)
1598 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1599 "Connecting peer %d to peer %d\n",
1602 connect_attempts += proc(pg, count, count + 1);
1605 /* Connect the last peer to the first peer */
1606 connect_attempts += proc(pg, pg->total - 1, 0);
1608 return connect_attempts;
1613 * Iterator for writing friends of a peer to a file.
1615 * @param cls closure, an open writable file handle
1616 * @param key the key the daemon was stored under
1617 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1619 * @return GNUNET_YES to continue iteration
1621 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
1622 * with a single file_iterator that takes a closure which contains
1623 * the prefix to write before the peer. Then this could be used
1624 * for blacklisting multiple transports and writing the friend
1625 * file. I'm sure *someone* will complain loudly about other
1626 * things that negate these functions even existing so no point in
1630 friend_file_iterator (void *cls,
1631 const GNUNET_HashCode * key,
1634 FILE *temp_friend_handle = cls;
1635 struct GNUNET_TESTING_Daemon *peer = value;
1636 struct GNUNET_PeerIdentity *temppeer;
1637 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1639 temppeer = &peer->id;
1640 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1641 fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
1646 struct BlacklistContext
1649 * The (open) file handle to write to
1651 FILE *temp_file_handle;
1654 * The transport that this peer will be blacklisted on.
1660 * Iterator for writing blacklist data to appropriate files.
1662 * @param cls closure, an open writable file handle
1663 * @param key the key the daemon was stored under
1664 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1666 * @return GNUNET_YES to continue iteration
1669 blacklist_file_iterator (void *cls,
1670 const GNUNET_HashCode * key,
1673 struct BlacklistContext *blacklist_ctx = cls;
1674 //FILE *temp_blacklist_handle = cls;
1675 struct GNUNET_TESTING_Daemon *peer = value;
1676 struct GNUNET_PeerIdentity *temppeer;
1677 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1679 temppeer = &peer->id;
1680 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1681 fprintf(blacklist_ctx->temp_file_handle, "%s:%s\n", blacklist_ctx->transport, (char *)&peer_enc);
1687 * Create the friend files based on the PeerConnection's
1688 * of each peer in the peer group, and copy the files
1689 * to the appropriate place
1691 * @param pg the peer group we are dealing with
1694 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
1696 FILE *temp_friend_handle;
1697 unsigned int pg_iter;
1698 char *temp_service_path;
1702 enum GNUNET_OS_ProcessStatusType type;
1703 unsigned long return_code;
1708 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1709 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1711 mytemp = GNUNET_DISK_mktemp("friends");
1712 GNUNET_assert(mytemp != NULL);
1713 temp_friend_handle = fopen (mytemp, "wt");
1714 GNUNET_assert(temp_friend_handle != NULL);
1715 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle);
1716 fclose(temp_friend_handle);
1719 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1721 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1722 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1725 if (UNLINK (mytemp) != 0)
1726 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1727 GNUNET_free (mytemp);
1731 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1733 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
1734 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1735 "mv", mytemp, arg, NULL);
1737 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1738 _("Copying file with command cp %s %s\n"), mytemp, arg);
1743 else /* Remote, scp the file to the correct place */
1745 if (NULL != pg->peers[pg_iter].daemon->username)
1746 GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1748 GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1749 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1750 "scp", mytemp, arg, NULL);
1753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1754 _("Copying file with command scp %s %s\n"), mytemp, arg);
1758 GNUNET_free (temp_service_path);
1759 GNUNET_free (mytemp);
1763 ret = GNUNET_SYSERR;
1764 while ((count < max_wait) && (ret != GNUNET_OK))
1767 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1770 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1771 _("Checking copy status of file %d\n"), pg_iter);
1773 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1775 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1777 ret = GNUNET_SYSERR;
1779 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1781 ret = GNUNET_SYSERR;
1785 pidarr[pg_iter] = 0;
1787 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1788 _("File %d copied\n"), pg_iter);
1794 if (ret == GNUNET_SYSERR)
1796 /* FIXME: why sleep here? -CG */
1802 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1803 _("Finished copying all friend files!\n"));
1805 GNUNET_free(pidarr);
1811 * Create the blacklist files based on the PeerConnection's
1812 * of each peer in the peer group, and copy the files
1813 * to the appropriate place.
1815 * @param pg the peer group we are dealing with
1816 * @param transports space delimited list of transports to blacklist
1819 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *transports)
1821 FILE *temp_file_handle;
1822 static struct BlacklistContext blacklist_ctx;
1823 unsigned int pg_iter;
1824 char *temp_service_path;
1828 enum GNUNET_OS_ProcessStatusType type;
1829 unsigned long return_code;
1836 char *temp_transports;
1838 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1839 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1841 mytemp = GNUNET_DISK_mktemp("blacklist");
1842 GNUNET_assert(mytemp != NULL);
1843 temp_file_handle = fopen (mytemp, "wt");
1844 GNUNET_assert(temp_file_handle != NULL);
1845 temp_transports = GNUNET_strdup(transports);
1846 blacklist_ctx.temp_file_handle = temp_file_handle;
1847 transport_len = strlen(temp_transports) + 1;
1850 for (i = 0; i < transport_len; i++)
1852 if ((temp_transports[i] == ' ') && (pos == NULL))
1853 continue; /* At start of string (whitespace) */
1854 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
1856 temp_transports[i] = '\0';
1857 blacklist_ctx.transport = pos;
1858 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, &blacklist_ctx);
1860 } /* At beginning of actual string */
1861 else if (pos == NULL)
1863 pos = &temp_transports[i];
1867 GNUNET_free (temp_transports);
1868 fclose(temp_file_handle);
1871 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1873 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1874 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1877 if (UNLINK (mytemp) != 0)
1878 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1879 GNUNET_free (mytemp);
1883 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1885 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
1886 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1887 "mv", mytemp, arg, NULL);
1889 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1890 _("Copying file with command cp %s %s\n"), mytemp, arg);
1895 else /* Remote, scp the file to the correct place */
1897 if (NULL != pg->peers[pg_iter].daemon->username)
1898 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1900 GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1901 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1902 "scp", mytemp, arg, NULL);
1905 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1906 _("Copying file with command scp %s %s\n"), mytemp, arg);
1910 GNUNET_free (temp_service_path);
1911 GNUNET_free (mytemp);
1915 ret = GNUNET_SYSERR;
1916 while ((count < max_wait) && (ret != GNUNET_OK))
1919 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1922 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1923 _("Checking copy status of file %d\n"), pg_iter);
1925 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1927 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1929 ret = GNUNET_SYSERR;
1931 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1933 ret = GNUNET_SYSERR;
1937 pidarr[pg_iter] = 0;
1939 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1940 _("File %d copied\n"), pg_iter);
1946 if (ret == GNUNET_SYSERR)
1948 /* FIXME: why sleep here? -CG */
1954 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1955 _("Finished copying all blacklist files!\n"));
1957 GNUNET_free(pidarr);
1963 * Internal notification of a connection, kept so that we can ensure some connections
1964 * happen instead of flooding all testing daemons with requests to connect.
1966 static void internal_connect_notify (void *cls,
1967 const struct GNUNET_PeerIdentity *first,
1968 const struct GNUNET_PeerIdentity *second,
1970 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1971 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1972 struct GNUNET_TESTING_Daemon *first_daemon,
1973 struct GNUNET_TESTING_Daemon *second_daemon,
1976 struct GNUNET_TESTING_PeerGroup *pg = cls;
1977 outstanding_connects--;
1979 pg->notify_connection(pg->notify_connection_cls, first, second, distance, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
1984 * Either delay a connection (because there are too many outstanding)
1985 * or schedule it for right now.
1987 * @param cls a connection context
1988 * @param tc the task runtime context
1990 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1992 struct ConnectContext *connect_context = cls;
1994 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1997 if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1999 #if VERBOSE_TESTING > 2
2000 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2001 _("Delaying connect, we have too many outstanding connections!\n"));
2003 GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &schedule_connect, connect_context);
2007 #if VERBOSE_TESTING > 2
2008 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2009 _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
2011 outstanding_connects++;
2012 GNUNET_TESTING_daemons_connect (connect_context->first,
2013 connect_context->second,
2016 &internal_connect_notify,
2017 connect_context->pg);
2018 GNUNET_free(connect_context);
2024 * Iterator for actually scheduling connections to be created
2025 * between two peers.
2027 * @param cls closure, a GNUNET_TESTING_Daemon
2028 * @param key the key the second Daemon was stored under
2029 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2031 * @return GNUNET_YES to continue iteration
2034 connect_iterator (void *cls,
2035 const GNUNET_HashCode * key,
2038 struct PeerData *first = cls;
2039 struct GNUNET_TESTING_Daemon *second = value;
2040 struct ConnectContext *connect_context;
2042 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
2043 connect_context->pg = first->pg;
2044 connect_context->first = first->daemon;
2045 connect_context->second = second;
2046 GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
2053 * Iterator for copying all entries in the allowed hashmap to the
2056 * @param cls closure, a GNUNET_TESTING_Daemon
2057 * @param key the key the second Daemon was stored under
2058 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2060 * @return GNUNET_YES to continue iteration
2063 copy_topology_iterator (void *cls,
2064 const GNUNET_HashCode * key,
2067 struct PeerData *first = cls;
2069 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2075 * Make the peers to connect the same as those that are allowed to be
2078 * @param pg the peer group
2081 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
2083 unsigned int pg_iter;
2088 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2090 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, ©_topology_iterator, &pg->peers[pg_iter]);
2091 if (GNUNET_SYSERR == ret)
2092 return GNUNET_SYSERR;
2094 total = total + ret;
2102 * Connect the topology as specified by the PeerConnection's
2103 * of each peer in the peer group
2105 * @param pg the peer group we are dealing with
2106 * @return the number of connections that will be attempted
2109 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
2111 unsigned int pg_iter;
2115 struct PeerConnection *connection_iter;
2116 struct ConnectContext *connect_context;
2120 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2122 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
2123 if (GNUNET_SYSERR == ret)
2124 return GNUNET_SYSERR;
2126 total = total + ret;
2130 while (connection_iter != NULL)
2132 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
2133 connect_context->pg = pg;
2134 connect_context->first = ;
2135 connect_context->second = connection_iter->daemon;
2136 GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
2137 connection_iter = connection_iter->next;
2146 * Takes a peer group and creates a topology based on the
2147 * one specified. Creates a topology means generates friend
2148 * files for the peers so they can only connect to those allowed
2149 * by the topology. This will only have an effect once peers
2150 * are started if the FRIENDS_ONLY option is set in the base
2151 * config. Also takes an optional restrict topology which
2152 * disallows connections based on a particular transport
2153 * UNLESS they are specified in the restricted topology.
2155 * @param pg the peer group struct representing the running peers
2156 * @param topology which topology to connect the peers in
2157 * @param restrict_topology allow only direct TCP connections in this topology
2158 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
2159 * @param restrict_transports space delimited list of transports to blacklist
2160 * to create restricted topology
2162 * @return the maximum number of connections were all allowed peers
2163 * connected to each other
2166 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
2167 enum GNUNET_TESTING_Topology topology,
2168 enum GNUNET_TESTING_Topology restrict_topology,
2169 char *restrict_transports)
2172 int num_connections;
2173 int unblacklisted_connections;
2175 GNUNET_assert (pg->notify_connection != NULL);
2180 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2182 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2183 _("Creating clique topology\n"));
2185 num_connections = create_clique (pg, &add_allowed_connections);
2187 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2189 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2190 _("Creating small world (ring) topology\n"));
2192 num_connections = create_small_world_ring (pg, &add_allowed_connections);
2194 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2196 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2197 _("Creating small world (2d-torus) topology\n"));
2199 num_connections = create_small_world (pg, &add_allowed_connections);
2201 case GNUNET_TESTING_TOPOLOGY_RING:
2203 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2204 _("Creating ring topology\n"));
2206 num_connections = create_ring (pg, &add_allowed_connections);
2208 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2210 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2211 _("Creating 2d torus topology\n"));
2213 num_connections = create_2d_torus (pg, &add_allowed_connections);
2215 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2217 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2218 _("Creating Erdos-Renyi topology\n"));
2220 num_connections = create_erdos_renyi (pg, &add_allowed_connections);
2222 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2224 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2225 _("Creating InterNAT topology\n"));
2227 num_connections = create_nated_internet (pg, &add_allowed_connections);
2229 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2231 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2232 _("Creating Scale Free topology\n"));
2234 num_connections = create_scale_free (pg, &add_allowed_connections);
2236 case GNUNET_TESTING_TOPOLOGY_LINE:
2238 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2239 _("Creating straight line topology\n"));
2241 num_connections = create_line (pg, &add_allowed_connections);
2243 case GNUNET_TESTING_TOPOLOGY_NONE:
2245 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2246 _("Creating no allowed topology (all peers can connect at core level)\n"));
2248 num_connections = 0;
2251 num_connections = 0;
2255 if (num_connections < 0)
2256 return GNUNET_SYSERR;
2258 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
2260 ret = create_and_copy_friend_files(pg);
2261 if (ret != GNUNET_OK)
2264 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2265 _("Failed during friend file copying!\n"));
2267 return GNUNET_SYSERR;
2272 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2273 _("Friend files created/copied successfully!\n"));
2278 /* Use the create clique method to initially set all connections as blacklisted. */
2279 if (restrict_topology != GNUNET_TESTING_TOPOLOGY_NONE)
2280 create_clique (pg, &blacklist_connections);
2281 unblacklisted_connections = 0;
2282 /* Un-blacklist connections as per the topology specified */
2283 switch (restrict_topology)
2285 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2287 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2288 _("Blacklisting all but clique topology\n"));
2290 unblacklisted_connections = create_clique (pg, &unblacklist_connections);
2292 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2295 _("Blacklisting all but small world (ring) topology\n"));
2297 unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections);
2299 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2301 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2302 _("Blacklisting all but small world (2d-torus) topology\n"));
2304 unblacklisted_connections = create_small_world (pg, &unblacklist_connections);
2306 case GNUNET_TESTING_TOPOLOGY_RING:
2308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2309 _("Blacklisting all but ring topology\n"));
2311 unblacklisted_connections = create_ring (pg, &unblacklist_connections);
2313 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2316 _("Blacklisting all but 2d torus topology\n"));
2318 unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections);
2320 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2323 _("Blacklisting all but Erdos-Renyi topology\n"));
2325 unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections);
2327 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2329 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2330 _("Blacklisting all but InterNAT topology\n"));
2332 unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections);
2334 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2336 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2337 _("Blacklisting all but Scale Free topology\n"));
2339 unblacklisted_connections = create_scale_free (pg, &unblacklist_connections);
2341 case GNUNET_TESTING_TOPOLOGY_LINE:
2343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2344 _("Blacklisting all but straight line topology\n"));
2346 unblacklisted_connections = create_line (pg, &unblacklist_connections);
2348 case GNUNET_TESTING_TOPOLOGY_NONE:
2350 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2351 _("Creating no blacklist topology (all peers can connect at transport level)\n"));
2357 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
2359 ret = create_and_copy_blacklist_files(pg, restrict_transports);
2360 if (ret != GNUNET_OK)
2363 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2364 _("Failed during blacklist file copying!\n"));
2366 return GNUNET_SYSERR;
2371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2372 _("Blacklist files created/copied successfully!\n"));
2376 return num_connections;
2379 struct RandomContext
2384 struct GNUNET_TESTING_PeerGroup *pg;
2387 * uid of the first peer
2392 * Peer data for first peer.
2394 struct PeerData *first;
2397 * Random percentage to use
2402 struct MinimumContext
2407 struct GNUNET_TESTING_PeerGroup *pg;
2410 * uid of the first peer
2415 * Peer data for first peer.
2417 struct PeerData *first;
2420 * Number of conns per peer
2422 unsigned int num_to_add;
2425 * Permuted array of all possible connections. Only add the Nth
2426 * peer if it's in the Nth position.
2428 unsigned int *pg_array;
2431 * What number is the current element we are iterating over?
2433 unsigned int current;
2441 struct GNUNET_TESTING_PeerGroup *pg;
2444 * uid of the first peer
2449 * uid of the second peer
2451 uint32_t second_uid;
2454 * Peer data for first peer.
2456 struct PeerData *first;
2459 * Which peer has been chosen as the one to add?
2461 unsigned int chosen;
2464 * What number is the current element we are iterating over?
2466 unsigned int current;
2470 * Iterator for choosing random peers to connect.
2472 * @param cls closure, a RandomContext
2473 * @param key the key the second Daemon was stored under
2474 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2476 * @return GNUNET_YES to continue iteration
2479 random_connect_iterator (void *cls,
2480 const GNUNET_HashCode * key,
2483 struct RandomContext *random_ctx = cls;
2484 double random_number;
2485 uint32_t second_pos;
2486 GNUNET_HashCode first_hash;
2487 random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
2488 UINT64_MAX)) / ( (double) UINT64_MAX);
2489 if (random_number < random_ctx->percentage)
2491 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2493 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
2494 uid_from_hash(key, &second_pos);
2495 hash_from_uid(random_ctx->first_uid, &first_hash);
2496 GNUNET_assert(random_ctx->pg->total > second_pos);
2497 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon));
2503 * Iterator for adding at least X peers to a peers connection set.
2505 * @param cls closure, MinimumContext
2506 * @param key the key the second Daemon was stored under
2507 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2509 * @return GNUNET_YES to continue iteration
2512 minimum_connect_iterator (void *cls,
2513 const GNUNET_HashCode * key,
2516 struct MinimumContext *min_ctx = cls;
2517 uint32_t second_pos;
2518 GNUNET_HashCode first_hash;
2521 if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
2523 for (i = 0; i < min_ctx->num_to_add; i++)
2525 if (min_ctx->pg_array[i] == min_ctx->current)
2527 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2528 uid_from_hash(key, &second_pos);
2529 hash_from_uid(min_ctx->first_uid, &first_hash);
2530 GNUNET_assert(min_ctx->pg->total > second_pos);
2531 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->pg->peers[second_pos].connect_peers_working_set, &first_hash, min_ctx->first->daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2532 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
2533 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon));
2540 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
2546 * Iterator for adding peers to a connection set based on a depth first search.
2548 * @param cls closure, MinimumContext
2549 * @param key the key the second daemon was stored under
2550 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2552 * @return GNUNET_YES to continue iteration
2555 dfs_connect_iterator (void *cls,
2556 const GNUNET_HashCode * key,
2559 struct DFSContext *dfs_ctx = cls;
2560 GNUNET_HashCode first_hash;
2562 if (dfs_ctx->current == dfs_ctx->chosen)
2564 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(dfs_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2565 uid_from_hash(key, &dfs_ctx->second_uid);
2566 hash_from_uid(dfs_ctx->first_uid, &first_hash);
2567 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(dfs_ctx->pg->peers[dfs_ctx->second_uid].connect_peers_working_set, &first_hash, dfs_ctx->first->daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2568 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(dfs_ctx->pg->peers[dfs_ctx->second_uid].connect_peers, &first_hash, dfs_ctx->first->daemon));
2569 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
2570 return GNUNET_NO; /* We have found our peer, don't iterate more */
2579 * From the set of connections possible, choose percentage percent of connections
2580 * to actually connect.
2582 * @param pg the peergroup we are dealing with
2583 * @param percentage what percent of total connections to make
2586 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage)
2588 struct RandomContext random_ctx;
2591 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2593 random_ctx.first_uid = pg_iter;
2594 random_ctx.first = &pg->peers[pg_iter];
2595 random_ctx.percentage = percentage;
2597 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2598 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx);
2599 /* Now remove the old connections */
2600 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2601 /* And replace with the random set */
2602 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2607 * From the set of connections possible, choose at least num connections per
2610 * @param pg the peergroup we are dealing with
2611 * @param num how many connections at least should each peer have (if possible)?
2614 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2616 struct MinimumContext minimum_ctx;
2619 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2621 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2624 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2626 minimum_ctx.first_uid = pg_iter;
2627 minimum_ctx.pg_array = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK,
2628 GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2629 minimum_ctx.first = &pg->peers[pg_iter];
2630 minimum_ctx.pg = pg;
2631 minimum_ctx.num_to_add = num;
2632 minimum_ctx.current = 0;
2633 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers,
2634 &minimum_connect_iterator,
2638 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2640 /* Remove the "old" connections */
2641 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2642 /* And replace with the working set */
2643 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2650 count_workingset_connections(struct GNUNET_TESTING_PeerGroup *pg)
2653 unsigned int pg_iter;
2657 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2659 count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2666 static unsigned int count_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg)
2669 unsigned int pg_iter;
2673 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2675 count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers);
2682 * From the set of connections possible, choose at least num connections per
2683 * peer based on depth first traversal of peer connections. If DFS leaves
2684 * peers unconnected, ensure those peers get connections.
2686 * @param pg the peergroup we are dealing with
2687 * @param num how many connections at least should each peer have (if possible)?
2690 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2692 struct DFSContext dfs_ctx;
2695 uint32_t starting_peer;
2696 uint32_t least_connections;
2697 GNUNET_HashCode second_hash;
2699 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2701 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2706 while ((count_workingset_connections(pg) < num * pg->total) && (count_allowed_connections(pg) > 0))
2708 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
2710 least_connections = -1; /* Set to very high number */
2711 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2713 if (GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set) < least_connections)
2715 starting_peer = pg_iter;
2716 least_connections = GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2721 if (GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
2727 /* Choose a random peer from the chosen peers set of connections to add */
2728 dfs_ctx.chosen = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers));
2729 dfs_ctx.first_uid = starting_peer;
2730 dfs_ctx.first = &pg->peers[starting_peer];
2732 dfs_ctx.current = 0;
2734 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[starting_peer].connect_peers, &dfs_connect_iterator, &dfs_ctx);
2735 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
2736 hash_from_uid(dfs_ctx.second_uid, &second_hash);
2737 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[starting_peer].connect_peers, &second_hash, pg->peers[dfs_ctx.second_uid].daemon));
2738 starting_peer = dfs_ctx.second_uid;
2741 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2743 /* Remove the "old" connections */
2744 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2745 /* And replace with the working set */
2746 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2751 * Internal callback for topology information for a particular peer.
2754 internal_topology_callback(void *cls,
2755 const struct GNUNET_PeerIdentity *peer,
2756 struct GNUNET_TIME_Relative latency, uint32_t distance)
2758 struct TopologyCoreContext *core_ctx = cls;
2759 struct TopologyIterateContext *iter_ctx = core_ctx->topology_context;
2761 if (peer == NULL) /* Either finished, or something went wrongo */
2763 iter_ctx->completed++;
2764 iter_ctx->connected--;
2768 iter_ctx->topology_cb(iter_ctx->cls, &core_ctx->daemon->id, peer, latency, distance, NULL);
2771 if (iter_ctx->completed == iter_ctx->total)
2773 iter_ctx->topology_cb(iter_ctx->cls, NULL, NULL, GNUNET_TIME_relative_get_zero(), 0, NULL);
2774 GNUNET_free(iter_ctx);
2775 GNUNET_free(core_ctx);
2781 * Check running topology iteration tasks, if below max start a new one, otherwise
2782 * schedule for some time in the future.
2785 schedule_get_topology(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
2787 struct TopologyCoreContext *core_context = cls;
2789 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
2792 if (core_context->topology_context->connected > MAX_OUTSTANDING_CONNECTIONS)
2794 #if VERBOSE_TESTING > 2
2795 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2796 _("Delaying connect, we have too many outstanding connections!\n"));
2798 GNUNET_SCHEDULER_add_delayed(core_context->daemon->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &schedule_get_topology, core_context);
2802 #if VERBOSE_TESTING > 2
2803 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2804 _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
2806 core_context->topology_context->connected++;
2807 if (GNUNET_OK != GNUNET_CORE_iterate_peers (core_context->daemon->sched, core_context->daemon->cfg, &internal_topology_callback, core_context))
2808 internal_topology_callback(core_context, NULL, GNUNET_TIME_relative_get_zero(), 0);
2814 * Iterate over all (running) peers in the peer group, retrieve
2815 * all connections that each currently has.
2818 GNUNET_TESTING_get_topology (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyTopology cb, void *cls)
2820 struct TopologyIterateContext *topology_context;
2821 struct TopologyCoreContext *core_ctx;
2823 unsigned int total_count;
2825 topology_context = GNUNET_malloc(sizeof(struct TopologyIterateContext));
2826 topology_context->topology_cb = cb;
2827 topology_context->cls = cls;
2829 for (i = 0; i < pg->total; i++)
2831 if (pg->peers[i].daemon->running == GNUNET_YES)
2833 core_ctx = GNUNET_malloc(sizeof(struct TopologyCoreContext));
2834 core_ctx->daemon = pg->peers[i].daemon;
2835 core_ctx->topology_context = topology_context;
2836 GNUNET_SCHEDULER_add_now(pg->sched, &schedule_get_topology, core_ctx);
2840 topology_context->total = total_count;
2845 * There are many ways to connect peers that are supported by this function.
2846 * To connect peers in the same topology that was created via the
2847 * GNUNET_TESTING_create_topology, the topology variable must be set to
2848 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
2849 * a new instance of that topology will be generated and attempted to be
2850 * connected. This could result in some connections being impossible,
2851 * because some topologies are non-deterministic.
2853 * @param pg the peer group struct representing the running peers
2854 * @param topology which topology to connect the peers in
2855 * @param options options for connecting the topology
2856 * @param option_modifier modifier for options that take a parameter
2857 * @return the number of connections that will be attempted, GNUNET_SYSERR on error
2860 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
2861 enum GNUNET_TESTING_Topology topology,
2862 enum GNUNET_TESTING_TopologyOption options,
2863 double option_modifier)
2867 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2868 #if VERBOSE_TOPOLOGY
2869 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2870 _("Creating clique CONNECT topology\n"));
2872 create_clique (pg, &add_actual_connections);
2874 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2875 #if VERBOSE_TOPOLOGY
2876 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2877 _("Creating small world (ring) CONNECT topology\n"));
2879 create_small_world_ring (pg, &add_actual_connections);
2881 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2882 #if VERBOSE_TOPOLOGY
2883 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2884 _("Creating small world (2d-torus) CONNECT topology\n"));
2886 create_small_world (pg, &add_actual_connections);
2888 case GNUNET_TESTING_TOPOLOGY_RING:
2889 #if VERBOSE_TOPOLOGY
2890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2891 _("Creating ring CONNECT topology\n"));
2893 create_ring (pg, &add_actual_connections);
2895 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2896 #if VERBOSE_TOPOLOGY
2897 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2898 _("Creating 2d torus CONNECT topology\n"));
2900 create_2d_torus (pg, &add_actual_connections);
2902 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2903 #if VERBOSE_TOPOLOGY
2904 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2905 _("Creating Erdos-Renyi CONNECT topology\n"));
2907 create_erdos_renyi (pg, &add_actual_connections);
2909 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2910 #if VERBOSE_TOPOLOGY
2911 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2912 _("Creating InterNAT CONNECT topology\n"));
2914 create_nated_internet (pg, &add_actual_connections);
2916 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2917 #if VERBOSE_TOPOLOGY
2918 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2919 _("Creating Scale Free CONNECT topology\n"));
2921 create_scale_free (pg, &add_actual_connections);
2923 case GNUNET_TESTING_TOPOLOGY_LINE:
2924 #if VERBOSE_TOPOLOGY
2925 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2926 _("Creating straight line CONNECT topology\n"));
2928 create_line (pg, &add_actual_connections);
2930 case GNUNET_TESTING_TOPOLOGY_NONE:
2931 #if VERBOSE_TOPOLOGY
2932 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2933 _("Creating no CONNECT topology\n"));
2935 copy_allowed_topology(pg);
2938 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
2939 _("Unknown topology specification, can't connect peers!\n"));
2940 return GNUNET_SYSERR;
2945 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM:
2946 #if VERBOSE_TOPOLOGY
2947 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2948 _("Connecting random subset (%'.2f percent) of possible peers\n"), 100 * option_modifier);
2950 choose_random_connections(pg, option_modifier);
2952 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM:
2953 #if VERBOSE_TOPOLOGY
2954 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2955 _("Connecting a minimum of %u peers each (if possible)\n"), (unsigned int)option_modifier);
2957 choose_minimum(pg, (unsigned int)option_modifier);
2959 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS:
2960 #if VERBOSE_TOPOLOGY
2961 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2962 _("Using DFS to connect a minimum of %u peers each (if possible)\n"), (unsigned int)option_modifier);
2964 perform_dfs(pg, (int)option_modifier);
2966 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
2968 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
2974 return connect_topology(pg);
2978 * Callback that is called whenever a hostkey is generated
2979 * for a peer. Call the real callback and decrement the
2980 * starting counter for the peergroup.
2982 * @param cls closure
2983 * @param id identifier for the daemon, NULL on error
2984 * @param d handle for the daemon
2985 * @param emsg error message (NULL on success)
2987 static void internal_hostkey_callback (void *cls,
2988 const struct GNUNET_PeerIdentity *id,
2989 struct GNUNET_TESTING_Daemon *d,
2992 struct InternalStartContext *internal_context = cls;
2993 internal_context->peer->pg->starting--;
2994 internal_context->peer->pg->started++;
2995 if (internal_context->hostkey_callback != NULL)
2996 internal_context->hostkey_callback(internal_context->hostkey_cls, id, d, emsg);
2997 else if (internal_context->peer->pg->started == internal_context->peer->pg->total)
2999 internal_context->peer->pg->started = 0; /* Internal startup may use this counter! */
3000 GNUNET_TESTING_daemons_continue_startup(internal_context->peer->pg);
3005 * Callback that is called whenever a peer has finished starting.
3006 * Call the real callback and decrement the starting counter
3007 * for the peergroup.
3009 * @param cls closure
3010 * @param id identifier for the daemon, NULL on error
3011 * @param d handle for the daemon
3012 * @param emsg error message (NULL on success)
3014 static void internal_startup_callback (void *cls,
3015 const struct GNUNET_PeerIdentity *id,
3016 const struct GNUNET_CONFIGURATION_Handle *cfg,
3017 struct GNUNET_TESTING_Daemon *d,
3020 struct InternalStartContext *internal_context = cls;
3021 internal_context->peer->pg->starting--;
3022 if (internal_context->start_cb != NULL)
3023 internal_context->start_cb(internal_context->start_cb_cls, id, cfg, d, emsg);
3027 internal_continue_startup (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
3029 struct InternalStartContext *internal_context = cls;
3031 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3036 if (internal_context->peer->pg->starting < MAX_CONCURRENT_STARTING)
3038 internal_context->peer->pg->starting++;
3039 GNUNET_TESTING_daemon_continue_startup (internal_context->peer->daemon);
3043 GNUNET_SCHEDULER_add_delayed(internal_context->peer->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &internal_continue_startup, internal_context);
3048 internal_start (void *cls, const struct GNUNET_SCHEDULER_TaskContext * tc)
3050 struct InternalStartContext *internal_context = cls;
3052 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
3057 if (internal_context->peer->pg->starting < MAX_CONCURRENT_HOSTKEYS)
3059 internal_context->peer->pg->starting++;
3060 internal_context->peer->daemon = GNUNET_TESTING_daemon_start (internal_context->peer->pg->sched,
3061 internal_context->peer->cfg,
3062 internal_context->timeout,
3063 internal_context->hostname,
3064 &internal_hostkey_callback,
3066 &internal_startup_callback,
3071 GNUNET_SCHEDULER_add_delayed(internal_context->peer->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_MILLISECONDS, 100), &internal_start, internal_context);
3076 * Function which continues a peer group starting up
3077 * after successfully generating hostkeys for each peer.
3079 * @param pg the peer group to continue starting
3083 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
3088 for (i = 0; i < pg->total; i++)
3090 GNUNET_SCHEDULER_add_now (pg->sched, &internal_continue_startup, &pg->peers[i].internal_context);
3091 //GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
3096 * Start count gnunet instances with the same set of transports and
3097 * applications. The port numbers (any option called "PORT") will be
3098 * adjusted to ensure that no two peers running on the same system
3099 * have the same port(s) in their respective configurations.
3101 * @param sched scheduler to use
3102 * @param cfg configuration template to use
3103 * @param total number of daemons to start
3104 * @param timeout total time allowed for peers to start
3105 * @param hostkey_callback function to call on each peers hostkey generation
3106 * if NULL, peers will be started by this call, if non-null,
3107 * GNUNET_TESTING_daemons_continue_startup must be called after
3108 * successful hostkey generation
3109 * @param hostkey_cls closure for hostkey callback
3110 * @param cb function to call on each daemon that was started
3111 * @param cb_cls closure for cb
3112 * @param connect_callback function to call each time two hosts are connected
3113 * @param connect_callback_cls closure for connect_callback
3114 * @param hostnames linked list of hosts to use to start peers on (NULL to run on localhost only)
3116 * @return NULL on error, otherwise handle to control peer group
3118 struct GNUNET_TESTING_PeerGroup *
3119 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
3120 const struct GNUNET_CONFIGURATION_Handle *cfg,
3122 struct GNUNET_TIME_Relative timeout,
3123 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
3125 GNUNET_TESTING_NotifyDaemonRunning cb,
3127 GNUNET_TESTING_NotifyConnection
3128 connect_callback, void *connect_callback_cls,
3129 const struct GNUNET_TESTING_Host *hostnames)
3131 struct GNUNET_TESTING_PeerGroup *pg;
3132 const struct GNUNET_TESTING_Host *hostpos;
3138 const char *hostname;
3139 char *baseservicehome;
3140 char *newservicehome;
3142 struct GNUNET_CONFIGURATION_Handle *pcfg;
3144 unsigned int hostcnt;
3154 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
3157 pg->notify_connection = connect_callback;
3158 pg->notify_connection_cls = connect_callback_cls;
3160 pg->max_timeout = GNUNET_TIME_relative_to_absolute(timeout);
3161 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
3162 if (NULL != hostnames)
3165 hostpos = hostnames;
3166 while (hostpos != NULL)
3168 hostpos = hostpos->next;
3171 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
3174 hostpos = hostnames;
3175 while (hostpos != NULL)
3177 pg->hosts[off].minport = LOW_PORT;
3178 pg->hosts[off++].hostname = GNUNET_strdup(hostpos->hostname);
3179 hostpos = hostpos->next;
3184 GNUNET_free (pg->hosts);
3192 /* skip leading spaces */
3193 while ((0 != *hostnames) && (isspace ( (unsigned char) *hostnames)))
3196 while ('\0' != *rpos)
3198 if (isspace ( (unsigned char) *rpos))
3202 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
3204 start = GNUNET_strdup (hostnames);
3206 while ('\0' != *pos)
3208 if (isspace ( (unsigned char) *pos))
3211 if (strlen (start) > 0)
3213 pg->hosts[off].minport = LOW_PORT;
3214 pg->hosts[off++].hostname = start;
3220 if (strlen (start) > 0)
3222 pg->hosts[off].minport = LOW_PORT;
3223 pg->hosts[off++].hostname = start;
3227 GNUNET_free (start);
3228 GNUNET_free (pg->hosts);
3232 minport = 0; /* make gcc happy */
3240 for (off = 0; off < total; off++)
3244 hostname = pg->hosts[off % hostcnt].hostname;
3245 pcfg = make_config (cfg,
3246 &pg->hosts[off % hostcnt].minport,
3253 pcfg = make_config (cfg,
3261 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3263 ("Could not create configuration for peer number %u on `%s'!\n"),
3264 off, hostname == NULL ? "localhost" : hostname);
3269 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
3272 GNUNET_asprintf (&newservicehome,
3273 "%s/%d/", baseservicehome, off);
3274 GNUNET_free (baseservicehome);
3278 tmpdir = getenv ("TMPDIR");
3279 tmpdir = tmpdir ? tmpdir : "/tmp";
3280 GNUNET_asprintf (&newservicehome,
3283 "gnunet-testing-test-test", off);
3285 GNUNET_CONFIGURATION_set_value_string (pcfg,
3287 "SERVICEHOME", newservicehome);
3288 GNUNET_free (newservicehome);
3289 pg->peers[off].cfg = pcfg;
3290 pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total);
3291 pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total);
3292 pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total);
3293 pg->peers[off].pg = pg;
3295 pg->peers[off].internal_context.peer = &pg->peers[off];
3296 pg->peers[off].internal_context.timeout = timeout;
3297 pg->peers[off].internal_context.hostname = hostname;
3298 pg->peers[off].internal_context.hostkey_callback = hostkey_callback;
3299 pg->peers[off].internal_context.hostkey_cls = hostkey_cls;
3300 pg->peers[off].internal_context.start_cb = cb;
3301 pg->peers[off].internal_context.start_cb_cls = cb_cls;
3303 GNUNET_SCHEDULER_add_now (sched, &internal_start, &pg->peers[off].internal_context);
3310 * Get a daemon by number, so callers don't have to do nasty
3311 * offsetting operation.
3313 struct GNUNET_TESTING_Daemon *
3314 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
3316 if (position < pg->total)
3317 return pg->peers[position].daemon;
3323 * Prototype of a function that will be called when a
3324 * particular operation was completed the testing library.
3326 * @param cls closure (a struct RestartContext)
3327 * @param id id of the peer that was restarted
3328 * @param cfg handle to the configuration of the peer
3329 * @param d handle to the daemon that was restarted
3330 * @param emsg NULL on success
3332 void restart_callback (void *cls,
3333 const struct GNUNET_PeerIdentity *id,
3334 const struct GNUNET_CONFIGURATION_Handle *cfg,
3335 struct GNUNET_TESTING_Daemon *d,
3338 struct RestartContext *restart_context = cls;
3342 restart_context->peers_restarted++;
3346 restart_context->peers_restart_failed++;
3349 if (restart_context->peers_restarted == restart_context->peer_group->total)
3351 restart_context->callback(restart_context->callback_cls, NULL);
3352 GNUNET_free(restart_context);
3354 else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total)
3356 restart_context->callback(restart_context->callback_cls, "Failed to restart peers!");
3357 GNUNET_free(restart_context);
3363 * Callback for informing us about a successful
3364 * or unsuccessful churn stop call.
3366 * @param cls a ChurnContext
3367 * @param emsg NULL on success, non-NULL on failure
3371 churn_stop_callback (void *cls, const char *emsg)
3373 struct ChurnContext *churn_ctx = cls;
3374 unsigned int total_left;
3375 char *error_message;
3377 error_message = NULL;
3380 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
3381 "Churn stop callback failed with error `%s'\n", emsg);
3382 churn_ctx->num_failed_stop++;
3386 churn_ctx->num_to_stop--;
3390 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
3391 "Stopped peer, %d left.\n",
3392 churn_ctx->num_to_stop);
3394 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
3396 if (total_left == 0)
3398 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
3400 GNUNET_asprintf(&error_message,
3401 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
3402 churn_ctx->num_failed_start,
3403 churn_ctx->num_failed_stop);
3405 churn_ctx->cb(churn_ctx->cb_cls, error_message);
3406 GNUNET_free_non_null(error_message);
3407 GNUNET_free(churn_ctx);
3412 * Callback for informing us about a successful
3413 * or unsuccessful churn start call.
3415 * @param cls a ChurnContext
3416 * @param id the peer identity of the started peer
3417 * @param cfg the handle to the configuration of the peer
3418 * @param d handle to the daemon for the peer
3419 * @param emsg NULL on success, non-NULL on failure
3423 churn_start_callback (void *cls,
3424 const struct GNUNET_PeerIdentity *id,
3425 const struct GNUNET_CONFIGURATION_Handle *cfg,
3426 struct GNUNET_TESTING_Daemon *d,
3429 struct ChurnContext *churn_ctx = cls;
3430 unsigned int total_left;
3431 char *error_message;
3433 error_message = NULL;
3436 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
3437 "Churn stop callback failed with error `%s'\n",
3439 churn_ctx->num_failed_start++;
3443 churn_ctx->num_to_start--;
3447 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
3448 "Started peer, %d left.\n",
3449 churn_ctx->num_to_start);
3452 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
3454 if (total_left == 0)
3456 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
3457 GNUNET_asprintf(&error_message,
3458 "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!",
3459 churn_ctx->num_failed_start,
3460 churn_ctx->num_failed_stop);
3461 churn_ctx->cb(churn_ctx->cb_cls, error_message);
3462 GNUNET_free_non_null(error_message);
3463 GNUNET_free(churn_ctx);
3469 * Simulate churn by stopping some peers (and possibly
3470 * re-starting others if churn is called multiple times). This
3471 * function can only be used to create leave-join churn (peers "never"
3472 * leave for good). First "voff" random peers that are currently
3473 * online will be taken offline; then "von" random peers that are then
3474 * offline will be put back online. No notifications will be
3475 * generated for any of these operations except for the callback upon
3478 * @param pg handle for the peer group
3479 * @param voff number of peers that should go offline
3480 * @param von number of peers that should come back online;
3481 * must be zero on first call (since "testbed_start"
3482 * always starts all of the peers)
3483 * @param timeout how long to wait for operations to finish before
3485 * @param cb function to call at the end
3486 * @param cb_cls closure for cb
3489 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
3492 struct GNUNET_TIME_Relative timeout,
3493 GNUNET_TESTING_NotifyCompletion cb,
3496 struct ChurnContext *churn_ctx;
3497 unsigned int running;
3498 unsigned int stopped;
3499 unsigned int total_running;
3500 unsigned int total_stopped;
3502 unsigned int *running_arr;
3503 unsigned int *stopped_arr;
3504 unsigned int *running_permute;
3505 unsigned int *stopped_permute;
3510 if ((von == 0) && (voff == 0)) /* No peers at all? */
3516 for (i = 0; i < pg->total; i++)
3518 if (pg->peers[i].daemon->running == GNUNET_YES)
3520 GNUNET_assert(running != -1);
3525 GNUNET_assert(stopped != -1);
3532 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to stop more peers than are currently running!\n");
3533 cb(cb_cls, "Trying to stop more peers than are currently running!");
3539 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to start more peers than are currently stopped!\n");
3540 cb(cb_cls, "Trying to start more peers than are currently stopped!");
3544 churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3548 running_arr = GNUNET_malloc(running * sizeof(unsigned int));
3552 stopped_arr = GNUNET_malloc(stopped * sizeof(unsigned int));
3554 running_permute = NULL;
3555 stopped_permute = NULL;
3558 running_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, running);
3560 stopped_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, stopped);
3562 total_running = running;
3563 total_stopped = stopped;
3567 churn_ctx->num_to_start = von;
3568 churn_ctx->num_to_stop = voff;
3570 churn_ctx->cb_cls = cb_cls;
3572 for (i = 0; i < pg->total; i++)
3574 if (pg->peers[i].daemon->running == GNUNET_YES)
3576 GNUNET_assert((running_arr != NULL) && (total_running > running));
3577 running_arr[running] = i;
3582 GNUNET_assert((stopped_arr != NULL) && (total_stopped > stopped));
3583 stopped_arr[stopped] = i;
3588 for (i = 0; i < voff; i++)
3591 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Stopping peer %d!\n", running_permute[i]);
3593 GNUNET_TESTING_daemon_stop (pg->peers[running_arr[running_permute[i]]].daemon,
3595 &churn_stop_callback, churn_ctx,
3596 GNUNET_NO, GNUNET_YES);
3599 for (i = 0; i < von; i++)
3602 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Starting up peer %d!\n", stopped_permute[i]);
3604 GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon,
3605 timeout, &churn_start_callback, churn_ctx);
3608 GNUNET_free_non_null(running_arr);
3609 GNUNET_free_non_null(stopped_arr);
3610 GNUNET_free_non_null(running_permute);
3611 GNUNET_free_non_null(stopped_permute);
3616 * Restart all peers in the given group.
3618 * @param pg the handle to the peer group
3619 * @param callback function to call on completion (or failure)
3620 * @param callback_cls closure for the callback function
3623 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls)
3625 struct RestartContext *restart_context;
3630 restart_context = GNUNET_malloc(sizeof(struct RestartContext));
3631 restart_context->peer_group = pg;
3632 restart_context->peers_restarted = 0;
3633 restart_context->callback = callback;
3634 restart_context->callback_cls = callback_cls;
3636 for (off = 0; off < pg->total; off++)
3638 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context);
3644 * Start or stop an individual peer from the given group.
3646 * @param pg handle to the peer group
3647 * @param offset which peer to start or stop
3648 * @param desired_status GNUNET_YES to have it running, GNUNET_NO to stop it
3649 * @param timeout how long to wait for shutdown
3650 * @param cb function to call at the end
3651 * @param cb_cls closure for cb
3654 GNUNET_TESTING_daemons_vary (struct GNUNET_TESTING_PeerGroup *pg,
3655 unsigned int offset,
3657 struct GNUNET_TIME_Relative timeout,
3658 GNUNET_TESTING_NotifyCompletion cb,
3661 struct ChurnContext *churn_ctx;
3663 if (GNUNET_NO == desired_status)
3665 if (NULL != pg->peers[offset].daemon)
3667 churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3668 churn_ctx->num_to_start = 0;
3669 churn_ctx->num_to_stop = 1;
3671 churn_ctx->cb_cls = cb_cls;
3672 GNUNET_TESTING_daemon_stop(pg->peers[offset].daemon,
3673 timeout, &churn_stop_callback, churn_ctx,
3674 GNUNET_NO, GNUNET_YES);
3677 else if (GNUNET_YES == desired_status)
3679 if (NULL == pg->peers[offset].daemon)
3681 churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
3682 churn_ctx->num_to_start = 1;
3683 churn_ctx->num_to_stop = 0;
3685 churn_ctx->cb_cls = cb_cls;
3686 GNUNET_TESTING_daemon_start_stopped(pg->peers[offset].daemon,
3687 timeout, &churn_start_callback, churn_ctx);
3696 * Callback for shutting down peers in a peer group.
3698 * @param cls closure (struct ShutdownContext)
3699 * @param emsg NULL on success
3701 void internal_shutdown_callback (void *cls,
3704 struct ShutdownContext *shutdown_ctx = cls;
3708 shutdown_ctx->peers_down++;
3712 shutdown_ctx->peers_failed++;
3715 if ((shutdown_ctx->cb != NULL) && (shutdown_ctx->peers_down + shutdown_ctx->peers_failed == shutdown_ctx->total_peers))
3717 if (shutdown_ctx->peers_failed > 0)
3718 shutdown_ctx->cb(shutdown_ctx->cb_cls, "Not all peers successfully shut down!");
3720 shutdown_ctx->cb(shutdown_ctx->cb_cls, NULL);
3721 GNUNET_free(shutdown_ctx);
3726 * Shutdown all peers started in the given group.
3728 * @param pg handle to the peer group
3729 * @param timeout how long to wait for shutdown
3730 * @param cb callback to notify upon success or failure
3731 * @param cb_cls closure for cb
3734 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg,
3735 struct GNUNET_TIME_Relative timeout,
3736 GNUNET_TESTING_NotifyCompletion cb,
3740 struct ShutdownContext *shutdown_ctx;
3741 GNUNET_TESTING_NotifyCompletion shutdown_cb;
3742 void *shutdown_cb_cls;
3744 GNUNET_assert(pg->total > 0);
3747 shutdown_ctx = NULL;
3751 shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
3752 shutdown_ctx->cb = cb;
3753 shutdown_ctx->cb_cls = cb_cls;
3754 shutdown_ctx->total_peers = pg->total;
3755 shutdown_cb = &internal_shutdown_callback;
3756 shutdown_cb_cls = cb_cls;
3759 for (off = 0; off < pg->total; off++)
3761 GNUNET_assert(NULL != pg->peers[off].daemon);
3762 GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, timeout, shutdown_cb, shutdown_ctx, GNUNET_YES, GNUNET_NO);
3763 if (NULL != pg->peers[off].cfg)
3764 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
3765 if (pg->peers[off].allowed_peers != NULL)
3766 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers);
3767 if (pg->peers[off].connect_peers != NULL)
3768 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers);
3769 if (pg->peers[off].blacklisted_peers != NULL)
3770 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers);
3772 GNUNET_free (pg->peers);
3773 if (NULL != pg->hosts)
3775 GNUNET_free (pg->hosts[0].hostname);
3776 GNUNET_free (pg->hosts);
3782 /* end of testing_group.c */