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 2, 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 Christian Grothoff
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
30 #define VERBOSE_TESTING GNUNET_NO
33 * Lowest port used for GNUnet testing. Should be high enough to not
34 * conflict with other applications running on the hosts but be low
35 * enough to not conflict with client-ports (typically starting around
38 #define LOW_PORT 10000
41 * Highest port used for GNUnet testing. Should be low enough to not
42 * conflict with the port range for "local" ports (client apps; see
43 * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
45 #define HIGH_PORT 32000
47 #define MAX_OUTSTANDING_CONNECTIONS 50
49 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 160)
51 #define CONNECT_ATTEMPTS 8
54 * Prototype of a function called whenever two peers would be connected
55 * in a certain topology.
57 typedef int (*GNUNET_TESTING_ConnectionProcessor)
58 (struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second);
61 * Context for handling churning a peer group
66 * Callback used to notify of churning finished
68 GNUNET_TESTING_NotifyCompletion cb;
71 * Closure for callback
76 * Number of peers that still need to be started
78 unsigned int num_to_start;
81 * Number of peers that still need to be stopped
83 unsigned int num_to_stop;
86 * Number of peers that failed to start
88 unsigned int num_failed_start;
91 * Number of peers that failed to stop
93 unsigned int num_failed_stop;
99 * The group of peers being restarted
101 struct GNUNET_TESTING_PeerGroup *peer_group;
104 * How many peers have been restarted thus far
106 unsigned int peers_restarted;
109 * How many peers got an error when restarting
111 unsigned int peers_restart_failed;
114 * The function to call once all peers have been restarted
116 GNUNET_TESTING_NotifyCompletion callback;
119 * Closure for callback function
125 struct CreateTopologyContext
129 * Function to call with number of connections
131 GNUNET_TESTING_NotifyConnections cont;
134 * Closure for connection notification
140 struct PeerConnection
145 struct PeerConnection *next;
148 * Pointer to daemon handle
150 struct GNUNET_TESTING_Daemon *daemon;
156 * Data we keep per peer.
161 * (Initial) configuration of the host.
162 * (initial because clients could change
163 * it and we would not know about those
166 struct GNUNET_CONFIGURATION_Handle *cfg;
169 * Handle for controlling the daemon.
171 struct GNUNET_TESTING_Daemon *daemon;
174 * The peergroup this peer belongs to.
176 struct GNUNET_TESTING_PeerGroup *pg;
179 * Linked list of peer connections (pointers)
181 //struct PeerConnection *connected_peers;
183 * Hash map of allowed peer connections (F2F created topology)
185 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
188 * Hash map of blacklisted peers
190 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
193 * Hash map of peer connections
195 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
198 * Temporary hash map of peer connections
200 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
203 * Temporary variable for topology creation, should be reset before
204 * creating any topology so the count is valid once finished.
211 * Data we keep per host.
221 * Lowest port that we have not yet used
229 * Handle to a group of GNUnet peers.
231 struct GNUNET_TESTING_PeerGroup
236 struct GNUNET_SCHEDULER_Handle *sched;
239 * Configuration template.
241 const struct GNUNET_CONFIGURATION_Handle *cfg;
244 * Function to call on each started daemon.
246 GNUNET_TESTING_NotifyDaemonRunning cb;
254 * Function to call on each topology connection created
256 GNUNET_TESTING_NotifyConnection notify_connection;
259 * Callback for notify_connection
261 void *notify_connection_cls;
264 * NULL-terminated array of information about
267 struct HostData *hosts;
270 * Array of "total" peers.
272 struct PeerData *peers;
275 * Number of peers in this group.
280 * At what time should we fail the peer startup process?
282 struct GNUNET_TIME_Absolute max_timeout;
286 * Convert unique ID to hash code.
288 * @param uid unique ID to convert
289 * @param hash set to uid (extended with zeros)
292 hash_from_uid (uint32_t uid,
293 GNUNET_HashCode *hash)
295 memset (hash, 0, sizeof(GNUNET_HashCode));
296 *((uint32_t*)hash) = uid;
300 * Convert hash code to unique ID.
302 * @param uid unique ID to convert
303 * @param hash set to uid (extended with zeros)
306 uid_from_hash (const GNUNET_HashCode *hash, uint32_t *uid)
308 memcpy (uid, hash, sizeof(uint32_t));
313 struct GNUNET_CONFIGURATION_Handle *ret;
315 const char *hostname;
319 struct ConnectContext
321 struct GNUNET_TESTING_Daemon *first;
323 struct GNUNET_TESTING_Daemon *second;
325 struct GNUNET_TESTING_PeerGroup *pg;
329 * Number of connects we are waiting on, allows us to rate limit
332 static int outstanding_connects;
336 * Function to iterate over options. Copies
337 * the options to the target configuration,
338 * updating PORT values as needed.
341 * @param section name of the section
342 * @param option name of the option
343 * @param value value of the option
346 update_config (void *cls,
347 const char *section, const char *option, const char *value)
349 struct UpdateContext *ctx = cls;
353 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
355 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
359 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
361 value = ctx->hostname;
364 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
369 * Create a new configuration using the given configuration
370 * as a template; however, each PORT in the existing cfg
371 * must be renumbered by incrementing "*port". If we run
372 * out of "*port" numbers, return NULL.
374 * @param cfg template configuration
375 * @param port port numbers to use, update to reflect
376 * port numbers that were used
377 * @param hostname hostname of the controlling host, to allow control connections from
379 * @return new configuration, NULL on error
381 static struct GNUNET_CONFIGURATION_Handle *
382 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, const char *hostname)
384 struct UpdateContext uc;
391 uc.ret = GNUNET_CONFIGURATION_create ();
392 uc.hostname = hostname;
394 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
395 if (uc.nport >= HIGH_PORT)
398 GNUNET_CONFIGURATION_destroy (uc.ret);
402 if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
404 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
405 GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
406 GNUNET_free_non_null(control_host);
407 GNUNET_free(allowed_hosts);
411 /* arm needs to know to allow connections from the host on which it is running,
412 * otherwise gnunet-arm is unable to connect to it in some instances */
413 if (hostname != NULL)
415 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
416 GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
417 GNUNET_free(allowed_hosts);
420 *port = (uint16_t) uc.nport;
426 * Add entries to the peers connect list
428 * @param pg the peer group we are working with
429 * @param first index of the first peer
430 * @param second index of the second peer
432 * @return the number of connections added (can be 0, 1 or 2)
433 * technically should only be 0 or 2, but the small price
434 * of iterating over the lists (hashmaps in the future)
435 * for being sure doesn't bother me!
439 add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
445 GNUNET_HashCode hash_first;
446 GNUNET_HashCode hash_second;
448 hash_from_uid(first, &hash_first);
449 hash_from_uid(second, &hash_second);
451 add_first = GNUNET_NO;
452 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second))
454 add_first = GNUNET_YES;
457 add_second = GNUNET_NO;
458 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first))
460 add_second = GNUNET_YES;
466 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
467 pg->peers[first].num_connections++;
473 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
474 pg->peers[second].num_connections++;
483 * Add entries to the peers allowed connections list
485 * @param pg the peer group we are working with
486 * @param first index of the first peer
487 * @param second index of the second peer
489 * @return the number of connections added (can be 0, 1 or 2)
490 * technically should only be 0 or 2, but the small price
491 * of iterating over the lists (hashmaps in the future)
492 * for being sure doesn't bother me!
496 add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
500 struct PeerConnection *first_iter;
501 struct PeerConnection *second_iter;
502 struct PeerConnection *new_first;
503 struct PeerConnection *new_second;
508 GNUNET_HashCode hash_first;
509 GNUNET_HashCode hash_second;
511 hash_from_uid(first, &hash_first);
512 hash_from_uid(second, &hash_second);
514 add_first = GNUNET_NO;
515 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second))
517 add_first = GNUNET_YES;
520 add_second = GNUNET_NO;
521 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first))
523 add_second = GNUNET_YES;
526 first_iter = pg->peers[first].connected_peers;
527 while (first_iter != NULL)
529 if (first_iter->daemon == pg->peers[second].daemon)
530 add_first = GNUNET_NO;
531 first_iter = first_iter->next;
534 second_iter = pg->peers[second].connected_peers;
535 add_second = GNUNET_YES;
536 while (second_iter != NULL)
538 if (second_iter->daemon == pg->peers[first].daemon)
539 add_second = GNUNET_NO;
540 second_iter = second_iter->next;
547 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
549 new_first = GNUNET_malloc(sizeof(struct PeerConnection));
550 new_first->daemon = pg->peers[second].daemon;
551 new_first->next = pg->peers[first].connected_peers;
552 pg->peers[first].connected_peers = new_first;
554 pg->peers[first].num_connections++;
560 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
562 new_second = GNUNET_malloc(sizeof(struct PeerConnection));
563 new_second->daemon = pg->peers[first].daemon;
564 new_second->next = pg->peers[second].connected_peers;
565 pg->peers[second].connected_peers = new_second;
566 pg->peers[first].num_connections++;
568 pg->peers[second].num_connections++;
576 * Add entries to the peers blacklisted list
578 * @param pg the peer group we are working with
579 * @param first index of the first peer
580 * @param second index of the second peer
582 * @return the number of connections added (can be 0, 1 or 2)
586 blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
591 GNUNET_HashCode hash_first;
592 GNUNET_HashCode hash_second;
594 hash_from_uid(first, &hash_first);
595 hash_from_uid(second, &hash_second);
597 add_first = GNUNET_NO;
598 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second))
600 add_first = GNUNET_YES;
603 add_second = GNUNET_NO;
604 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first))
606 add_second = GNUNET_YES;
612 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
613 pg->peers[first].num_connections++;
619 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
620 pg->peers[second].num_connections++;
628 * Remove entries from the peers blacklisted list
630 * @param pg the peer group we are working with
631 * @param first index of the first peer
632 * @param second index of the second peer
634 * @return the number of connections removed (can be 0, 1 or 2)
638 unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
643 GNUNET_HashCode hash_first;
644 GNUNET_HashCode hash_second;
646 hash_from_uid(first, &hash_first);
647 hash_from_uid(second, &hash_second);
649 remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second);
650 remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first);
655 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon));
661 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon));
669 * Scale free network construction as described in:
671 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
673 * Start with a network of "one" peer, then progressively add
674 * peers up to the total number. At each step, iterate over
675 * all possible peers and connect new peer based on number of
676 * existing connections of the target peer.
678 * @param pg the peer group we are dealing with
679 * @param proc the connection processor to use
681 * @return the number of connections created
684 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
687 unsigned int total_connections;
688 unsigned int outer_count;
690 unsigned int previous_total_connections;
694 GNUNET_assert(pg->total > 1);
696 /* Add a connection between the first two nodes */
697 total_connections = proc(pg, 0, 1);
699 for (outer_count = 1; outer_count < pg->total; outer_count++)
701 previous_total_connections = total_connections;
702 for (i = 0; i < outer_count; i++)
704 probability = pg->peers[i].num_connections / (double)previous_total_connections;
705 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
706 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
708 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709 "Considering connecting peer %d to peer %d\n",
712 if (random < probability)
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
716 "Connecting peer %d to peer %d\n",
719 total_connections += proc(pg, outer_count, i);
724 return total_connections;
728 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
733 unsigned int randomPeer;
734 double random, logNModifier, percentage;
735 unsigned int smallWorldConnections;
741 int connect_attempts;
743 logNModifier = 0.5; /* FIXME: default value? */
744 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
749 if (sscanf(p_string, "%lf", &logNModifier) != 1)
750 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
751 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
755 GNUNET_free (p_string);
757 percentage = 0.5; /* FIXME: default percentage? */
758 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
763 if (sscanf(p_string, "%lf", &percentage) != 1)
764 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
765 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
769 GNUNET_free (p_string);
771 natLog = log (pg->total);
772 connsPerPeer = ceil (natLog * logNModifier);
774 if (connsPerPeer % 2 == 1)
777 smallWorldConnections = 0;
778 connect_attempts = 0;
779 for (i = 0; i < pg->total; i++)
782 max = i + connsPerPeer / 2;
783 min = i - connsPerPeer / 2;
785 if (max > pg->total - 1)
787 max = max - pg->total;
793 min = pg->total - 1 + min;
797 for (j = 0; j < connsPerPeer / 2; j++)
799 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
800 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
801 if (random < percentage)
803 /* Connect to uniformly selected random peer */
805 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
807 while ((((randomPeer < max) && (randomPeer > min))
808 && (useAnd == 0)) || (((randomPeer > min)
809 || (randomPeer < max))
813 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
816 smallWorldConnections +=
817 proc (pg, i, randomPeer);
821 nodeToConnect = i + j + 1;
822 if (nodeToConnect > pg->total - 1)
824 nodeToConnect = nodeToConnect - pg->total;
827 proc (pg, i, nodeToConnect);
833 connect_attempts += smallWorldConnections;
835 return connect_attempts;
840 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
842 unsigned int outer_count, inner_count;
844 int connect_attempts;
845 double nat_percentage;
848 nat_percentage = 0.6; /* FIXME: default percentage? */
849 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
854 if (sscanf(p_string, "%lf", &nat_percentage) != 1)
855 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
856 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
860 GNUNET_free (p_string);
865 cutoff = (unsigned int) (nat_percentage * pg->total);
867 connect_attempts = 0;
869 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
871 for (inner_count = outer_count + 1; inner_count < pg->total;
874 if ((outer_count > cutoff) || (inner_count > cutoff))
877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
878 "Connecting peer %d to peer %d\n",
879 outer_count, inner_count);
881 connect_attempts += proc(pg, outer_count, inner_count);
886 return connect_attempts;
893 create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
895 unsigned int i, j, k;
899 unsigned int toggle = 1;
900 unsigned int nodeToConnect;
902 unsigned int node1Row;
903 unsigned int node1Col;
904 unsigned int node2Row;
905 unsigned int node2Col;
906 unsigned int distance;
907 double probability, random, percentage;
908 unsigned int smallWorldConnections;
910 int connect_attempts;
911 square = floor (sqrt (pg->total));
915 percentage = 0.5; /* FIXME: default percentage? */
916 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
921 if (sscanf(p_string, "%lf", &percentage) != 1)
922 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
923 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
927 GNUNET_free (p_string);
929 probability = 0.5; /* FIXME: default percentage? */
930 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
935 if (sscanf(p_string, "%lf", &probability) != 1)
936 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
937 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
941 GNUNET_free (p_string);
943 if (square * square != pg->total)
945 while (rows * cols < pg->total)
956 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
957 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
961 connect_attempts = 0;
962 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
963 * to the node to its right and above. Once this is over, we'll have our torus!
964 * Special case for the last node (if the rows and columns are not equal), connect
965 * to the first in the row to maintain topology.
967 for (i = 0; i < pg->total; i++)
969 /* First connect to the node to the right */
970 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
971 nodeToConnect = i + 1;
972 else if (i + 1 == pg->total)
973 nodeToConnect = rows * cols - cols;
975 nodeToConnect = i - cols + 1;
977 connect_attempts += proc (pg, i, nodeToConnect);
980 nodeToConnect = (rows * cols) - cols + i;
982 nodeToConnect = i - cols;
984 if (nodeToConnect < pg->total)
985 connect_attempts += proc (pg, i, nodeToConnect);
987 natLog = log (pg->total);
988 #if VERBOSE_TESTING > 2
989 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
990 _("natural log of %d is %d, will run %d iterations\n"),
991 pg->total, natLog, (int) (natLog * percentage));
992 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
994 smallWorldConnections = 0;
995 for (i = 0; i < (int) (natLog * percentage); i++)
997 for (j = 0; j < pg->total; j++)
999 /* Determine the row and column of node at position j on the 2d torus */
1000 node1Row = j / cols;
1001 node1Col = j - (node1Row * cols);
1002 for (k = 0; k < pg->total; k++)
1004 /* Determine the row and column of node at position k on the 2d torus */
1005 node2Row = k / cols;
1006 node2Col = k - (node2Row * cols);
1007 /* Simple Cartesian distance */
1008 distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
1011 /* Calculate probability as 1 over the square of the distance */
1012 probability = 1.0 / (distance * distance);
1013 /* Choose a random value between 0 and 1 */
1014 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1015 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
1016 /* If random < probability, then connect the two nodes */
1017 if (random < probability)
1018 smallWorldConnections += proc (pg, j, k);
1024 connect_attempts += smallWorldConnections;
1025 #if VERBOSE_TESTING > 2
1026 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1027 _("Total connections added for small world: %d!\n"),
1028 smallWorldConnections);
1030 return connect_attempts;
1036 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1039 unsigned int outer_count;
1040 unsigned int inner_count;
1041 int connect_attempts;
1045 probability = 0.5; /* FIXME: default percentage? */
1046 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1051 if (sscanf(p_string, "%lf", &probability) != 1)
1052 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1053 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1057 GNUNET_free (p_string);
1059 connect_attempts = 0;
1060 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1062 for (inner_count = outer_count + 1; inner_count < pg->total;
1065 temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1066 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
1068 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1069 _("rand is %f probability is %f\n"), temp_rand,
1072 if (temp_rand < probability)
1074 connect_attempts += proc (pg, outer_count, inner_count);
1079 return connect_attempts;
1083 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1086 unsigned int square;
1089 unsigned int toggle = 1;
1090 unsigned int nodeToConnect;
1091 int connect_attempts;
1093 connect_attempts = 0;
1095 square = floor (sqrt (pg->total));
1099 if (square * square != pg->total)
1101 while (rows * cols < pg->total)
1103 if (toggle % 2 == 0)
1112 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1113 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1116 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1117 * to the node to its right and above. Once this is over, we'll have our torus!
1118 * Special case for the last node (if the rows and columns are not equal), connect
1119 * to the first in the row to maintain topology.
1121 for (i = 0; i < pg->total; i++)
1123 /* First connect to the node to the right */
1124 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1125 nodeToConnect = i + 1;
1126 else if (i + 1 == pg->total)
1127 nodeToConnect = rows * cols - cols;
1129 nodeToConnect = i - cols + 1;
1131 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1132 "Connecting peer %d to peer %d\n",
1135 connect_attempts += proc(pg, i, nodeToConnect);
1137 /* Second connect to the node immediately above */
1139 nodeToConnect = (rows * cols) - cols + i;
1141 nodeToConnect = i - cols;
1143 if (nodeToConnect < pg->total)
1146 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1147 "Connecting peer %d to peer %d\n",
1150 connect_attempts += proc(pg, i, nodeToConnect);
1155 return connect_attempts;
1161 create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1163 unsigned int outer_count;
1164 unsigned int inner_count;
1165 int connect_attempts;
1167 connect_attempts = 0;
1169 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1171 for (inner_count = outer_count + 1; inner_count < pg->total;
1175 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176 "Connecting peer %d to peer %d\n",
1177 outer_count, inner_count);
1179 connect_attempts += proc(pg, outer_count, inner_count);
1183 return connect_attempts;
1188 create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1191 int connect_attempts;
1193 connect_attempts = 0;
1195 /* Connect each peer to the next highest numbered peer */
1196 for (count = 0; count < pg->total - 1; count++)
1199 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1200 "Connecting peer %d to peer %d\n",
1203 connect_attempts += proc(pg, count, count + 1);
1206 /* Connect the last peer to the first peer */
1207 connect_attempts += proc(pg, pg->total - 1, 0);
1209 return connect_attempts;
1214 * Iterator for writing friends of a peer to a file.
1216 * @param cls closure, an open writable file handle
1217 * @param key the key the daemon was stored under
1218 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1220 * @return GNUNET_YES to continue iteration
1222 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
1223 * with a single file_iterator that takes a closure which contains
1224 * the prefix to write before the peer. Then this could be used
1225 * for blacklisting multiple transports and writing the friend
1226 * file. I'm sure *someone* will complain loudly about other
1227 * things that negate these functions even existing so no point in
1231 friend_file_iterator (void *cls,
1232 const GNUNET_HashCode * key,
1235 FILE *temp_friend_handle = cls;
1236 struct GNUNET_TESTING_Daemon *peer = value;
1237 struct GNUNET_PeerIdentity *temppeer;
1238 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1240 temppeer = &peer->id;
1241 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1242 fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
1247 struct BlacklistContext
1250 * The (open) file handle to write to
1252 FILE *temp_file_handle;
1255 * The transport that this peer will be blacklisted on.
1261 * Iterator for writing blacklist data to appropriate files.
1263 * @param cls closure, an open writable file handle
1264 * @param key the key the daemon was stored under
1265 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1267 * @return GNUNET_YES to continue iteration
1270 blacklist_file_iterator (void *cls,
1271 const GNUNET_HashCode * key,
1274 struct BlacklistContext *blacklist_ctx = cls;
1275 //FILE *temp_blacklist_handle = cls;
1276 struct GNUNET_TESTING_Daemon *peer = value;
1277 struct GNUNET_PeerIdentity *temppeer;
1278 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1280 temppeer = &peer->id;
1281 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1282 fprintf(blacklist_ctx->temp_file_handle, "%s:%s\n", blacklist_ctx->transport, (char *)&peer_enc);
1288 * Create the friend files based on the PeerConnection's
1289 * of each peer in the peer group, and copy the files
1290 * to the appropriate place
1292 * @param pg the peer group we are dealing with
1295 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
1297 FILE *temp_friend_handle;
1298 unsigned int pg_iter;
1299 char *temp_service_path;
1303 enum GNUNET_OS_ProcessStatusType type;
1304 unsigned long return_code;
1309 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1310 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1312 mytemp = GNUNET_DISK_mktemp("friends");
1313 GNUNET_assert(mytemp != NULL);
1314 temp_friend_handle = fopen (mytemp, "wt");
1315 GNUNET_assert(temp_friend_handle != NULL);
1316 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle);
1317 fclose(temp_friend_handle);
1320 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1322 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1323 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1326 if (UNLINK (mytemp) != 0)
1327 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1328 GNUNET_free (mytemp);
1332 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1334 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
1335 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1336 "mv", mytemp, arg, NULL);
1338 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1339 _("Copying file with command cp %s %s\n"), mytemp, arg);
1344 else /* Remote, scp the file to the correct place */
1346 if (NULL != pg->peers[pg_iter].daemon->username)
1347 GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1349 GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1350 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1351 "scp", mytemp, arg, NULL);
1354 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1355 _("Copying file with command scp %s %s\n"), mytemp, arg);
1359 GNUNET_free (temp_service_path);
1360 GNUNET_free (mytemp);
1364 ret = GNUNET_SYSERR;
1365 while ((count < max_wait) && (ret != GNUNET_OK))
1368 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1372 _("Checking copy status of file %d\n"), pg_iter);
1374 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1376 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1378 ret = GNUNET_SYSERR;
1380 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1382 ret = GNUNET_SYSERR;
1386 pidarr[pg_iter] = 0;
1388 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1389 _("File %d copied\n"), pg_iter);
1395 if (ret == GNUNET_SYSERR)
1402 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1403 _("Finished copying all friend files!\n"));
1405 GNUNET_free(pidarr);
1411 * Create the blacklist files based on the PeerConnection's
1412 * of each peer in the peer group, and copy the files
1413 * to the appropriate place.
1415 * @param pg the peer group we are dealing with
1416 * @param transports space delimited list of transports to blacklist
1419 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg, char *transports)
1421 FILE *temp_file_handle;
1422 static struct BlacklistContext blacklist_ctx;
1423 unsigned int pg_iter;
1424 char *temp_service_path;
1428 enum GNUNET_OS_ProcessStatusType type;
1429 unsigned long return_code;
1436 char *temp_transports;
1438 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1439 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1441 mytemp = GNUNET_DISK_mktemp("blacklist");
1442 GNUNET_assert(mytemp != NULL);
1443 temp_file_handle = fopen (mytemp, "wt");
1444 GNUNET_assert(temp_file_handle != NULL);
1445 temp_transports = GNUNET_strdup(transports);
1446 blacklist_ctx.temp_file_handle = temp_file_handle;
1447 transport_len = strlen(temp_transports) + 1;
1450 for (i = 0; i < transport_len; i++)
1452 if ((temp_transports[i] == ' ') && (pos == NULL))
1453 continue; /* At start of string (whitespace) */
1454 else if ((temp_transports[i] == ' ') || (temp_transports[i] == '\0')) /* At end of string */
1456 temp_transports[i] = '\0';
1457 blacklist_ctx.transport = pos;
1458 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, &blacklist_ctx);
1460 } /* At beginning of actual string */
1461 else if (pos == NULL)
1463 pos = &temp_transports[i];
1467 GNUNET_free_non_null(temp_transports);
1468 fclose(temp_file_handle);
1471 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1473 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1474 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1477 if (UNLINK (mytemp) != 0)
1478 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1479 GNUNET_free (mytemp);
1483 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1485 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
1486 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1487 "mv", mytemp, arg, NULL);
1489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1490 _("Copying file with command cp %s %s\n"), mytemp, arg);
1495 else /* Remote, scp the file to the correct place */
1497 if (NULL != pg->peers[pg_iter].daemon->username)
1498 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1500 GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1501 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1502 "scp", mytemp, arg, NULL);
1505 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1506 _("Copying file with command scp %s %s\n"), mytemp, arg);
1510 GNUNET_free (temp_service_path);
1511 GNUNET_free (mytemp);
1515 ret = GNUNET_SYSERR;
1516 while ((count < max_wait) && (ret != GNUNET_OK))
1519 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1522 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1523 _("Checking copy status of file %d\n"), pg_iter);
1525 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1527 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1529 ret = GNUNET_SYSERR;
1531 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1533 ret = GNUNET_SYSERR;
1537 pidarr[pg_iter] = 0;
1539 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1540 _("File %d copied\n"), pg_iter);
1546 if (ret == GNUNET_SYSERR)
1553 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1554 _("Finished copying all blacklist files!\n"));
1556 GNUNET_free(pidarr);
1562 * Internal notification of a connection, kept so that we can ensure some connections
1563 * happen instead of flooding all testing daemons with requests to connect.
1565 static void internal_connect_notify (void *cls,
1566 const struct GNUNET_PeerIdentity *first,
1567 const struct GNUNET_PeerIdentity *second,
1568 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1569 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1570 struct GNUNET_TESTING_Daemon *first_daemon,
1571 struct GNUNET_TESTING_Daemon *second_daemon,
1574 struct GNUNET_TESTING_PeerGroup *pg = cls;
1575 outstanding_connects--;
1577 pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
1581 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1583 struct ConnectContext *connect_context = cls;
1585 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1588 if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1590 #if VERBOSE_TESTING > 2
1591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1592 _("Delaying connect, we have too many outstanding connections!\n"));
1594 GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1598 #if VERBOSE_TESTING > 2
1599 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1600 _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1602 outstanding_connects++;
1603 GNUNET_TESTING_daemons_connect (connect_context->first,
1604 connect_context->second,
1607 &internal_connect_notify,
1608 connect_context->pg);
1609 GNUNET_free(connect_context);
1614 * Iterator for actually scheduling connections to be created
1615 * between two peers.
1617 * @param cls closure, a GNUNET_TESTING_Daemon
1618 * @param key the key the second Daemon was stored under
1619 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1621 * @return GNUNET_YES to continue iteration
1624 connect_iterator (void *cls,
1625 const GNUNET_HashCode * key,
1628 struct PeerData *first = cls;
1629 struct GNUNET_TESTING_Daemon *second = value;
1630 struct ConnectContext *connect_context;
1632 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1633 connect_context->pg = first->pg;
1634 connect_context->first = first->daemon;
1635 connect_context->second = second;
1636 GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
1642 * Iterator for copying all entries in the allowed hashmap to the
1645 * @param cls closure, a GNUNET_TESTING_Daemon
1646 * @param key the key the second Daemon was stored under
1647 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1649 * @return GNUNET_YES to continue iteration
1652 copy_topology_iterator (void *cls,
1653 const GNUNET_HashCode * key,
1656 struct PeerData *first = cls;
1658 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1664 * Make the peers to connect the same as those that are allowed to be
1667 * @param pg the peer group
1670 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
1672 unsigned int pg_iter;
1677 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1679 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, ©_topology_iterator, &pg->peers[pg_iter]);
1680 if (GNUNET_SYSERR == ret)
1681 return GNUNET_SYSERR;
1683 total = total + ret;
1691 * Connect the topology as specified by the PeerConnection's
1692 * of each peer in the peer group
1694 * @param pg the peer group we are dealing with
1696 * @return the number of connections that will be attempted
1699 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1701 unsigned int pg_iter;
1705 struct PeerConnection *connection_iter;
1706 struct ConnectContext *connect_context;
1710 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1712 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
1713 if (GNUNET_SYSERR == ret)
1714 return GNUNET_SYSERR;
1716 total = total + ret;
1720 while (connection_iter != NULL)
1722 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1723 connect_context->pg = pg;
1724 connect_context->first = ;
1725 connect_context->second = connection_iter->daemon;
1726 GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1727 connection_iter = connection_iter->next;
1736 * Takes a peer group and creates a topology based on the
1737 * one specified. Creates a topology means generates friend
1738 * files for the peers so they can only connect to those allowed
1739 * by the topology. This will only have an effect once peers
1740 * are started if the FRIENDS_ONLY option is set in the base
1741 * config. Also takes an optional restrict topology which
1742 * disallows direct TCP connections UNLESS they are specified in
1743 * the restricted topology.
1745 * @param pg the peer group struct representing the running peers
1746 * @param topology which topology to connect the peers in
1747 * @param restrict_topology allow only direct TCP connections in this topology
1748 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
1749 * @param restrict_transports space delimited list of transports to blacklist
1750 * to create restricted topology
1752 * @return the maximum number of connections were all allowed peers
1753 * connected to each other
1756 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
1757 enum GNUNET_TESTING_Topology topology,
1758 enum GNUNET_TESTING_Topology restrict_topology,
1759 char *restrict_transports)
1762 int num_connections;
1763 int unblacklisted_connections;
1765 GNUNET_assert (pg->notify_connection != NULL);
1770 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1772 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1773 _("Creating clique topology\n"));
1775 num_connections = create_clique (pg, &add_allowed_connections);
1777 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1780 _("Creating small world (ring) topology\n"));
1782 num_connections = create_small_world_ring (pg, &add_allowed_connections);
1784 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 _("Creating small world (2d-torus) topology\n"));
1789 num_connections = create_small_world (pg, &add_allowed_connections);
1791 case GNUNET_TESTING_TOPOLOGY_RING:
1793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1794 _("Creating ring topology\n"));
1796 num_connections = create_ring (pg, &add_allowed_connections);
1798 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1801 _("Creating 2d torus topology\n"));
1803 num_connections = create_2d_torus (pg, &add_allowed_connections);
1805 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1808 _("Creating Erdos-Renyi topology\n"));
1810 num_connections = create_erdos_renyi (pg, &add_allowed_connections);
1812 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1815 _("Creating InterNAT topology\n"));
1817 num_connections = create_nated_internet (pg, &add_allowed_connections);
1819 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1822 _("Creating Scale Free topology\n"));
1824 num_connections = create_scale_free (pg, &add_allowed_connections);
1826 case GNUNET_TESTING_TOPOLOGY_NONE:
1827 num_connections = 0;
1830 num_connections = 0;
1833 if (num_connections < 1)
1834 return GNUNET_SYSERR;
1836 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1838 ret = create_and_copy_friend_files(pg);
1841 if (ret != GNUNET_OK)
1844 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1845 _("Failed during friend file copying!\n"));
1847 return GNUNET_SYSERR;
1852 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1853 _("Friend files created/copied successfully!\n"));
1858 * Use the create clique method to initially set all connections
1861 create_clique (pg, &blacklist_connections);
1862 unblacklisted_connections = 0;
1864 * Un-blacklist connections as per the topology specified
1866 switch (restrict_topology)
1868 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1870 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1871 _("Blacklisting all but clique topology\n"));
1873 unblacklisted_connections = create_clique (pg, &unblacklist_connections);
1875 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1877 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1878 _("Blacklisting all but small world (ring) topology\n"));
1880 unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections);
1882 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1884 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1885 _("Blacklisting all but small world (2d-torus) topology\n"));
1887 unblacklisted_connections = create_small_world (pg, &unblacklist_connections);
1889 case GNUNET_TESTING_TOPOLOGY_RING:
1891 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1892 _("Blacklisting all but ring topology\n"));
1894 unblacklisted_connections = create_ring (pg, &unblacklist_connections);
1896 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1898 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1899 _("Blacklisting all but 2d torus topology\n"));
1901 unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections);
1903 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1905 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1906 _("Blacklisting all but Erdos-Renyi topology\n"));
1908 unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections);
1910 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1912 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1913 _("Blacklisting all but InterNAT topology\n"));
1915 unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections);
1917 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1919 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1920 _("Blacklisting all but Scale Free topology\n"));
1922 unblacklisted_connections = create_scale_free (pg, &unblacklist_connections);
1924 case GNUNET_TESTING_TOPOLOGY_NONE:
1930 if ((unblacklisted_connections > 0) && (restrict_transports != NULL))
1932 ret = create_and_copy_blacklist_files(pg, restrict_transports);
1933 if (ret != GNUNET_OK)
1936 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1937 _("Failed during blacklist file copying!\n"));
1939 return GNUNET_SYSERR;
1944 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1945 _("Blacklist files created/copied successfully!\n"));
1951 return num_connections;
1954 struct RandomContext
1959 struct GNUNET_TESTING_PeerGroup *pg;
1962 * uid of the first peer
1967 * Peer data for first peer.
1969 struct PeerData *first;
1972 * Random percentage to use
1977 struct MinimumContext
1982 struct GNUNET_TESTING_PeerGroup *pg;
1985 * uid of the first peer
1990 * Peer data for first peer.
1992 struct PeerData *first;
1995 * Number of conns per peer
1997 unsigned int num_to_add;
2000 * Permuted array of all possible connections. Only add the Nth
2001 * peer if it's in the Nth position.
2003 unsigned int *pg_array;
2006 * What number is the current element we are iterating over?
2008 unsigned int current;
2016 struct GNUNET_TESTING_PeerGroup *pg;
2019 * uid of the first peer
2024 * uid of the second peer
2026 uint32_t second_uid;
2029 * Peer data for first peer.
2031 struct PeerData *first;
2034 * Which peer has been chosen as the one to add?
2036 unsigned int chosen;
2039 * What number is the current element we are iterating over?
2041 unsigned int current;
2045 * Iterator for choosing random peers to connect.
2047 * @param cls closure, a RandomContext
2048 * @param key the key the second Daemon was stored under
2049 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2051 * @return GNUNET_YES to continue iteration
2054 random_connect_iterator (void *cls,
2055 const GNUNET_HashCode * key,
2058 struct RandomContext *random_ctx = cls;
2059 double random_number;
2060 uint32_t second_pos;
2061 GNUNET_HashCode first_hash;
2062 random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
2063 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
2064 if (random_number < random_ctx->percentage)
2066 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2068 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
2069 uid_from_hash(key, &second_pos);
2070 hash_from_uid(random_ctx->first_uid, &first_hash);
2071 GNUNET_assert(random_ctx->pg->total > second_pos);
2072 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon));
2078 * Iterator for adding at least X peers to a peers connection set.
2080 * @param cls closure, MinimumContext
2081 * @param key the key the second Daemon was stored under
2082 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2084 * @return GNUNET_YES to continue iteration
2087 minimum_connect_iterator (void *cls,
2088 const GNUNET_HashCode * key,
2091 struct MinimumContext *min_ctx = cls;
2092 uint32_t second_pos;
2093 GNUNET_HashCode first_hash;
2096 if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
2098 for (i = 0; i < min_ctx->num_to_add; i++)
2100 if (min_ctx->pg_array[i] == min_ctx->current)
2102 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2103 uid_from_hash(key, &second_pos);
2104 hash_from_uid(min_ctx->first_uid, &first_hash);
2105 GNUNET_assert(min_ctx->pg->total > second_pos);
2106 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));
2107 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
2108 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon));
2115 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
2121 * Iterator for adding peers to a connection set based on a depth first search.
2123 * @param cls closure, MinimumContext
2124 * @param key the key the second daemon was stored under
2125 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
2127 * @return GNUNET_YES to continue iteration
2130 dfs_connect_iterator (void *cls,
2131 const GNUNET_HashCode * key,
2134 struct DFSContext *dfs_ctx = cls;
2135 GNUNET_HashCode first_hash;
2137 if (dfs_ctx->current == dfs_ctx->chosen)
2139 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(dfs_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2140 uid_from_hash(key, &dfs_ctx->second_uid);
2141 hash_from_uid(dfs_ctx->first_uid, &first_hash);
2142 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));
2143 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(dfs_ctx->pg->peers[dfs_ctx->second_uid].connect_peers, &first_hash, dfs_ctx->first->daemon));
2144 /* Can't remove second from first yet because we are currently iterating, hence the return value in the DFSContext! */
2145 return GNUNET_NO; /* We have found our peer, don't iterate more */
2154 * From the set of connections possible, choose percentage percent of connections
2155 * to actually connect.
2157 * @param pg the peergroup we are dealing with
2158 * @param percentage what percent of total connections to make
2161 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage)
2163 struct RandomContext random_ctx;
2166 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2168 random_ctx.first_uid = pg_iter;
2169 random_ctx.first = &pg->peers[pg_iter];
2170 random_ctx.percentage = percentage;
2172 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2173 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx);
2174 /* Now remove the old connections */
2175 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2176 /* And replace with the random set */
2177 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2182 * From the set of connections possible, choose at least num connections per
2185 * @param pg the peergroup we are dealing with
2186 * @param num how many connections at least should each peer have (if possible)?
2189 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2191 struct MinimumContext minimum_ctx;
2194 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2196 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2199 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2201 minimum_ctx.first_uid = pg_iter;
2202 minimum_ctx.pg_array = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2203 minimum_ctx.first = &pg->peers[pg_iter];
2204 minimum_ctx.pg = pg;
2205 minimum_ctx.num_to_add = num;
2206 minimum_ctx.current = 0;
2207 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2208 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &minimum_connect_iterator, &minimum_ctx);
2211 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2213 /* Remove the "old" connections */
2214 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2215 /* And replace with the working set */
2216 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2217 fprintf(stderr, "Finished! Hashmap size %u\n", GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2223 static unsigned int count_workingset_connections(struct GNUNET_TESTING_PeerGroup *pg)
2226 unsigned int pg_iter;
2230 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2232 count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2239 static unsigned int count_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg)
2242 unsigned int pg_iter;
2246 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2248 count += GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers);
2255 * From the set of connections possible, choose at least num connections per
2256 * peer based on depth first traversal of peer connections. If DFS leaves
2257 * peers unconnected, ensure those peers get connections.
2259 * @param pg the peergroup we are dealing with
2260 * @param num how many connections at least should each peer have (if possible)?
2263 perform_dfs (struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2265 struct DFSContext dfs_ctx;
2268 uint32_t starting_peer;
2269 uint32_t least_connections;
2270 GNUNET_HashCode second_hash;
2272 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2274 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2279 while ((count_workingset_connections(pg) < num * pg->total) && (count_allowed_connections(pg) > 0))
2281 if (dfs_count % pg->total == 0) /* Restart the DFS at some weakly connected peer */
2283 least_connections = -1; /* Set to very high number */
2284 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2286 if (GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set) < least_connections)
2288 starting_peer = pg_iter;
2289 least_connections = GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers_working_set);
2294 if (GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers) == 0) /* Ensure there is at least one peer left to connect! */
2300 /* Choose a random peer from the chosen peers set of connections to add */
2301 dfs_ctx.chosen = GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CONTAINER_multihashmap_size(pg->peers[starting_peer].connect_peers));
2302 dfs_ctx.first_uid = starting_peer;
2303 dfs_ctx.first = &pg->peers[starting_peer];
2305 dfs_ctx.current = 0;
2307 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[starting_peer].connect_peers, &dfs_connect_iterator, &dfs_ctx);
2308 /* Remove the second from the first, since we will be continuing the search and may encounter the first peer again! */
2309 hash_from_uid(dfs_ctx.second_uid, &second_hash);
2310 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[starting_peer].connect_peers, &second_hash, pg->peers[dfs_ctx.second_uid].daemon));
2311 starting_peer = dfs_ctx.second_uid;
2314 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2319 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2321 /* Remove the "old" connections */
2322 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2323 /* And replace with the working set */
2324 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2325 fprintf(stderr, "Finished! Hashmap size %u\n", GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2331 * @param pg the peer group struct representing the running peers
2332 * @param topology which topology to connect the peers in
2333 * @param options options for connecting the topology
2334 * @param option_modifier modifier for options that take a parameter
2336 * There are many ways to connect peers that are supported by this function.
2337 * To connect peers in the same topology that was created via the
2338 * GNUNET_TESTING_create_topology, the topology variable must be set to
2339 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
2340 * a new instance of that topology will be generated and attempted to be
2341 * connected. This could result in some connections being impossible,
2342 * because some topologies are non-deterministic.
2346 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
2347 enum GNUNET_TESTING_Topology topology,
2348 enum GNUNET_TESTING_TopologyOption options,
2349 double option_modifier)
2351 int num_connections;
2355 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2358 _("Creating clique topology\n"));
2360 num_connections = create_clique (pg, &add_actual_connections);
2362 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2364 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2365 _("Creating small world (ring) topology\n"));
2367 num_connections = create_small_world_ring (pg, &add_actual_connections);
2369 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2372 _("Creating small world (2d-torus) topology\n"));
2374 num_connections = create_small_world (pg, &add_actual_connections);
2376 case GNUNET_TESTING_TOPOLOGY_RING:
2378 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2379 _("Creating ring topology\n"));
2381 num_connections = create_ring (pg, &add_actual_connections);
2383 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2385 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2386 _("Creating 2d torus topology\n"));
2388 num_connections = create_2d_torus (pg, &add_actual_connections);
2390 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2392 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2393 _("Creating Erdos-Renyi topology\n"));
2395 num_connections = create_erdos_renyi (pg, &add_actual_connections);
2397 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2399 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2400 _("Creating InterNAT topology\n"));
2402 num_connections = create_nated_internet (pg, &add_actual_connections);
2404 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2406 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2407 _("Creating Scale Free topology\n"));
2409 num_connections = create_scale_free (pg, &add_actual_connections);
2411 case GNUNET_TESTING_TOPOLOGY_NONE:
2412 num_connections = copy_allowed_topology(pg);
2415 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unknown topology specification, can't connect peers!\n");
2416 return GNUNET_SYSERR;
2421 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM: /* Create a random subset of total connections based on parameter */
2422 choose_random_connections(pg, option_modifier);
2424 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM: /* Create at least X connections per peer (if possible!) */
2425 choose_minimum(pg, (unsigned int)option_modifier);
2427 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS: /* Choose a random starting point, randomly walk graph, try to get each peer X connections */
2428 perform_dfs(pg, (int)option_modifier);
2430 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
2432 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
2438 return connect_topology(pg);
2442 * Function which continues a peer group starting up
2443 * after successfully generating hostkeys for each peer.
2445 * @param pg the peer group to continue starting
2449 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
2453 for (i = 0; i < pg->total; i++)
2455 GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
2460 * Start count gnunetd processes with the same set of transports and
2461 * applications. The port numbers (any option called "PORT") will be
2462 * adjusted to ensure that no two peers running on the same system
2463 * have the same port(s) in their respective configurations.
2465 * @param sched scheduler to use
2466 * @param cfg configuration template to use
2467 * @param total number of daemons to start
2468 * @param timeout total time allowed for peers to start
2469 * @param hostkey_callback function to call on each peers hostkey generation
2470 * if NULL, peers will be started by this call, if non-null,
2471 * GNUNET_TESTING_daemons_continue_startup must be called after
2472 * successful hostkey generation
2473 * @param hostkey_cls closure for hostkey callback
2474 * @param cb function to call on each daemon that was started
2475 * @param cb_cls closure for cb
2476 * @param connect_callback function to call each time two hosts are connected
2477 * @param connect_callback_cls closure for connect_callback
2478 * @param hostnames space-separated list of hostnames to use; can be NULL (to run
2479 * everything on localhost).
2480 * @return NULL on error, otherwise handle to control peer group
2482 struct GNUNET_TESTING_PeerGroup *
2483 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
2484 const struct GNUNET_CONFIGURATION_Handle *cfg,
2486 struct GNUNET_TIME_Relative timeout,
2487 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
2489 GNUNET_TESTING_NotifyDaemonRunning cb,
2491 GNUNET_TESTING_NotifyConnection
2492 connect_callback, void *connect_callback_cls,
2493 const char *hostnames)
2495 struct GNUNET_TESTING_PeerGroup *pg;
2499 const char *hostname;
2500 char *baseservicehome;
2501 char *newservicehome;
2503 struct GNUNET_CONFIGURATION_Handle *pcfg;
2505 unsigned int hostcnt;
2514 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
2518 pg->cb_cls = cb_cls;
2519 pg->notify_connection = connect_callback;
2520 pg->notify_connection_cls = connect_callback_cls;
2522 pg->max_timeout = GNUNET_TIME_relative_to_absolute(timeout);
2523 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
2524 if (NULL != hostnames)
2527 /* skip leading spaces */
2528 while ((0 != *hostnames) && (isspace (*hostnames)))
2531 while ('\0' != *rpos)
2533 if (isspace (*rpos))
2537 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
2539 start = GNUNET_strdup (hostnames);
2541 while ('\0' != *pos)
2546 if (strlen (start) > 0)
2548 pg->hosts[off].minport = LOW_PORT;
2549 pg->hosts[off++].hostname = start;
2555 if (strlen (start) > 0)
2557 pg->hosts[off].minport = LOW_PORT;
2558 pg->hosts[off++].hostname = start;
2562 GNUNET_free (start);
2563 GNUNET_free (pg->hosts);
2567 minport = 0; /* make gcc happy */
2574 for (off = 0; off < total; off++)
2578 hostname = pg->hosts[off % hostcnt].hostname;
2579 pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
2584 pcfg = make_config (cfg, &minport, hostname);
2589 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2591 ("Could not create configuration for peer number %u on `%s'!\n"),
2592 off, hostname == NULL ? "localhost" : hostname);
2597 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
2600 GNUNET_asprintf (&newservicehome,
2601 "%s/%d/", baseservicehome, off);
2602 GNUNET_free (baseservicehome);
2606 tmpdir = getenv ("TMPDIR");
2607 tmpdir = tmpdir ? tmpdir : "/tmp";
2608 GNUNET_asprintf (&newservicehome,
2611 "gnunet-testing-test-test", off);
2613 GNUNET_CONFIGURATION_set_value_string (pcfg,
2615 "SERVICEHOME", newservicehome);
2616 GNUNET_free (newservicehome);
2617 pg->peers[off].cfg = pcfg;
2618 pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total);
2619 pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total);
2620 pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total);
2621 pg->peers[off].pg = pg;
2622 pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
2629 if (NULL == pg->peers[off].daemon)
2630 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2631 _("Could not start peer number %u!\n"), off);
2638 * Get a daemon by number, so callers don't have to do nasty
2639 * offsetting operation.
2641 struct GNUNET_TESTING_Daemon *
2642 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
2644 if (position < pg->total)
2645 return pg->peers[position].daemon;
2651 * Prototype of a function that will be called when a
2652 * particular operation was completed the testing library.
2654 * @param id id of the peer that was restarted
2655 * @param cfg handle to the configuration of the peer
2656 * @param d handle to the daemon that was restarted
2657 * @param emsg NULL on success
2659 void restart_callback (void *cls,
2660 const struct GNUNET_PeerIdentity *id,
2661 const struct GNUNET_CONFIGURATION_Handle *cfg,
2662 struct GNUNET_TESTING_Daemon *d,
2665 struct RestartContext *restart_context = cls;
2669 restart_context->peers_restarted++;
2673 restart_context->peers_restart_failed++;
2676 if (restart_context->peers_restarted == restart_context->peer_group->total)
2678 restart_context->callback(restart_context->callback_cls, NULL);
2679 GNUNET_free(restart_context);
2681 else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total)
2683 restart_context->callback(restart_context->callback_cls, "Failed to restart peers!");
2684 GNUNET_free(restart_context);
2690 * Callback for informing us about a successful
2691 * or unsuccessful churn stop call.
2693 * @param cls a ChurnContext
2694 * @param emsg NULL on success, non-NULL on failure
2698 churn_stop_callback (void *cls, const char *emsg)
2700 struct ChurnContext *churn_ctx = cls;
2701 unsigned int total_left;
2702 char *error_message;
2704 error_message = NULL;
2707 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Churn stop callback failed with error `%s'\n", emsg);
2708 churn_ctx->num_failed_stop++;
2712 churn_ctx->num_to_stop--;
2715 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
2717 if (total_left == 0)
2719 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
2720 GNUNET_asprintf(&error_message, "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", churn_ctx->num_failed_start, churn_ctx->num_failed_stop);
2721 churn_ctx->cb(churn_ctx->cb_cls, error_message);
2722 GNUNET_free_non_null(error_message);
2723 GNUNET_free(churn_ctx);
2728 * Callback for informing us about a successful
2729 * or unsuccessful churn start call.
2731 * @param cls a ChurnContext
2732 * @param id the peer identity of the started peer
2733 * @param cfg the handle to the configuration of the peer
2734 * @param d handle to the daemon for the peer
2735 * @param emsg NULL on success, non-NULL on failure
2739 churn_start_callback (void *cls,
2740 const struct GNUNET_PeerIdentity *id,
2741 const struct GNUNET_CONFIGURATION_Handle *cfg,
2742 struct GNUNET_TESTING_Daemon *d,
2745 struct ChurnContext *churn_ctx = cls;
2746 unsigned int total_left;
2747 char *error_message;
2749 error_message = NULL;
2752 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Churn stop callback failed with error `%s'\n", emsg);
2753 churn_ctx->num_failed_start++;
2757 churn_ctx->num_to_start--;
2760 total_left = (churn_ctx->num_to_stop - churn_ctx->num_failed_stop) + (churn_ctx->num_to_start - churn_ctx->num_failed_start);
2762 if (total_left == 0)
2764 if ((churn_ctx->num_failed_stop > 0) || (churn_ctx->num_failed_start > 0))
2765 GNUNET_asprintf(&error_message, "Churn didn't complete successfully, %u peers failed to start %u peers failed to be stopped!", churn_ctx->num_failed_start, churn_ctx->num_failed_stop);
2766 churn_ctx->cb(churn_ctx->cb_cls, error_message);
2767 GNUNET_free_non_null(error_message);
2768 GNUNET_free(churn_ctx);
2774 * Simulate churn by stopping some peers (and possibly
2775 * re-starting others if churn is called multiple times). This
2776 * function can only be used to create leave-join churn (peers "never"
2777 * leave for good). First "voff" random peers that are currently
2778 * online will be taken offline; then "von" random peers that are then
2779 * offline will be put back online. No notifications will be
2780 * generated for any of these operations except for the callback upon
2783 * @param pg handle for the peer group
2784 * @param voff number of peers that should go offline
2785 * @param von number of peers that should come back online;
2786 * must be zero on first call (since "testbed_start"
2787 * always starts all of the peers)
2788 * @param timeout how long to wait for operations to finish before
2790 * @param cb function to call at the end
2791 * @param cb_cls closure for cb
2794 GNUNET_TESTING_daemons_churn (struct GNUNET_TESTING_PeerGroup *pg,
2797 struct GNUNET_TIME_Relative timeout,
2798 GNUNET_TESTING_NotifyCompletion cb,
2801 struct ChurnContext *churn_ctx;
2802 unsigned int running;
2803 unsigned int stopped;
2805 unsigned int *running_arr;
2806 unsigned int *stopped_arr;
2807 unsigned int *running_permute;
2808 unsigned int *stopped_permute;
2813 for (i = 0; i < pg->total; i++)
2815 if (pg->peers[i].daemon->running == GNUNET_YES)
2827 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to stop more peers than are currently running!\n");
2828 cb(cb_cls, "Trying to stop more peers than are currently running!");
2834 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Trying to start more peers than are currently stopped!\n");
2835 cb(cb_cls, "Trying to start more peers than are currently stopped!");
2839 churn_ctx = GNUNET_malloc(sizeof(struct ChurnContext));
2840 running_arr = GNUNET_malloc(running * sizeof(unsigned int));
2841 stopped_arr = GNUNET_malloc(stopped * sizeof(unsigned int));
2842 running_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, running);
2843 stopped_permute = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, stopped);
2848 churn_ctx->num_to_start = von;
2849 churn_ctx->num_to_stop = voff;
2851 churn_ctx->cb_cls = cb_cls;
2853 for (i = 0; i < pg->total; i++)
2855 if (pg->peers[i].daemon->running == GNUNET_YES)
2857 running_arr[running] = i;
2862 stopped_arr[stopped] = i;
2867 for (i = 0; i < voff; i++)
2869 GNUNET_TESTING_daemon_stop(pg->peers[running_arr[running_permute[i]]].daemon, timeout, &churn_stop_callback, churn_ctx, GNUNET_NO, GNUNET_YES);
2872 for (i = 0; i < von; i++)
2874 GNUNET_TESTING_daemon_start_stopped(pg->peers[stopped_arr[stopped_permute[i]]].daemon, timeout, &churn_start_callback, churn_ctx);
2880 * Restart all peers in the given group.
2882 * @param pg the handle to the peer group
2883 * @param timeout how long to wait on failure
2884 * @param callback function to call on completion (or failure)
2885 * @param callback_cls closure for the callback function
2888 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls)
2890 struct RestartContext *restart_context;
2895 restart_context = GNUNET_malloc(sizeof(struct RestartContext));
2896 restart_context->peer_group = pg;
2897 restart_context->peers_restarted = 0;
2898 restart_context->callback = callback;
2899 restart_context->callback_cls = callback_cls;
2901 for (off = 0; off < pg->total; off++)
2903 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context);
2909 * Shutdown all peers started in the given group.
2911 * @param pg handle to the peer group
2912 * @param timeout how long to wait for shutdown
2916 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg, struct GNUNET_TIME_Relative timeout)
2920 for (off = 0; off < pg->total; off++)
2922 /* FIXME: should we wait for our
2923 continuations to be called here? This
2924 would require us to take a continuation
2927 if (NULL != pg->peers[off].daemon)
2928 GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, timeout, NULL, NULL, GNUNET_YES, GNUNET_NO);
2929 if (NULL != pg->peers[off].cfg)
2930 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
2932 if (pg->peers[off].allowed_peers != NULL)
2933 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers);
2934 if (pg->peers[off].connect_peers != NULL)
2935 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers);
2936 if (pg->peers[off].blacklisted_peers != NULL)
2937 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers);
2940 GNUNET_free (pg->peers);
2941 if (NULL != pg->hosts)
2943 GNUNET_free (pg->hosts[0].hostname);
2944 GNUNET_free (pg->hosts);
2950 /* end of testing_group.c */