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_YES
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);
63 * The group of peers being restarted
65 struct GNUNET_TESTING_PeerGroup *peer_group;
68 * How many peers have been restarted thus far
70 unsigned int peers_restarted;
73 * How many peers got an error when restarting
75 unsigned int peers_restart_failed;
78 * The function to call once all peers have been restarted
80 GNUNET_TESTING_NotifyCompletion callback;
83 * Closure for callback function
89 struct CreateTopologyContext
93 * Function to call with number of connections
95 GNUNET_TESTING_NotifyConnections cont;
98 * Closure for connection notification
104 struct PeerConnection
109 struct PeerConnection *next;
112 * Pointer to daemon handle
114 struct GNUNET_TESTING_Daemon *daemon;
120 * Data we keep per peer.
125 * (Initial) configuration of the host.
126 * (initial because clients could change
127 * it and we would not know about those
130 struct GNUNET_CONFIGURATION_Handle *cfg;
133 * Handle for controlling the daemon.
135 struct GNUNET_TESTING_Daemon *daemon;
138 * The peergroup this peer belongs to.
140 struct GNUNET_TESTING_PeerGroup *pg;
143 * Linked list of peer connections (pointers)
145 //struct PeerConnection *connected_peers;
147 * Hash map of allowed peer connections (F2F created topology)
149 struct GNUNET_CONTAINER_MultiHashMap *allowed_peers;
152 * Hash map of blacklisted peers
154 struct GNUNET_CONTAINER_MultiHashMap *blacklisted_peers;
157 * Hash map of peer connections
159 struct GNUNET_CONTAINER_MultiHashMap *connect_peers;
162 * Temporary hash map of peer connections
164 struct GNUNET_CONTAINER_MultiHashMap *connect_peers_working_set;
167 * Total number of connections this peer has
174 * Data we keep per host.
184 * Lowest port that we have not yet used
192 * Handle to a group of GNUnet peers.
194 struct GNUNET_TESTING_PeerGroup
199 struct GNUNET_SCHEDULER_Handle *sched;
202 * Configuration template.
204 const struct GNUNET_CONFIGURATION_Handle *cfg;
207 * Function to call on each started daemon.
209 GNUNET_TESTING_NotifyDaemonRunning cb;
217 * Function to call on each topology connection created
219 GNUNET_TESTING_NotifyConnection notify_connection;
222 * Callback for notify_connection
224 void *notify_connection_cls;
227 * NULL-terminated array of information about
230 struct HostData *hosts;
233 * Array of "total" peers.
235 struct PeerData *peers;
238 * Number of peers in this group.
245 * Convert unique ID to hash code.
247 * @param uid unique ID to convert
248 * @param hash set to uid (extended with zeros)
251 hash_from_uid (uint32_t uid,
252 GNUNET_HashCode *hash)
254 memset (hash, 0, sizeof(GNUNET_HashCode));
255 *((uint32_t*)hash) = uid;
259 * Convert hash code to unique ID.
261 * @param uid unique ID to convert
262 * @param hash set to uid (extended with zeros)
265 uid_from_hash (const GNUNET_HashCode *hash, uint32_t *uid)
267 memcpy (uid, hash, sizeof(uint32_t));
272 struct GNUNET_CONFIGURATION_Handle *ret;
274 const char *hostname;
278 struct ConnectContext
280 struct GNUNET_TESTING_Daemon *first;
282 struct GNUNET_TESTING_Daemon *second;
284 struct GNUNET_TESTING_PeerGroup *pg;
288 * Number of connects we are waiting on, allows us to rate limit
291 static int outstanding_connects;
295 * Function to iterate over options. Copies
296 * the options to the target configuration,
297 * updating PORT values as needed.
300 * @param section name of the section
301 * @param option name of the option
302 * @param value value of the option
305 update_config (void *cls,
306 const char *section, const char *option, const char *value)
308 struct UpdateContext *ctx = cls;
312 if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
314 GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
318 if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
320 value = ctx->hostname;
323 GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
328 * Create a new configuration using the given configuration
329 * as a template; however, each PORT in the existing cfg
330 * must be renumbered by incrementing "*port". If we run
331 * out of "*port" numbers, return NULL.
333 * @param cfg template configuration
334 * @param port port numbers to use, update to reflect
335 * port numbers that were used
336 * @param hostname hostname of the controlling host, to allow control connections from
338 * @return new configuration, NULL on error
340 static struct GNUNET_CONFIGURATION_Handle *
341 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, const char *hostname)
343 struct UpdateContext uc;
350 uc.ret = GNUNET_CONFIGURATION_create ();
351 uc.hostname = hostname;
353 GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
354 if (uc.nport >= HIGH_PORT)
357 GNUNET_CONFIGURATION_destroy (uc.ret);
361 if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
363 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
364 GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
365 GNUNET_free_non_null(control_host);
366 GNUNET_free(allowed_hosts);
370 /* arm needs to know to allow connections from the host on which it is running,
371 * otherwise gnunet-arm is unable to connect to it in some instances */
372 if (hostname != NULL)
374 GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
375 GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
376 GNUNET_free(allowed_hosts);
379 *port = (uint16_t) uc.nport;
385 * Add entries to the peers connect list
387 * @param pg the peer group we are working with
388 * @param first index of the first peer
389 * @param second index of the second peer
391 * @return the number of connections added (can be 0, 1 or 2)
392 * technically should only be 0 or 2, but the small price
393 * of iterating over the lists (hashmaps in the future)
394 * for being sure doesn't bother me!
398 add_actual_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
404 GNUNET_HashCode hash_first;
405 GNUNET_HashCode hash_second;
407 hash_from_uid(first, &hash_first);
408 hash_from_uid(second, &hash_second);
410 add_first = GNUNET_NO;
411 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].connect_peers, &hash_second))
413 add_first = GNUNET_YES;
416 add_second = GNUNET_NO;
417 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].connect_peers, &hash_first))
419 add_second = GNUNET_YES;
425 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].connect_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
431 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].connect_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
440 * Add entries to the peers allowed connections list
442 * @param pg the peer group we are working with
443 * @param first index of the first peer
444 * @param second index of the second peer
446 * @return the number of connections added (can be 0, 1 or 2)
447 * technically should only be 0 or 2, but the small price
448 * of iterating over the lists (hashmaps in the future)
449 * for being sure doesn't bother me!
453 add_allowed_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
457 struct PeerConnection *first_iter;
458 struct PeerConnection *second_iter;
459 struct PeerConnection *new_first;
460 struct PeerConnection *new_second;
465 GNUNET_HashCode hash_first;
466 GNUNET_HashCode hash_second;
468 hash_from_uid(first, &hash_first);
469 hash_from_uid(second, &hash_second);
471 add_first = GNUNET_NO;
472 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].allowed_peers, &hash_second))
474 add_first = GNUNET_YES;
477 add_second = GNUNET_NO;
478 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].allowed_peers, &hash_first))
480 add_second = GNUNET_YES;
483 first_iter = pg->peers[first].connected_peers;
484 while (first_iter != NULL)
486 if (first_iter->daemon == pg->peers[second].daemon)
487 add_first = GNUNET_NO;
488 first_iter = first_iter->next;
491 second_iter = pg->peers[second].connected_peers;
492 add_second = GNUNET_YES;
493 while (second_iter != NULL)
495 if (second_iter->daemon == pg->peers[first].daemon)
496 add_second = GNUNET_NO;
497 second_iter = second_iter->next;
504 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].allowed_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
506 new_first = GNUNET_malloc(sizeof(struct PeerConnection));
507 new_first->daemon = pg->peers[second].daemon;
508 new_first->next = pg->peers[first].connected_peers;
509 pg->peers[first].connected_peers = new_first;
510 pg->peers[first].num_connections++;
517 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].allowed_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
519 new_second = GNUNET_malloc(sizeof(struct PeerConnection));
520 new_second->daemon = pg->peers[first].daemon;
521 new_second->next = pg->peers[second].connected_peers;
522 pg->peers[second].connected_peers = new_second;
523 pg->peers[first].num_connections++;
532 * Add entries to the peers blacklisted list
534 * @param pg the peer group we are working with
535 * @param first index of the first peer
536 * @param second index of the second peer
538 * @return the number of connections added (can be 0, 1 or 2)
542 blacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
547 GNUNET_HashCode hash_first;
548 GNUNET_HashCode hash_second;
550 hash_from_uid(first, &hash_first);
551 hash_from_uid(second, &hash_second);
553 add_first = GNUNET_NO;
554 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second))
556 add_first = GNUNET_YES;
559 add_second = GNUNET_NO;
560 if (GNUNET_NO == GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first))
562 add_second = GNUNET_YES;
568 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
574 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
582 * Remove entries from the peers blacklisted list
584 * @param pg the peer group we are working with
585 * @param first index of the first peer
586 * @param second index of the second peer
588 * @return the number of connections removed (can be 0, 1 or 2)
592 unblacklist_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
597 GNUNET_HashCode hash_first;
598 GNUNET_HashCode hash_second;
600 hash_from_uid(first, &hash_first);
601 hash_from_uid(second, &hash_second);
603 remove_first = GNUNET_CONTAINER_multihashmap_contains(pg->peers[first].blacklisted_peers, &hash_second);
604 remove_second = GNUNET_CONTAINER_multihashmap_contains(pg->peers[second].blacklisted_peers, &hash_first);
609 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[first].blacklisted_peers, &hash_second, pg->peers[second].daemon));
615 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(pg->peers[second].blacklisted_peers, &hash_first, pg->peers[first].daemon));
623 * Scale free network construction as described in:
625 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
627 * Start with a network of "one" peer, then progressively add
628 * peers up to the total number. At each step, iterate over
629 * all possible peers and connect new peer based on number of
630 * existing connections of the target peer.
632 * @param pg the peer group we are dealing with
634 * @return the number of connections created
637 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
640 unsigned int total_connections;
641 unsigned int outer_count;
643 unsigned int previous_total_connections;
647 GNUNET_assert(pg->total > 1);
649 /* Add a connection between the first two nodes */
650 total_connections = proc(pg, 0, 1);
652 for (outer_count = 1; outer_count < pg->total; outer_count++)
654 previous_total_connections = total_connections;
655 for (i = 0; i < outer_count; i++)
657 probability = pg->peers[i].num_connections / (double)previous_total_connections;
658 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
659 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
661 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
662 "Considering connecting peer %d to peer %d\n",
665 if (random < probability)
668 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
669 "Connecting peer %d to peer %d\n",
672 total_connections += proc(pg, outer_count, i);
677 return total_connections;
681 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
686 unsigned int randomPeer;
687 double random, logNModifier, percentage;
688 unsigned int smallWorldConnections;
694 int connect_attempts;
696 logNModifier = 0.5; /* FIXME: default value? */
697 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
702 if (sscanf(p_string, "%lf", &logNModifier) != 1)
703 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
704 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
708 GNUNET_free (p_string);
710 percentage = 0.5; /* FIXME: default percentage? */
711 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
716 if (sscanf(p_string, "%lf", &percentage) != 1)
717 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
718 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
722 GNUNET_free (p_string);
724 natLog = log (pg->total);
725 connsPerPeer = ceil (natLog * logNModifier);
727 if (connsPerPeer % 2 == 1)
730 smallWorldConnections = 0;
731 connect_attempts = 0;
732 for (i = 0; i < pg->total; i++)
735 max = i + connsPerPeer / 2;
736 min = i - connsPerPeer / 2;
738 if (max > pg->total - 1)
740 max = max - pg->total;
746 min = pg->total - 1 + min;
750 for (j = 0; j < connsPerPeer / 2; j++)
752 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
753 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
754 if (random < percentage)
756 /* Connect to uniformly selected random peer */
758 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
760 while ((((randomPeer < max) && (randomPeer > min))
761 && (useAnd == 0)) || (((randomPeer > min)
762 || (randomPeer < max))
766 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
769 smallWorldConnections +=
770 proc (pg, i, randomPeer);
774 nodeToConnect = i + j + 1;
775 if (nodeToConnect > pg->total - 1)
777 nodeToConnect = nodeToConnect - pg->total;
780 proc (pg, i, nodeToConnect);
786 connect_attempts += smallWorldConnections;
788 return connect_attempts;
793 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
795 unsigned int outer_count, inner_count;
797 int connect_attempts;
798 double nat_percentage;
801 nat_percentage = 0.6; /* FIXME: default percentage? */
802 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
807 if (sscanf(p_string, "%lf", &nat_percentage) != 1)
808 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
809 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
813 GNUNET_free (p_string);
818 cutoff = (unsigned int) (nat_percentage * pg->total);
820 connect_attempts = 0;
822 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
824 for (inner_count = outer_count + 1; inner_count < pg->total;
827 if ((outer_count > cutoff) || (inner_count > cutoff))
830 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
831 "Connecting peer %d to peer %d\n",
832 outer_count, inner_count);
834 connect_attempts += proc(pg, outer_count, inner_count);
839 return connect_attempts;
846 create_small_world (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
848 unsigned int i, j, k;
852 unsigned int toggle = 1;
853 unsigned int nodeToConnect;
855 unsigned int node1Row;
856 unsigned int node1Col;
857 unsigned int node2Row;
858 unsigned int node2Col;
859 unsigned int distance;
860 double probability, random, percentage;
861 unsigned int smallWorldConnections;
863 int connect_attempts;
864 square = floor (sqrt (pg->total));
868 percentage = 0.5; /* FIXME: default percentage? */
869 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
874 if (sscanf(p_string, "%lf", &percentage) != 1)
875 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
876 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
880 GNUNET_free (p_string);
882 probability = 0.5; /* FIXME: default percentage? */
883 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
888 if (sscanf(p_string, "%lf", &probability) != 1)
889 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
890 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
894 GNUNET_free (p_string);
896 if (square * square != pg->total)
898 while (rows * cols < pg->total)
909 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
910 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
914 connect_attempts = 0;
915 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
916 * to the node to its right and above. Once this is over, we'll have our torus!
917 * Special case for the last node (if the rows and columns are not equal), connect
918 * to the first in the row to maintain topology.
920 for (i = 0; i < pg->total; i++)
922 /* First connect to the node to the right */
923 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
924 nodeToConnect = i + 1;
925 else if (i + 1 == pg->total)
926 nodeToConnect = rows * cols - cols;
928 nodeToConnect = i - cols + 1;
930 connect_attempts += proc (pg, i, nodeToConnect);
933 nodeToConnect = (rows * cols) - cols + i;
935 nodeToConnect = i - cols;
937 if (nodeToConnect < pg->total)
938 connect_attempts += proc (pg, i, nodeToConnect);
940 natLog = log (pg->total);
941 #if VERBOSE_TESTING > 2
942 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943 _("natural log of %d is %d, will run %d iterations\n"),
944 pg->total, natLog, (int) (natLog * percentage));
945 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
947 smallWorldConnections = 0;
948 for (i = 0; i < (int) (natLog * percentage); i++)
950 for (j = 0; j < pg->total; j++)
952 /* Determine the row and column of node at position j on the 2d torus */
954 node1Col = j - (node1Row * cols);
955 for (k = 0; k < pg->total; k++)
957 /* Determine the row and column of node at position k on the 2d torus */
959 node2Col = k - (node2Row * cols);
960 /* Simple Cartesian distance */
961 distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
964 /* Calculate probability as 1 over the square of the distance */
965 probability = 1.0 / (distance * distance);
966 /* Choose a random value between 0 and 1 */
967 random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
968 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
969 /* If random < probability, then connect the two nodes */
970 if (random < probability)
971 smallWorldConnections += proc (pg, j, k);
977 connect_attempts += smallWorldConnections;
978 #if VERBOSE_TESTING > 2
979 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
980 _("Total connections added for small world: %d!\n"),
981 smallWorldConnections);
983 return connect_attempts;
989 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
992 unsigned int outer_count;
993 unsigned int inner_count;
994 int connect_attempts;
998 probability = 0.5; /* FIXME: default percentage? */
999 if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
1004 if (sscanf(p_string, "%lf", &probability) != 1)
1005 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1006 _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
1010 GNUNET_free (p_string);
1012 connect_attempts = 0;
1013 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1015 for (inner_count = outer_count + 1; inner_count < pg->total;
1018 temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1019 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
1021 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1022 _("rand is %f probability is %f\n"), temp_rand,
1025 if (temp_rand < probability)
1027 connect_attempts += proc (pg, outer_count, inner_count);
1032 return connect_attempts;
1036 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1039 unsigned int square;
1042 unsigned int toggle = 1;
1043 unsigned int nodeToConnect;
1044 int connect_attempts;
1046 connect_attempts = 0;
1048 square = floor (sqrt (pg->total));
1052 if (square * square != pg->total)
1054 while (rows * cols < pg->total)
1056 if (toggle % 2 == 0)
1065 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1066 _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
1069 /* Rows and columns are all sorted out, now iterate over all nodes and connect each
1070 * to the node to its right and above. Once this is over, we'll have our torus!
1071 * Special case for the last node (if the rows and columns are not equal), connect
1072 * to the first in the row to maintain topology.
1074 for (i = 0; i < pg->total; i++)
1076 /* First connect to the node to the right */
1077 if (((i + 1) % cols != 0) && (i + 1 != pg->total))
1078 nodeToConnect = i + 1;
1079 else if (i + 1 == pg->total)
1080 nodeToConnect = rows * cols - cols;
1082 nodeToConnect = i - cols + 1;
1084 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1085 "Connecting peer %d to peer %d\n",
1088 connect_attempts += proc(pg, i, nodeToConnect);
1090 /* Second connect to the node immediately above */
1092 nodeToConnect = (rows * cols) - cols + i;
1094 nodeToConnect = i - cols;
1096 if (nodeToConnect < pg->total)
1099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1100 "Connecting peer %d to peer %d\n",
1103 connect_attempts += proc(pg, i, nodeToConnect);
1108 return connect_attempts;
1114 create_clique (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1116 unsigned int outer_count;
1117 unsigned int inner_count;
1118 int connect_attempts;
1120 connect_attempts = 0;
1122 for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
1124 for (inner_count = outer_count + 1; inner_count < pg->total;
1128 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1129 "Connecting peer %d to peer %d\n",
1130 outer_count, inner_count);
1132 connect_attempts += proc(pg, outer_count, inner_count);
1136 return connect_attempts;
1141 create_ring (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_ConnectionProcessor proc)
1144 int connect_attempts;
1146 connect_attempts = 0;
1148 /* Connect each peer to the next highest numbered peer */
1149 for (count = 0; count < pg->total - 1; count++)
1152 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153 "Connecting peer %d to peer %d\n",
1156 connect_attempts += proc(pg, count, count + 1);
1159 /* Connect the last peer to the first peer */
1160 connect_attempts += proc(pg, pg->total - 1, 0);
1162 return connect_attempts;
1167 * Iterator for writing friends of a peer to a file.
1169 * @param cls closure, an open writable file handle
1170 * @param key the key the daemon was stored under
1171 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1173 * @return GNUNET_YES to continue iteration
1175 * TODO: Could replace friend_file_iterator and blacklist_file_iterator
1176 * with a single file_iterator that takes a closure which contains
1177 * the prefix to write before the peer. Then this could be used
1178 * for blacklisting multiple transports and writing the friend
1179 * file. I'm sure *someone* will complain loudly about other
1180 * things that negate these functions even existing so no point in
1184 friend_file_iterator (void *cls,
1185 const GNUNET_HashCode * key,
1188 FILE *temp_friend_handle = cls;
1189 struct GNUNET_TESTING_Daemon *peer = value;
1190 struct GNUNET_PeerIdentity *temppeer;
1191 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1193 temppeer = &peer->id;
1194 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1195 fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
1202 * Iterator for writing blacklist data to appropriate files.
1204 * @param cls closure, an open writable file handle
1205 * @param key the key the daemon was stored under
1206 * @param value the GNUNET_TESTING_Daemon that needs to be written.
1208 * @return GNUNET_YES to continue iteration
1211 blacklist_file_iterator (void *cls,
1212 const GNUNET_HashCode * key,
1215 FILE *temp_blacklist_handle = cls;
1216 struct GNUNET_TESTING_Daemon *peer = value;
1217 struct GNUNET_PeerIdentity *temppeer;
1218 struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
1220 temppeer = &peer->id;
1221 GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
1222 fprintf(temp_blacklist_handle, "tcp:%s\n", (char *)&peer_enc);
1228 * Create the friend files based on the PeerConnection's
1229 * of each peer in the peer group, and copy the files
1230 * to the appropriate place
1232 * @param pg the peer group we are dealing with
1235 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
1237 FILE *temp_friend_handle;
1238 unsigned int pg_iter;
1239 char *temp_service_path;
1243 enum GNUNET_OS_ProcessStatusType type;
1244 unsigned long return_code;
1249 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1250 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1252 mytemp = GNUNET_DISK_mktemp("friends");
1253 GNUNET_assert(mytemp != NULL);
1254 temp_friend_handle = fopen (mytemp, "wt");
1255 GNUNET_assert(temp_friend_handle != NULL);
1256 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, &friend_file_iterator, temp_friend_handle);
1257 fclose(temp_friend_handle);
1260 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1262 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1263 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1266 if (UNLINK (mytemp) != 0)
1267 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1268 GNUNET_free (mytemp);
1272 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1274 GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
1275 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1276 "mv", mytemp, arg, NULL);
1278 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1279 _("Copying file with command cp %s %s\n"), mytemp, arg);
1284 else /* Remote, scp the file to the correct place */
1286 if (NULL != pg->peers[pg_iter].daemon->username)
1287 GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1289 GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1290 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1291 "scp", mytemp, arg, NULL);
1294 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1295 _("Copying file with command scp %s %s\n"), mytemp, arg);
1299 GNUNET_free (temp_service_path);
1300 GNUNET_free (mytemp);
1304 ret = GNUNET_SYSERR;
1305 while ((count < max_wait) && (ret != GNUNET_OK))
1308 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1311 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1312 _("Checking copy status of file %d\n"), pg_iter);
1314 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1316 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1318 ret = GNUNET_SYSERR;
1320 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1322 ret = GNUNET_SYSERR;
1326 pidarr[pg_iter] = 0;
1328 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1329 _("File %d copied\n"), pg_iter);
1335 if (ret == GNUNET_SYSERR)
1342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1343 _("Finished copying all friend files!\n"));
1345 GNUNET_free(pidarr);
1351 * Create the blacklist files based on the PeerConnection's
1352 * of each peer in the peer group, and copy the files
1353 * to the appropriate place.
1355 * @param pg the peer group we are dealing with
1358 create_and_copy_blacklist_files (struct GNUNET_TESTING_PeerGroup *pg)
1360 FILE *temp_friend_handle;
1361 unsigned int pg_iter;
1362 char *temp_service_path;
1366 enum GNUNET_OS_ProcessStatusType type;
1367 unsigned long return_code;
1372 pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
1373 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1375 mytemp = GNUNET_DISK_mktemp("blacklist");
1376 GNUNET_assert(mytemp != NULL);
1377 temp_friend_handle = fopen (mytemp, "wt");
1378 GNUNET_assert(temp_friend_handle != NULL);
1379 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].blacklisted_peers, &blacklist_file_iterator, temp_friend_handle);
1380 fclose(temp_friend_handle);
1383 GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
1385 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1386 _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
1389 if (UNLINK (mytemp) != 0)
1390 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
1391 GNUNET_free (mytemp);
1395 if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
1397 GNUNET_asprintf (&arg, "%s/blacklist", temp_service_path);
1398 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
1399 "mv", mytemp, arg, NULL);
1401 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1402 _("Copying file with command cp %s %s\n"), mytemp, arg);
1407 else /* Remote, scp the file to the correct place */
1409 if (NULL != pg->peers[pg_iter].daemon->username)
1410 GNUNET_asprintf (&arg, "%s@%s:%s/blacklist", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
1412 GNUNET_asprintf (&arg, "%s:%s/blacklist", pg->peers[pg_iter].daemon->hostname, temp_service_path);
1413 pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
1414 "scp", mytemp, arg, NULL);
1417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1418 _("Copying file with command scp %s %s\n"), mytemp, arg);
1422 GNUNET_free (temp_service_path);
1423 GNUNET_free (mytemp);
1427 ret = GNUNET_SYSERR;
1428 while ((count < max_wait) && (ret != GNUNET_OK))
1431 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1434 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1435 _("Checking copy status of file %d\n"), pg_iter);
1437 if (pidarr[pg_iter] != 0) /* Check for already completed! */
1439 if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1441 ret = GNUNET_SYSERR;
1443 else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1445 ret = GNUNET_SYSERR;
1449 pidarr[pg_iter] = 0;
1451 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1452 _("File %d copied\n"), pg_iter);
1458 if (ret == GNUNET_SYSERR)
1465 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1466 _("Finished copying all blacklist files!\n"));
1468 GNUNET_free(pidarr);
1474 * Internal notification of a connection, kept so that we can ensure some connections
1475 * happen instead of flooding all testing daemons with requests to connect.
1477 static void internal_connect_notify (void *cls,
1478 const struct GNUNET_PeerIdentity *first,
1479 const struct GNUNET_PeerIdentity *second,
1480 const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1481 const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1482 struct GNUNET_TESTING_Daemon *first_daemon,
1483 struct GNUNET_TESTING_Daemon *second_daemon,
1486 struct GNUNET_TESTING_PeerGroup *pg = cls;
1487 outstanding_connects--;
1489 pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
1493 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1495 struct ConnectContext *connect_context = cls;
1497 if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1500 if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1502 #if VERBOSE_TESTING > 2
1503 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1504 _("Delaying connect, we have too many outstanding connections!\n"));
1506 GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1510 #if VERBOSE_TESTING > 2
1511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1512 _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1514 outstanding_connects++;
1515 GNUNET_TESTING_daemons_connect (connect_context->first,
1516 connect_context->second,
1519 &internal_connect_notify,
1520 connect_context->pg);
1521 GNUNET_free(connect_context);
1526 * Iterator for actually scheduling connections to be created
1527 * between two peers.
1529 * @param cls closure, a GNUNET_TESTING_Daemon
1530 * @param key the key the second Daemon was stored under
1531 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1533 * @return GNUNET_YES to continue iteration
1536 connect_iterator (void *cls,
1537 const GNUNET_HashCode * key,
1540 struct PeerData *first = cls;
1541 struct GNUNET_TESTING_Daemon *second = value;
1542 struct ConnectContext *connect_context;
1544 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1545 connect_context->pg = first->pg;
1546 connect_context->first = first->daemon;
1547 connect_context->second = second;
1548 GNUNET_SCHEDULER_add_now(first->pg->sched, &schedule_connect, connect_context);
1554 * Iterator for copying all entries in the allowed hashmap to the
1557 * @param cls closure, a GNUNET_TESTING_Daemon
1558 * @param key the key the second Daemon was stored under
1559 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1561 * @return GNUNET_YES to continue iteration
1564 copy_topology_iterator (void *cls,
1565 const GNUNET_HashCode * key,
1568 struct PeerData *first = cls;
1570 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(first->connect_peers, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1576 * Make the peers to connect the same as those that are allowed to be
1579 * @param pg the peer group
1582 copy_allowed_topology (struct GNUNET_TESTING_PeerGroup *pg)
1584 unsigned int pg_iter;
1589 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1591 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].allowed_peers, ©_topology_iterator, &pg->peers[pg_iter]);
1592 if (GNUNET_SYSERR == ret)
1593 return GNUNET_SYSERR;
1595 total = total + ret;
1603 * Connect the topology as specified by the PeerConnection's
1604 * of each peer in the peer group
1606 * @param pg the peer group we are dealing with
1608 * @return the number of connections that will be attempted
1611 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1613 unsigned int pg_iter;
1617 struct PeerConnection *connection_iter;
1618 struct ConnectContext *connect_context;
1622 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1624 ret = GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &connect_iterator, &pg->peers[pg_iter]);
1625 if (GNUNET_SYSERR == ret)
1626 return GNUNET_SYSERR;
1628 total = total + ret;
1632 while (connection_iter != NULL)
1634 connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1635 connect_context->pg = pg;
1636 connect_context->first = ;
1637 connect_context->second = connection_iter->daemon;
1638 GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1639 connection_iter = connection_iter->next;
1648 * Takes a peer group and creates a topology based on the
1649 * one specified. Creates a topology means generates friend
1650 * files for the peers so they can only connect to those allowed
1651 * by the topology. This will only have an effect once peers
1652 * are started if the FRIENDS_ONLY option is set in the base
1653 * config. Also takes an optional restrict topology which
1654 * disallows direct TCP connections UNLESS they are specified in
1655 * the restricted topology.
1657 * @param pg the peer group struct representing the running peers
1658 * @param topology which topology to connect the peers in
1659 * @param restrict_topology allow only direct TCP connections in this topology
1660 * use GNUNET_TESTING_TOPOLOGY_NONE for no restrictions
1662 * @return the maximum number of connections were all allowed peers
1663 * connected to each other
1666 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg,
1667 enum GNUNET_TESTING_Topology topology,
1668 enum GNUNET_TESTING_Topology restrict_topology)
1671 int num_connections;
1672 int unblacklisted_connections;
1674 GNUNET_assert (pg->notify_connection != NULL);
1679 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1681 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1682 _("Creating clique topology\n"));
1684 num_connections = create_clique (pg, &add_allowed_connections);
1686 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1688 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1689 _("Creating small world (ring) topology\n"));
1691 num_connections = create_small_world_ring (pg, &add_allowed_connections);
1693 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1695 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1696 _("Creating small world (2d-torus) topology\n"));
1698 num_connections = create_small_world (pg, &add_allowed_connections);
1700 case GNUNET_TESTING_TOPOLOGY_RING:
1702 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1703 _("Creating ring topology\n"));
1705 num_connections = create_ring (pg, &add_allowed_connections);
1707 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1709 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1710 _("Creating 2d torus topology\n"));
1712 num_connections = create_2d_torus (pg, &add_allowed_connections);
1714 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1716 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1717 _("Creating Erdos-Renyi topology\n"));
1719 num_connections = create_erdos_renyi (pg, &add_allowed_connections);
1721 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1723 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1724 _("Creating InterNAT topology\n"));
1726 num_connections = create_nated_internet (pg, &add_allowed_connections);
1728 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1731 _("Creating Scale Free topology\n"));
1733 num_connections = create_scale_free (pg, &add_allowed_connections);
1735 case GNUNET_TESTING_TOPOLOGY_NONE:
1736 num_connections = 0;
1739 num_connections = 0;
1742 if (num_connections < 1)
1743 return GNUNET_SYSERR;
1745 if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1747 ret = create_and_copy_friend_files(pg);
1750 if (ret != GNUNET_OK)
1753 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1754 _("Failed during friend file copying!\n"));
1756 return GNUNET_SYSERR;
1761 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1762 _("Friend files created/copied successfully!\n"));
1767 * Use the create clique method to initially set all connections
1770 create_clique (pg, &blacklist_connections);
1771 unblacklisted_connections = 0;
1773 * Un-blacklist connections as per the topology specified
1775 switch (restrict_topology)
1777 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1779 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1780 _("Blacklisting all but clique topology\n"));
1782 unblacklisted_connections = create_clique (pg, &unblacklist_connections);
1784 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1786 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1787 _("Blacklisting all but small world (ring) topology\n"));
1789 unblacklisted_connections = create_small_world_ring (pg, &unblacklist_connections);
1791 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1793 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1794 _("Blacklisting all but small world (2d-torus) topology\n"));
1796 unblacklisted_connections = create_small_world (pg, &unblacklist_connections);
1798 case GNUNET_TESTING_TOPOLOGY_RING:
1800 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1801 _("Blacklisting all but ring topology\n"));
1803 unblacklisted_connections = create_ring (pg, &unblacklist_connections);
1805 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1808 _("Blacklisting all but 2d torus topology\n"));
1810 unblacklisted_connections = create_2d_torus (pg, &unblacklist_connections);
1812 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1814 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1815 _("Blacklisting all but Erdos-Renyi topology\n"));
1817 unblacklisted_connections = create_erdos_renyi (pg, &unblacklist_connections);
1819 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1821 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1822 _("Blacklisting all but InterNAT topology\n"));
1824 unblacklisted_connections = create_nated_internet (pg, &unblacklist_connections);
1826 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1828 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1829 _("Blacklisting all but Scale Free topology\n"));
1831 unblacklisted_connections = create_scale_free (pg, &unblacklist_connections);
1833 case GNUNET_TESTING_TOPOLOGY_NONE:
1839 if (unblacklisted_connections > 0)
1841 ret = create_and_copy_blacklist_files(pg);
1842 if (ret != GNUNET_OK)
1845 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1846 _("Failed during blacklist file copying!\n"));
1848 return GNUNET_SYSERR;
1853 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1854 _("Blacklist files created/copied successfully!\n"));
1860 return num_connections;
1863 struct RandomContext
1868 struct GNUNET_TESTING_PeerGroup *pg;
1871 * uid of the first peer
1876 * Peer data for first peer.
1878 struct PeerData *first;
1881 * Random percentage to use
1886 struct MinimumContext
1891 struct GNUNET_TESTING_PeerGroup *pg;
1894 * uid of the first peer
1899 * Peer data for first peer.
1901 struct PeerData *first;
1904 * Number of conns per peer
1906 unsigned int num_to_add;
1909 * Permuted array of all possible connections. Only add the Nth
1910 * peer if it's in the Nth position.
1912 unsigned int *pg_array;
1915 * What number is the current element we are iterating over?
1917 unsigned int current;
1921 * Iterator for choosing random peers to connect.
1923 * @param cls closure, a RandomContext
1924 * @param key the key the second Daemon was stored under
1925 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1927 * @return GNUNET_YES to continue iteration
1930 random_connect_iterator (void *cls,
1931 const GNUNET_HashCode * key,
1934 struct RandomContext *random_ctx = cls;
1935 double random_number;
1936 uint32_t second_pos;
1937 GNUNET_HashCode first_hash;
1938 random_number = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
1939 (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
1940 if (random_number < random_ctx->percentage)
1942 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(random_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1944 /* Now we have considered this particular connection, remove it from the second peer so it's not double counted */
1945 uid_from_hash(key, &second_pos);
1946 hash_from_uid(random_ctx->first_uid, &first_hash);
1947 GNUNET_assert(random_ctx->pg->total > second_pos);
1948 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(random_ctx->pg->peers[second_pos].connect_peers, &first_hash, random_ctx->first->daemon));
1954 * Iterator for adding at least X peers to a peers connection set.
1956 * @param cls closure, MinimumContext
1957 * @param key the key the second Daemon was stored under
1958 * @param value the GNUNET_TESTING_Daemon that the first is to connect to
1960 * @return GNUNET_YES to continue iteration
1963 minimum_connect_iterator (void *cls,
1964 const GNUNET_HashCode * key,
1967 struct MinimumContext *min_ctx = cls;
1968 uint32_t second_pos;
1969 GNUNET_HashCode first_hash;
1972 if (GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set) < min_ctx->num_to_add)
1974 for (i = 0; i < min_ctx->num_to_add; i++)
1976 if (min_ctx->pg_array[i] == min_ctx->current)
1978 fprintf(stderr, "Adding another peer, hashmap size %u, minimum %u\n", GNUNET_CONTAINER_multihashmap_size(min_ctx->first->connect_peers_working_set), min_ctx->num_to_add);
1979 GNUNET_assert(GNUNET_OK == GNUNET_CONTAINER_multihashmap_put(min_ctx->first->connect_peers_working_set, key, value, GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1980 uid_from_hash(key, &second_pos);
1981 hash_from_uid(min_ctx->first_uid, &first_hash);
1982 GNUNET_assert(min_ctx->pg->total > second_pos);
1983 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));
1984 /* Now we have added this particular connection, remove it from the second peer's map so it's not double counted */
1985 GNUNET_assert(GNUNET_YES == GNUNET_CONTAINER_multihashmap_remove(min_ctx->pg->peers[second_pos].connect_peers, &first_hash, min_ctx->first->daemon));
1992 return GNUNET_NO; /* We can stop iterating, we have enough peers! */
1997 * From the set of connections possible, choose percentage percent of connections
1998 * to actually connect.
2000 * @param pg the peergroup we are dealing with
2001 * @param percentage what percent of total connections to make
2004 choose_random_connections(struct GNUNET_TESTING_PeerGroup *pg, double percentage)
2006 struct RandomContext random_ctx;
2009 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2011 random_ctx.first_uid = pg_iter;
2012 random_ctx.first = &pg->peers[pg_iter];
2013 random_ctx.percentage = percentage;
2014 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2015 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &random_connect_iterator, &random_ctx);
2016 /* Now remove the old connections */
2017 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2018 /* And replace with the random set */
2019 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2024 * From the set of connections possible, choose at least num connections per
2027 * @param pg the peergroup we are dealing with
2028 * @param num how many connections at least should each peer have (if possible)?
2031 choose_minimum(struct GNUNET_TESTING_PeerGroup *pg, unsigned int num)
2033 struct MinimumContext minimum_ctx;
2036 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2038 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(num);
2041 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2043 minimum_ctx.first_uid = pg_iter;
2044 minimum_ctx.pg_array = GNUNET_CRYPTO_random_permute(GNUNET_CRYPTO_QUALITY_WEAK, GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2045 minimum_ctx.first = &pg->peers[pg_iter];
2046 minimum_ctx.pg = pg;
2047 minimum_ctx.num_to_add = num;
2048 minimum_ctx.current = 0;
2049 pg->peers[pg_iter].connect_peers_working_set = GNUNET_CONTAINER_multihashmap_create(pg->total);
2050 GNUNET_CONTAINER_multihashmap_iterate(pg->peers[pg_iter].connect_peers, &minimum_connect_iterator, &minimum_ctx);
2053 for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
2055 /* Remove the "old" connections */
2056 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[pg_iter].connect_peers);
2057 /* And replace with the working set */
2058 pg->peers[pg_iter].connect_peers = pg->peers[pg_iter].connect_peers_working_set;
2059 fprintf(stderr, "Finished! Hashmap size %u\n", GNUNET_CONTAINER_multihashmap_size(pg->peers[pg_iter].connect_peers));
2066 * @param pg the peer group struct representing the running peers
2067 * @param topology which topology to connect the peers in
2068 * @param options options for connecting the topology
2069 * @param option_modifier modifier for options that take a parameter
2071 * There are many ways to connect peers that are supported by this function.
2072 * To connect peers in the same topology that was created via the
2073 * GNUNET_TESTING_create_topology, the topology variable must be set to
2074 * GNUNET_TESTING_TOPOLOGY_NONE. If the topology variable is specified,
2075 * a new instance of that topology will be generated and attempted to be
2076 * connected. This could result in some connections being impossible,
2077 * because some topologies are non-deterministic.
2081 GNUNET_TESTING_connect_topology (struct GNUNET_TESTING_PeerGroup *pg,
2082 enum GNUNET_TESTING_Topology topology,
2083 enum GNUNET_TESTING_TopologyOption options,
2084 double option_modifier)
2086 int num_connections;
2090 case GNUNET_TESTING_TOPOLOGY_CLIQUE:
2092 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2093 _("Creating clique topology\n"));
2095 num_connections = create_clique (pg, &add_actual_connections);
2097 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
2099 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2100 _("Creating small world (ring) topology\n"));
2102 num_connections = create_small_world_ring (pg, &add_actual_connections);
2104 case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
2106 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2107 _("Creating small world (2d-torus) topology\n"));
2109 num_connections = create_small_world (pg, &add_actual_connections);
2111 case GNUNET_TESTING_TOPOLOGY_RING:
2113 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2114 _("Creating ring topology\n"));
2116 num_connections = create_ring (pg, &add_actual_connections);
2118 case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
2120 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2121 _("Creating 2d torus topology\n"));
2123 num_connections = create_2d_torus (pg, &add_actual_connections);
2125 case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
2127 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2128 _("Creating Erdos-Renyi topology\n"));
2130 num_connections = create_erdos_renyi (pg, &add_actual_connections);
2132 case GNUNET_TESTING_TOPOLOGY_INTERNAT:
2134 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2135 _("Creating InterNAT topology\n"));
2137 num_connections = create_nated_internet (pg, &add_actual_connections);
2139 case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
2141 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
2142 _("Creating Scale Free topology\n"));
2144 num_connections = create_scale_free (pg, &add_actual_connections);
2146 case GNUNET_TESTING_TOPOLOGY_NONE:
2147 num_connections = copy_allowed_topology(pg);
2150 GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "Unknown topology specification, can't connect peers!\n");
2151 return GNUNET_SYSERR;
2156 case GNUNET_TESTING_TOPOLOGY_OPTION_RANDOM: /* Create a random subset of total connections based on parameter */
2157 choose_random_connections(pg, option_modifier);
2159 case GNUNET_TESTING_TOPOLOGY_OPTION_MINIMUM: /* Create at least X connections per peer (if possible!) */
2160 choose_minimum(pg, (unsigned int)option_modifier);
2162 case GNUNET_TESTING_TOPOLOGY_OPTION_DFS: /* Choose a random starting point, randomly walk graph, try to get each peer X connections */
2163 //choose_dfs(pg, (int)option_modifier);
2165 case GNUNET_TESTING_TOPOLOGY_OPTION_NONE:
2167 case GNUNET_TESTING_TOPOLOGY_OPTION_ALL:
2173 return connect_topology(pg);
2177 * Function which continues a peer group starting up
2178 * after successfully generating hostkeys for each peer.
2180 * @param pg the peer group to continue starting
2184 GNUNET_TESTING_daemons_continue_startup(struct GNUNET_TESTING_PeerGroup *pg)
2188 for (i = 0; i < pg->total; i++)
2190 GNUNET_TESTING_daemon_continue_startup(pg->peers[i].daemon);
2195 * Start count gnunetd processes with the same set of transports and
2196 * applications. The port numbers (any option called "PORT") will be
2197 * adjusted to ensure that no two peers running on the same system
2198 * have the same port(s) in their respective configurations.
2200 * @param sched scheduler to use
2201 * @param cfg configuration template to use
2202 * @param total number of daemons to start
2203 * @param hostkey_callback function to call on each peers hostkey generation
2204 * if NULL, peers will be started by this call, if non-null,
2205 * GNUNET_TESTING_daemons_continue_startup must be called after
2206 * successful hostkey generation
2207 * @param hostkey_cls closure for hostkey callback
2208 * @param cb function to call on each daemon that was started
2209 * @param cb_cls closure for cb
2210 * @param connect_callback function to call each time two hosts are connected
2211 * @param connect_callback_cls closure for connect_callback
2212 * @param hostnames space-separated list of hostnames to use; can be NULL (to run
2213 * everything on localhost).
2214 * @return NULL on error, otherwise handle to control peer group
2216 struct GNUNET_TESTING_PeerGroup *
2217 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
2218 const struct GNUNET_CONFIGURATION_Handle *cfg,
2220 GNUNET_TESTING_NotifyHostkeyCreated hostkey_callback,
2222 GNUNET_TESTING_NotifyDaemonRunning cb,
2224 GNUNET_TESTING_NotifyConnection
2225 connect_callback, void *connect_callback_cls,
2226 const char *hostnames)
2228 struct GNUNET_TESTING_PeerGroup *pg;
2232 const char *hostname;
2233 char *baseservicehome;
2234 char *newservicehome;
2236 struct GNUNET_CONFIGURATION_Handle *pcfg;
2238 unsigned int hostcnt;
2247 pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
2251 pg->cb_cls = cb_cls;
2252 pg->notify_connection = connect_callback;
2253 pg->notify_connection_cls = connect_callback_cls;
2255 pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
2256 if (NULL != hostnames)
2259 /* skip leading spaces */
2260 while ((0 != *hostnames) && (isspace (*hostnames)))
2263 while ('\0' != *rpos)
2265 if (isspace (*rpos))
2269 pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
2271 start = GNUNET_strdup (hostnames);
2273 while ('\0' != *pos)
2278 if (strlen (start) > 0)
2280 pg->hosts[off].minport = LOW_PORT;
2281 pg->hosts[off++].hostname = start;
2287 if (strlen (start) > 0)
2289 pg->hosts[off].minport = LOW_PORT;
2290 pg->hosts[off++].hostname = start;
2294 GNUNET_free (start);
2295 GNUNET_free (pg->hosts);
2299 minport = 0; /* make gcc happy */
2306 for (off = 0; off < total; off++)
2310 hostname = pg->hosts[off % hostcnt].hostname;
2311 pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
2316 pcfg = make_config (cfg, &minport, hostname);
2321 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2323 ("Could not create configuration for peer number %u on `%s'!\n"),
2324 off, hostname == NULL ? "localhost" : hostname);
2329 GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
2332 GNUNET_asprintf (&newservicehome,
2333 "%s/%d/", baseservicehome, off);
2334 GNUNET_free (baseservicehome);
2338 tmpdir = getenv ("TMPDIR");
2339 tmpdir = tmpdir ? tmpdir : "/tmp";
2340 GNUNET_asprintf (&newservicehome,
2343 "gnunet-testing-test-test", off);
2345 GNUNET_CONFIGURATION_set_value_string (pcfg,
2347 "SERVICEHOME", newservicehome);
2348 GNUNET_free (newservicehome);
2349 pg->peers[off].cfg = pcfg;
2350 pg->peers[off].allowed_peers = GNUNET_CONTAINER_multihashmap_create(total);
2351 pg->peers[off].connect_peers = GNUNET_CONTAINER_multihashmap_create(total);
2352 pg->peers[off].blacklisted_peers = GNUNET_CONTAINER_multihashmap_create(total);
2353 pg->peers[off].pg = pg;
2354 pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
2360 if (NULL == pg->peers[off].daemon)
2361 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2362 _("Could not start peer number %u!\n"), off);
2369 * Get a daemon by number, so callers don't have to do nasty
2370 * offsetting operation.
2372 struct GNUNET_TESTING_Daemon *
2373 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
2375 if (position < pg->total)
2376 return pg->peers[position].daemon;
2382 * Prototype of a function that will be called when a
2383 * particular operation was completed the testing library.
2385 * @param cls closure
2386 * @param emsg NULL on success
2388 void restart_callback (void *cls,
2389 const struct GNUNET_PeerIdentity *id,
2390 const struct GNUNET_CONFIGURATION_Handle *cfg,
2391 struct GNUNET_TESTING_Daemon *d,
2394 struct RestartContext *restart_context = cls;
2398 restart_context->peers_restarted++;
2402 restart_context->peers_restart_failed++;
2405 if (restart_context->peers_restarted == restart_context->peer_group->total)
2407 restart_context->callback(restart_context->callback_cls, NULL);
2408 GNUNET_free(restart_context);
2410 else if (restart_context->peers_restart_failed + restart_context->peers_restarted == restart_context->peer_group->total)
2412 restart_context->callback(restart_context->callback_cls, "Failed to restart peers!");
2413 GNUNET_free(restart_context);
2419 * Restart all peers in the given group.
2421 * @param pg the handle to the peer group
2422 * @param timeout how long to wait on failure
2423 * @param callback function to call on completion (or failure)
2424 * @param callback_cls closure for the callback function
2427 GNUNET_TESTING_daemons_restart (struct GNUNET_TESTING_PeerGroup *pg, GNUNET_TESTING_NotifyCompletion callback, void *callback_cls)
2429 struct RestartContext *restart_context;
2434 restart_context = GNUNET_malloc(sizeof(struct RestartContext));
2435 restart_context->peer_group = pg;
2436 restart_context->peers_restarted = 0;
2437 restart_context->callback = callback;
2438 restart_context->callback_cls = callback_cls;
2440 for (off = 0; off < pg->total; off++)
2442 GNUNET_TESTING_daemon_restart (pg->peers[off].daemon, &restart_callback, restart_context);
2448 * Shutdown all peers started in the given group.
2450 * @param pg handle to the peer group
2453 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
2457 for (off = 0; off < pg->total; off++)
2459 /* FIXME: should we wait for our
2460 continuations to be called here? This
2461 would require us to take a continuation
2464 if (NULL != pg->peers[off].daemon)
2465 GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL, GNUNET_YES);
2466 if (NULL != pg->peers[off].cfg)
2467 GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
2469 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].allowed_peers);
2470 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].connect_peers);
2471 GNUNET_CONTAINER_multihashmap_destroy(pg->peers[off].blacklisted_peers);
2474 GNUNET_free (pg->peers);
2475 if (NULL != pg->hosts)
2477 GNUNET_free (pg->hosts[0].hostname);
2478 GNUNET_free (pg->hosts);
2484 /* end of testing_group.c */