2 This file is part of GNUnet
3 (C) 2008--2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testbed/testbed_api_topology.c
23 * @brief topology-generation functions
24 * @author Christian Grothoff
27 #include "gnunet_testbed_service.h"
28 #include "testbed_api.h"
29 #include "testbed_api_peers.h"
30 #include "testbed_api_operations.h"
31 #include "testbed_api_topology.h"
34 * Generic loggins shorthand
36 #define LOG(kind,...) \
37 GNUNET_log_from (kind, "testbed-api-topology", __VA_ARGS__)
41 * Context information for topology operations
43 struct TopologyContext;
47 * Representation of an overlay link
53 * An operation corresponding to this link
55 struct GNUNET_TESTBED_Operation *op;
58 * The topology context this link is a part of
60 struct TopologyContext *tc;
63 * position of peer A's handle in peers array
68 * position of peer B's handle in peers array
76 * Context information for topology operations
78 struct TopologyContext
83 struct GNUNET_TESTBED_Peer **peers;
86 * An array of links; this array is of size link_array_size
88 struct OverlayLink *link_array;
91 * The operation closure
98 unsigned int num_peers;
101 * The size of the link array
103 unsigned int link_array_size;
106 * should the automatic retry be disabled
114 * A array of names representing topologies. Should be in sync with enum
115 * GNUNET_TESTBED_TopologyOption
117 const char *topology_strings[] = {
120 * A clique (everyone connected to everyone else). No options. If there are N
121 * peers this topology results in (N * (N -1)) connections.
126 * Small-world network (2d torus plus random links). Followed
127 * by the number of random links to add (unsigned int).
132 * Small-world network (ring plus random links). Followed
133 * by the number of random links to add (unsigned int).
138 * Ring topology. No options.
143 * 2-d torus. No options.
148 * Random graph. Followed by the number of random links to be established
151 "RANDOM", // GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI
154 * Certain percentage of peers are unable to communicate directly
155 * replicating NAT conditions. Followed by the fraction of
156 * NAT'ed peers (float).
161 * Scale free topology. No options.
166 * Straight line topology. No options.
171 * Read a topology from a given file. Followed by the name of the file (const char *).
176 * All peers are disconnected. No options.
188 * Callback to be called when an overlay_link operation complete
190 * @param cls element of the link_op array which points to the corresponding operation
191 * @param op the operation that has been finished
192 * @param emsg error message in case the operation has failed; will be NULL if
193 * operation has executed successfully.
196 overlay_link_completed (void *cls, struct GNUNET_TESTBED_Operation *op,
199 struct OverlayLink *link = cls;
200 struct TopologyContext *tc;
202 GNUNET_assert (op == link->op);
203 GNUNET_TESTBED_operation_done (op);
206 if ((NULL != emsg) && (GNUNET_NO == tc->disable_retry))
208 LOG (GNUNET_ERROR_TYPE_WARNING,
209 "Error while establishing a link: %s -- Retrying\n", emsg);
211 GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
212 link, tc->peers[link->A],
221 * Function called when a overlay connect operation is ready
223 * @param cls the Topology context
226 opstart_overlay_configure_topology (void *cls)
228 struct TopologyContext *tc = cls;
231 for (p = 0; p < tc->link_array_size; p++)
233 tc->link_array[p].op =
234 GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
236 tc->peers[tc->link_array[p].A],
237 tc->peers[tc->link_array[p].B]);
243 * Callback which will be called when overlay connect operation is released
245 * @param cls the Topology context
248 oprelease_overlay_configure_topology (void *cls)
250 struct TopologyContext *tc = cls;
253 if (NULL != tc->link_array)
255 for (p = 0; p < tc->link_array_size; p++)
256 if (NULL != tc->link_array[p].op)
257 GNUNET_TESTBED_operation_done (tc->link_array[p].op);
258 GNUNET_free (tc->link_array);
265 * Populates the OverlayLink structure.
267 * @param link the OverlayLink
268 * @param A the peer A. Should be different from B
269 * @param B the peer B. Should be different from A
270 * @param tc the TopologyContext
274 make_link (struct OverlayLink *link, uint32_t A, uint32_t B,
275 struct TopologyContext *tc)
277 GNUNET_assert (A != B);
278 LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %u to %u\n", B, A);
287 * Generates line topology
289 * @param tc the topology context
292 gen_topo_line (struct TopologyContext *tc)
296 tc->link_array_size = tc->num_peers - 1;
298 GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
299 for (cnt = 0; cnt < (tc->num_peers - 1); cnt++)
300 make_link (&tc->link_array[cnt], cnt, cnt + 1, tc);
305 * Generates ring topology
307 * @param tc the topology context
310 gen_topo_ring (struct TopologyContext *tc)
313 tc->link_array_size++;
315 GNUNET_realloc (tc->link_array,
316 sizeof (struct OverlayLink) * tc->link_array_size);
317 make_link (&tc->link_array[tc->link_array_size - 1], tc->num_peers - 1, 0,
323 * Returns the number of links that are required to generate a 2d torus for the
324 * given number of peers. Also returns the arrangment (number of rows and the
325 * length of each row)
327 * @param num_peers number of peers
328 * @param rows number of rows in the 2d torus. Can be NULL
329 * @param rows_len the length of each row. This array will be allocated
330 * fresh. The caller should free it. Can be NULL
333 GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows,
334 unsigned int **rows_len)
337 unsigned int sq_floor;
339 unsigned int *_rows_len;
342 unsigned int _num_peers;
345 sq = sqrt (num_peers);
347 sq_floor = (unsigned int) sq;
348 _rows = (sq_floor + 1);
349 _rows_len = GNUNET_malloc (sizeof (unsigned int) * _rows);
350 for (y = 0; y < _rows - 1; y++)
351 _rows_len[y] = sq_floor;
352 _num_peers = sq_floor * sq_floor;
353 cnt = 2 * _num_peers;
356 while (_num_peers < num_peers)
359 _rows_len[_rows - 1] = ++x;
364 cnt += (x < 2) ? x : 2 * x;
365 cnt += (y < 2) ? y : 2 * y;
366 if (0 == _rows_len[_rows - 1])
370 if (NULL != rows_len)
371 *rows_len = _rows_len;
373 GNUNET_free (_rows_len);
379 * Generates ring topology
381 * @param tc the topology context
384 gen_topo_2dtorus (struct TopologyContext *tc)
387 unsigned int *rows_len;
393 tc->link_array_size =
394 GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows, &rows_len);
396 GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
399 for (y = 0; y < rows; y++)
401 for (x = 0; x < rows_len[y] - 1; x++)
403 make_link (&tc->link_array[cnt], offset + x, offset + x + 1, tc);
408 make_link (&tc->link_array[cnt], offset + x, offset, tc);
410 offset += rows_len[y];
412 for (x = 0; x < rows_len[0]; x++)
415 for (y = 0; y < rows - 1; y++)
417 if (x == rows_len[y + 1])
419 GNUNET_assert (x < rows_len[y + 1]);
420 make_link (&tc->link_array[cnt], offset + x, offset + rows_len[y] + x,
422 offset += rows_len[y];
427 make_link (&tc->link_array[cnt], offset + x, x, tc);
430 GNUNET_assert (cnt == tc->link_array_size);
431 GNUNET_free (rows_len);
436 * Generates ring topology
438 * @param tc the topology context
439 * @param links the number of random links to establish
440 * @param append GNUNET_YES to add links to existing link array; GNUNET_NO to
441 * create a new link array
444 gen_topo_random (struct TopologyContext *tc, unsigned int links, int append)
451 if (GNUNET_YES == append)
453 GNUNET_assert ((0 < tc->link_array_size) && (NULL != tc->link_array));
454 index = tc->link_array_size;
455 tc->link_array_size += links;
457 GNUNET_realloc (tc->link_array,
458 sizeof (struct OverlayLink) * tc->link_array_size);
462 GNUNET_assert ((0 == tc->link_array_size) && (NULL == tc->link_array));
464 tc->link_array_size = links;
466 GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
468 for (cnt = 0; cnt < links; cnt++)
473 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
475 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
477 while (A_rand == B_rand);
478 make_link (&tc->link_array[index + cnt], A_rand, B_rand, tc);
484 * Generates scale free network. Its construction is described in:
486 * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
488 * @param tc the topology context
491 gen_scale_free (struct TopologyContext *tc)
496 unsigned int previous_connections;
498 uint16_t *popularity;
500 popularity = GNUNET_malloc (sizeof (uint16_t) * tc->num_peers);
501 /* Initially connect peer 1 to peer 0 */
502 tc->link_array_size = 1;
503 tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink));
504 make_link (&tc->link_array[0], 0, 1, tc);
505 popularity[0]++; /* increase popularity of 0 as 1 connected to it */
506 for (cnt = 1; cnt < tc->num_peers; cnt++)
508 previous_connections = tc->link_array_size;
509 for (i = 0; i < cnt; i++)
511 probability = ((double) popularity[i]) / ((double) previous_connections);
514 GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
515 UINT64_MAX)) / ((double) UINT64_MAX);
516 if (random < probability)
518 tc->link_array_size++;
520 GNUNET_realloc (tc->link_array,
521 (sizeof (struct OverlayLink) *
522 tc->link_array_size));
523 make_link (&tc->link_array[tc->link_array_size - 1], cnt, i, tc);
528 GNUNET_free (popularity);
533 * Generates topology from the given file
535 * @param tc the topology context
536 * @param filename the filename of the file containing topology data
539 gen_topo_from_file (struct TopologyContext *tc, const char *filename)
546 unsigned long int peer_id;
547 unsigned long int other_peer_id;
552 * We read the peer index
557 * We read the other peer indices
564 status = GNUNET_SYSERR;
565 if (GNUNET_YES != GNUNET_DISK_file_test (filename))
567 LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s not found\n"), filename);
571 GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
573 LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s has no data\n"),
577 data = GNUNET_malloc (fs);
578 if (fs != GNUNET_DISK_fn_read (filename, data, fs))
580 LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s cannot be read\n"),
591 if (0 != isspace (data[offset]))
599 buf = strchr (&data[offset], ':');
602 LOG (GNUNET_ERROR_TYPE_ERROR,
603 _("Failed to read peer index from toology file: %s"), filename);
608 peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
611 LOG (GNUNET_ERROR_TYPE_ERROR,
612 _("Value in given topology file: %s out of range\n"), filename);
615 if (&data[offset] == end)
617 LOG (GNUNET_ERROR_TYPE_ERROR,
618 _("Failed to read peer index from topology file: %s"), filename);
621 if (tc->num_peers <= peer_id)
623 LOG (GNUNET_ERROR_TYPE_ERROR,
624 _("Topology file needs more peers than given ones\n"), filename);
627 state = OTHER_PEER_INDEX;
628 offset += ((unsigned int) (buf - &data[offset])) + 1;
630 case OTHER_PEER_INDEX:
632 other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
635 LOG (GNUNET_ERROR_TYPE_ERROR,
636 _("Value in given topology file: %s out of range\n"), filename);
639 if (&data[offset] == end)
641 LOG (GNUNET_ERROR_TYPE_ERROR,
642 _("Failed to read peer index from topology file: %s"), filename);
645 if (tc->num_peers <= other_peer_id)
647 LOG (GNUNET_ERROR_TYPE_ERROR,
648 _("Topology file needs more peers than given ones\n"), filename);
651 if (peer_id != other_peer_id)
653 tc->link_array_size++;
655 GNUNET_realloc (tc->link_array,
656 sizeof (struct OverlayLink) * tc->link_array_size);
657 offset += end - &data[offset];
658 make_link (&tc->link_array[tc->link_array_size - 1], peer_id,
662 LOG (GNUNET_ERROR_TYPE_WARNING,
663 _("Ignoring to connect peer %u to peer %u\n"), peer_id,
665 while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs))
667 if ('\n' == data[offset])
669 else if ('|' == data[offset])
671 state = OTHER_PEER_INDEX;
681 if (GNUNET_OK != status)
683 LOG (GNUNET_ERROR_TYPE_WARNING, "Removing link data read from the file\n");
684 tc->link_array_size = 0;
685 GNUNET_free_non_null (tc->link_array);
686 tc->link_array = NULL;
692 * Configure overall network topology to have a particular shape.
694 * @param op_cls closure argument to give with the operation event
695 * @param num_peers number of peers in 'peers'
696 * @param peers array of 'num_peers' with the peers to configure
697 * @param topo desired underlay topology to use
698 * @param ap topology-specific options
699 * @return handle to the operation, NULL if configuring the topology
700 * is not allowed at this time
702 struct GNUNET_TESTBED_Operation *
703 GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls,
704 unsigned int num_peers,
705 struct GNUNET_TESTBED_Peer
708 GNUNET_TESTBED_TopologyOption
717 * Configure overall network topology to have a particular shape.
719 * @param op_cls closure argument to give with the operation event
720 * @param num_peers number of peers in 'peers'
721 * @param peers array of 'num_peers' with the peers to configure
722 * @param topo desired underlay topology to use
723 * @param ... topology-specific options
724 * @return handle to the operation, NULL if configuring the topology
725 * is not allowed at this time
727 struct GNUNET_TESTBED_Operation *
728 GNUNET_TESTBED_underlay_configure_topology (void *op_cls,
729 unsigned int num_peers,
730 struct GNUNET_TESTBED_Peer **peers,
731 enum GNUNET_TESTBED_TopologyOption
740 * All peers must have been started before calling this function.
741 * This function then connects the given peers in the P2P overlay
742 * using the given topology.
744 * @param op_cls closure argument to give with the operation event
745 * @param num_peers number of peers in 'peers'
746 * @param peers array of 'num_peers' with the peers to configure
747 * @param max_connections the maximums number of overlay connections that will
748 * be made to achieve the given topology
749 * @param topo desired underlay topology to use
750 * @param va topology-specific options
751 * @return handle to the operation, NULL if connecting these
752 * peers is fundamentally not possible at this time (peers
753 * not running or underlay disallows) or if num_peers is less than 2
755 struct GNUNET_TESTBED_Operation *
756 GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls,
757 unsigned int num_peers,
758 struct GNUNET_TESTBED_Peer
760 unsigned int *max_connections,
761 enum GNUNET_TESTBED_TopologyOption
764 struct TopologyContext *tc;
765 struct GNUNET_TESTBED_Operation *op;
766 struct GNUNET_TESTBED_Controller *c;
767 enum GNUNET_TESTBED_TopologyOption secondary_option;
772 c = peers[0]->controller;
773 tc = GNUNET_malloc (sizeof (struct TopologyContext));
775 tc->num_peers = num_peers;
779 case GNUNET_TESTBED_TOPOLOGY_LINE:
782 case GNUNET_TESTBED_TOPOLOGY_RING:
785 case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
786 gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO);
789 case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
791 gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
794 case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
795 tc->link_array_size = num_peers * (num_peers - 1);
797 GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
802 for (cnt = 0; cnt < num_peers; cnt++)
804 unsigned int neighbour;
806 for (neighbour = 0; neighbour < num_peers; neighbour++)
808 if (neighbour == cnt)
810 tc->link_array[offset].A = cnt;
811 tc->link_array[offset].B = neighbour;
812 tc->link_array[offset].tc = tc;
818 case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
819 gen_topo_2dtorus (tc);
821 case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
822 gen_topo_2dtorus (tc);
823 gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
826 case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
829 case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
831 const char *filename;
833 filename = va_arg (va, const char *);
835 GNUNET_assert (NULL != filename);
836 gen_topo_from_file (tc, filename);
846 secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
848 switch (secondary_option)
850 case GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY:
851 tc->disable_retry = GNUNET_YES;
853 case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
856 GNUNET_break (0); /* Should not use any other option apart from
857 * the ones handled here */
858 GNUNET_free_non_null (tc->link_array);
863 while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option);
864 op = GNUNET_TESTBED_operation_create_ (tc,
865 &opstart_overlay_configure_topology,
866 &oprelease_overlay_configure_topology);
867 GNUNET_TESTBED_operation_queue_insert_
868 (c->opq_parallel_topology_config_operations, op);
869 GNUNET_TESTBED_operation_begin_wait_ (op);
870 LOG (GNUNET_ERROR_TYPE_DEBUG, "Generated %u connections\n",
871 tc->link_array_size);
872 if (NULL != max_connections)
873 *max_connections = tc->link_array_size;
879 * All peers must have been started before calling this function.
880 * This function then connects the given peers in the P2P overlay
881 * using the given topology.
883 * @param op_cls closure argument to give with the operation event
884 * @param num_peers number of peers in 'peers'
885 * @param peers array of 'num_peers' with the peers to configure
886 * @param max_connections the maximums number of overlay connections that will
887 * be made to achieve the given topology
888 * @param topo desired underlay topology to use
889 * @param ... topology-specific options
890 * @return handle to the operation, NULL if connecting these
891 * peers is fundamentally not possible at this time (peers
892 * not running or underlay disallows) or if num_peers is less than 2
894 struct GNUNET_TESTBED_Operation *
895 GNUNET_TESTBED_overlay_configure_topology (void *op_cls, unsigned int num_peers,
896 struct GNUNET_TESTBED_Peer **peers,
897 unsigned int *max_connections,
898 enum GNUNET_TESTBED_TopologyOption
901 struct GNUNET_TESTBED_Operation *op;
904 GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END);
905 va_start (vargs, topo);
906 op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers,
907 max_connections, topo,
915 * Get a topology from a string input.
917 * @param topology where to write the retrieved topology
918 * @param topology_string The string to attempt to
919 * get a configuration value from
920 * @return GNUNET_YES if topology string matched a
921 * known topology, GNUNET_NO if not
924 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
925 const char *topology_string)
929 for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
931 if (0 == strcasecmp (topology_string, topology_strings[cnt]))
933 if (NULL != topology)
934 *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
943 * Returns the string corresponding to the given topology
945 * @param topology the topology
946 * @return the string (freshly allocated) of given topology; NULL if topology cannot be
947 * expressed as a string
950 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
952 if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
954 return GNUNET_strdup (topology_strings[topology]);
957 /* end of testbed_api_topology.c */