- indent
[oweals/gnunet.git] / src / testbed / testbed_api_topology.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2012 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19  */
20
21 /**
22  * @file testbed/testbed_api_topology.c
23  * @brief topology-generation functions
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
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"
32
33 /**
34  * Generic loggins shorthand
35  */
36 #define LOG(kind,...)                                           \
37   GNUNET_log_from (kind, "testbed-api-topology", __VA_ARGS__)
38
39
40 /**
41  * Context information for topology operations
42  */
43 struct TopologyContext;
44
45
46 /**
47  * Representation of an overlay link
48  */
49 struct OverlayLink
50 {
51
52   /**
53    * An operation corresponding to this link
54    */
55   struct GNUNET_TESTBED_Operation *op;
56
57   /**
58    * The topology context this link is a part of
59    */
60   struct TopologyContext *tc;
61
62   /**
63    * position of peer A's handle in peers array
64    */
65   uint32_t A;
66
67   /**
68    * position of peer B's handle in peers array
69    */
70   uint32_t B;
71
72 };
73
74
75 /**
76  * Context information for topology operations
77  */
78 struct TopologyContext
79 {
80   /**
81    * The array of peers
82    */
83   struct GNUNET_TESTBED_Peer **peers;
84
85   /**
86    * An array of links; this array is of size link_array_size
87    */
88   struct OverlayLink *link_array;
89
90   /**
91    * The operation closure
92    */
93   void *op_cls;
94
95   /**
96    * The number of peers
97    */
98   unsigned int num_peers;
99
100   /**
101    * The size of the link array
102    */
103   unsigned int link_array_size;
104
105   /**
106    * should the automatic retry be disabled
107    */
108   int disable_retry;
109
110 };
111
112
113 /**
114  * A array of names representing topologies. Should be in sync with enum
115  * GNUNET_TESTBED_TopologyOption
116  */
117 const char *topology_strings[] = {
118
119     /**
120      * A clique (everyone connected to everyone else).  No options. If there are N
121      * peers this topology results in (N * (N -1)) connections.
122      */
123   "CLIQUE",
124
125     /**
126      * Small-world network (2d torus plus random links).  Followed
127      * by the number of random links to add (unsigned int).
128      */
129   "SMALL_WORLD",
130
131     /**
132      * Small-world network (ring plus random links).  Followed
133      * by the number of random links to add (unsigned int).
134      */
135   "SMALL_WORLD_RING",
136
137     /**
138      * Ring topology.  No options.
139      */
140   "RING",
141
142     /**
143      * 2-d torus.  No options.
144      */
145   "2D_TORUS",
146
147     /**
148      * Random graph.  Followed by the number of random links to be established
149      * (unsigned int)
150      */
151   "RANDOM",                     // GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI
152
153     /**
154      * Certain percentage of peers are unable to communicate directly
155      * replicating NAT conditions.  Followed by the fraction of
156      * NAT'ed peers (float).
157      */
158   "INTERNAT",
159
160     /**
161      * Scale free topology. No options.
162      */
163   "SCALE_FREE",
164
165     /**
166      * Straight line topology.  No options.
167      */
168   "LINE",
169
170     /**
171      * Read a topology from a given file.  Followed by the name of the file (const char *).
172      */
173   "FROM_FILE",
174
175     /**
176      * All peers are disconnected.  No options.
177      */
178   "NONE",
179
180     /**
181      * End of strings
182      */
183   NULL
184 };
185
186
187 /**
188  * Callback to be called when an overlay_link operation complete
189  *
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.
194  */
195 static void
196 overlay_link_completed (void *cls, struct GNUNET_TESTBED_Operation *op,
197                         const char *emsg)
198 {
199   struct OverlayLink *link = cls;
200   struct TopologyContext *tc;
201
202   GNUNET_assert (op == link->op);
203   GNUNET_TESTBED_operation_done (op);
204   link->op = NULL;
205   tc = link->tc;
206   if ((NULL != emsg) && (GNUNET_NO == tc->disable_retry))
207   {
208     LOG (GNUNET_ERROR_TYPE_WARNING,
209          "Error while establishing a link: %s -- Retrying\n", emsg);
210     link->op =
211         GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
212                                         link, tc->peers[link->A],
213                                         tc->peers[link->B]);
214     return;
215   }
216 }
217
218
219
220 /**
221  * Function called when a overlay connect operation is ready
222  *
223  * @param cls the Topology context
224  */
225 static void
226 opstart_overlay_configure_topology (void *cls)
227 {
228   struct TopologyContext *tc = cls;
229   unsigned int p;
230
231   for (p = 0; p < tc->link_array_size; p++)
232   {
233     tc->link_array[p].op =
234         GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
235                                         &tc->link_array[p],
236                                         tc->peers[tc->link_array[p].A],
237                                         tc->peers[tc->link_array[p].B]);
238   }
239 }
240
241
242 /**
243  * Callback which will be called when overlay connect operation is released
244  *
245  * @param cls the Topology context
246  */
247 static void
248 oprelease_overlay_configure_topology (void *cls)
249 {
250   struct TopologyContext *tc = cls;
251   unsigned int p;
252
253   if (NULL != tc->link_array)
254   {
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);
259   }
260   GNUNET_free (tc);
261 }
262
263
264 /**
265  * Populates the OverlayLink structure.
266  *
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
271  * @return
272  */
273 static void
274 make_link (struct OverlayLink *link, uint32_t A, uint32_t B,
275            struct TopologyContext *tc)
276 {
277   GNUNET_assert (A != B);
278   LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %u to %u\n", B, A);
279   link->A = A;
280   link->B = B;
281   link->op = NULL;
282   link->tc = tc;
283 }
284
285
286 /**
287  * Generates line topology
288  *
289  * @param tc the topology context
290  */
291 static void
292 gen_topo_line (struct TopologyContext *tc)
293 {
294   unsigned int cnt;
295
296   tc->link_array_size = tc->num_peers - 1;
297   tc->link_array =
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);
301 }
302
303
304 /**
305  * Generates ring topology
306  *
307  * @param tc the topology context
308  */
309 static void
310 gen_topo_ring (struct TopologyContext *tc)
311 {
312   gen_topo_line (tc);
313   tc->link_array_size++;
314   tc->link_array =
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,
318              tc);
319 }
320
321
322 /**
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)
326  *
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
331  */
332 unsigned int
333 GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows,
334                                    unsigned int **rows_len)
335 {
336   double sq;
337   unsigned int sq_floor;
338   unsigned int _rows;
339   unsigned int *_rows_len;
340   unsigned int x;
341   unsigned int y;
342   unsigned int _num_peers;
343   unsigned int cnt;
344
345   sq = sqrt (num_peers);
346   sq = floor (sq);
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;
354   x = 0;
355   y = 0;
356   while (_num_peers < num_peers)
357   {
358     if (x < y)
359       _rows_len[_rows - 1] = ++x;
360     else
361       _rows_len[y++]++;
362     _num_peers++;
363   }
364   cnt += (x < 2) ? x : 2 * x;
365   cnt += (y < 2) ? y : 2 * y;
366   if (0 == _rows_len[_rows - 1])
367     _rows--;
368   if (NULL != rows)
369     *rows = _rows;
370   if (NULL != rows_len)
371     *rows_len = _rows_len;
372   else
373     GNUNET_free (_rows_len);
374   return cnt;
375 }
376
377
378 /**
379  * Generates ring topology
380  *
381  * @param tc the topology context
382  */
383 static void
384 gen_topo_2dtorus (struct TopologyContext *tc)
385 {
386   unsigned int rows;
387   unsigned int *rows_len;
388   unsigned int x;
389   unsigned int y;
390   unsigned int cnt;
391   unsigned int offset;
392
393   tc->link_array_size =
394       GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows, &rows_len);
395   tc->link_array =
396       GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
397   cnt = 0;
398   offset = 0;
399   for (y = 0; y < rows; y++)
400   {
401     for (x = 0; x < rows_len[y] - 1; x++)
402     {
403       make_link (&tc->link_array[cnt], offset + x, offset + x + 1, tc);
404       cnt++;
405     }
406     if (0 == x)
407       break;
408     make_link (&tc->link_array[cnt], offset + x, offset, tc);
409     cnt++;
410     offset += rows_len[y];
411   }
412   for (x = 0; x < rows_len[0]; x++)
413   {
414     offset = 0;
415     for (y = 0; y < rows - 1; y++)
416     {
417       if (x == rows_len[y + 1])
418         break;
419       GNUNET_assert (x < rows_len[y + 1]);
420       make_link (&tc->link_array[cnt], offset + x, offset + rows_len[y] + x,
421                  tc);
422       offset += rows_len[y];
423       cnt++;
424     }
425     if (0 == offset)
426       break;
427     make_link (&tc->link_array[cnt], offset + x, x, tc);
428     cnt++;
429   }
430   GNUNET_assert (cnt == tc->link_array_size);
431   GNUNET_free (rows_len);
432 }
433
434
435 /**
436  * Generates ring topology
437  *
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
442  */
443 static void
444 gen_topo_random (struct TopologyContext *tc, unsigned int links, int append)
445 {
446   unsigned int cnt;
447   unsigned int index;
448   uint32_t A_rand;
449   uint32_t B_rand;
450
451   if (GNUNET_YES == append)
452   {
453     GNUNET_assert ((0 < tc->link_array_size) && (NULL != tc->link_array));
454     index = tc->link_array_size;
455     tc->link_array_size += links;
456     tc->link_array =
457         GNUNET_realloc (tc->link_array,
458                         sizeof (struct OverlayLink) * tc->link_array_size);
459   }
460   else
461   {
462     GNUNET_assert ((0 == tc->link_array_size) && (NULL == tc->link_array));
463     index = 0;
464     tc->link_array_size = links;
465     tc->link_array =
466         GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
467   }
468   for (cnt = 0; cnt < links; cnt++)
469   {
470     do
471     {
472       A_rand =
473           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
474       B_rand =
475           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
476     }
477     while (A_rand == B_rand);
478     make_link (&tc->link_array[index + cnt], A_rand, B_rand, tc);
479   }
480 }
481
482
483 /**
484  * Generates scale free network. Its construction is described in:
485  *
486  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
487  *
488  * @param tc the topology context
489  */
490 static void
491 gen_scale_free (struct TopologyContext *tc)
492 {
493   double random;
494   double probability;
495   unsigned int cnt;
496   unsigned int previous_connections;
497   unsigned int i;
498   uint16_t *popularity;
499
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++)
507   {
508     previous_connections = tc->link_array_size;
509     for (i = 0; i < cnt; i++)
510     {
511       probability = ((double) popularity[i]) / ((double) previous_connections);
512       random =
513           ((double)
514            GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
515                                      UINT64_MAX)) / ((double) UINT64_MAX);
516       if (random < probability)
517       {
518         tc->link_array_size++;
519         tc->link_array =
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);
524         popularity[cnt]++;
525       }
526     }
527   }
528   GNUNET_free (popularity);
529 }
530
531
532 /**
533  * Generates topology from the given file
534  *
535  * @param tc the topology context
536  * @param filename the filename of the file containing topology data
537  */
538 static void
539 gen_topo_from_file (struct TopologyContext *tc, const char *filename)
540 {
541   char *data;
542   char *end;
543   char *buf;
544   uint64_t fs;
545   uint64_t offset;
546   unsigned long int peer_id;
547   unsigned long int other_peer_id;
548   enum ParseState
549   {
550
551     /**
552      * We read the peer index
553      */
554     PEER_INDEX,
555
556     /**
557      * We read the other peer indices
558      */
559     OTHER_PEER_INDEX,
560
561   } state;
562   int status;
563
564   status = GNUNET_SYSERR;
565   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
566   {
567     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s not found\n"), filename);
568     return;
569   }
570   if (GNUNET_OK !=
571       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
572   {
573     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s has no data\n"),
574          filename);
575     return;
576   }
577   data = GNUNET_malloc (fs);
578   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
579   {
580     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s cannot be read\n"),
581          filename);
582     goto _exit;
583   }
584
585   offset = 0;
586   peer_id = 0;
587   state = PEER_INDEX;
588   buf = data;
589   while (offset < fs)
590   {
591     if (0 != isspace (data[offset]))
592     {
593       offset++;
594       continue;
595     }
596     switch (state)
597     {
598     case PEER_INDEX:
599       buf = strchr (&data[offset], ':');
600       if (NULL == buf)
601       {
602         LOG (GNUNET_ERROR_TYPE_ERROR,
603              _("Failed to read peer index from toology file: %s"), filename);
604         goto _exit;
605       }
606       *buf = '\0';
607       errno = 0;
608       peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
609       if (0 != errno)
610       {
611         LOG (GNUNET_ERROR_TYPE_ERROR,
612              _("Value in given topology file: %s out of range\n"), filename);
613         goto _exit;
614       }
615       if (&data[offset] == end)
616       {
617         LOG (GNUNET_ERROR_TYPE_ERROR,
618              _("Failed to read peer index from topology file: %s"), filename);
619         goto _exit;
620       }
621       if (tc->num_peers <= peer_id)
622       {
623         LOG (GNUNET_ERROR_TYPE_ERROR,
624              _("Topology file needs more peers than given ones\n"), filename);
625         goto _exit;
626       }
627       state = OTHER_PEER_INDEX;
628       offset += ((unsigned int) (buf - &data[offset])) + 1;
629       break;
630     case OTHER_PEER_INDEX:
631       errno = 0;
632       other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
633       if (0 != errno)
634       {
635         LOG (GNUNET_ERROR_TYPE_ERROR,
636              _("Value in given topology file: %s out of range\n"), filename);
637         goto _exit;
638       }
639       if (&data[offset] == end)
640       {
641         LOG (GNUNET_ERROR_TYPE_ERROR,
642              _("Failed to read peer index from topology file: %s"), filename);
643         goto _exit;
644       }
645       if (tc->num_peers <= other_peer_id)
646       {
647         LOG (GNUNET_ERROR_TYPE_ERROR,
648              _("Topology file needs more peers than given ones\n"), filename);
649         goto _exit;
650       }
651       if (peer_id != other_peer_id)
652       {
653         tc->link_array_size++;
654         tc->link_array =
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,
659                    other_peer_id, tc);
660       }
661       else
662         LOG (GNUNET_ERROR_TYPE_WARNING,
663              _("Ignoring to connect peer %u to peer %u\n"), peer_id,
664              other_peer_id);
665       while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs))
666         offset++;
667       if ('\n' == data[offset])
668         state = PEER_INDEX;
669       else if ('|' == data[offset])
670       {
671         state = OTHER_PEER_INDEX;
672         offset++;
673       }
674       break;
675     }
676   }
677   status = GNUNET_OK;
678
679 _exit:
680   GNUNET_free (data);
681   if (GNUNET_OK != status)
682   {
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;
687   }
688 }
689
690
691 /**
692  * Configure overall network topology to have a particular shape.
693  *
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
701  */
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
706                                                **peers,
707                                                enum
708                                                GNUNET_TESTBED_TopologyOption
709                                                topo, va_list ap)
710 {
711   GNUNET_break (0);
712   return NULL;
713 }
714
715
716 /**
717  * Configure overall network topology to have a particular shape.
718  *
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
726  */
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
732                                             topo, ...)
733 {
734   GNUNET_break (0);
735   return NULL;
736 }
737
738
739 /**
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.
743  *
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
754  */
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
759                                               **peers,
760                                               unsigned int *max_connections,
761                                               enum GNUNET_TESTBED_TopologyOption
762                                               topo, va_list va)
763 {
764   struct TopologyContext *tc;
765   struct GNUNET_TESTBED_Operation *op;
766   struct GNUNET_TESTBED_Controller *c;
767   enum GNUNET_TESTBED_TopologyOption secondary_option;
768   unsigned int cnt;
769
770   if (num_peers < 2)
771     return NULL;
772   c = peers[0]->controller;
773   tc = GNUNET_malloc (sizeof (struct TopologyContext));
774   tc->peers = peers;
775   tc->num_peers = num_peers;
776   tc->op_cls = op_cls;
777   switch (topo)
778   {
779   case GNUNET_TESTBED_TOPOLOGY_LINE:
780     gen_topo_line (tc);
781     break;
782   case GNUNET_TESTBED_TOPOLOGY_RING:
783     gen_topo_ring (tc);
784     break;
785   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
786     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO);
787
788     break;
789   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
790     gen_topo_ring (tc);
791     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
792
793     break;
794   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
795     tc->link_array_size = num_peers * (num_peers - 1);
796     tc->link_array =
797         GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
798     {
799       unsigned int offset;
800
801       offset = 0;
802       for (cnt = 0; cnt < num_peers; cnt++)
803       {
804         unsigned int neighbour;
805
806         for (neighbour = 0; neighbour < num_peers; neighbour++)
807         {
808           if (neighbour == cnt)
809             continue;
810           tc->link_array[offset].A = cnt;
811           tc->link_array[offset].B = neighbour;
812           tc->link_array[offset].tc = tc;
813           offset++;
814         }
815       }
816     }
817     break;
818   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
819     gen_topo_2dtorus (tc);
820     break;
821   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
822     gen_topo_2dtorus (tc);
823     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
824
825     break;
826   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
827     gen_scale_free (tc);
828     break;
829   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
830   {
831     const char *filename;
832
833     filename = va_arg (va, const char *);
834
835     GNUNET_assert (NULL != filename);
836     gen_topo_from_file (tc, filename);
837   }
838     break;
839   default:
840     GNUNET_break (0);
841     GNUNET_free (tc);
842     return NULL;
843   }
844   do
845   {
846     secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
847
848     switch (secondary_option)
849     {
850     case GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY:
851       tc->disable_retry = GNUNET_YES;
852       break;
853     case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
854       break;
855     default:
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);
859       GNUNET_free (tc);
860       return NULL;
861     }
862   }
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;
874   return op;
875 }
876
877
878 /**
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.
882  *
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
893  */
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
899                                            topo, ...)
900 {
901   struct GNUNET_TESTBED_Operation *op;
902   va_list vargs;
903
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,
908                                                      vargs);
909   va_end (vargs);
910   return op;
911 }
912
913
914 /**
915  * Get a topology from a string input.
916  *
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
922  */
923 int
924 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
925                               const char *topology_string)
926 {
927   unsigned int cnt;
928
929   for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
930   {
931     if (0 == strcasecmp (topology_string, topology_strings[cnt]))
932     {
933       if (NULL != topology)
934         *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
935       return GNUNET_YES;
936     }
937   }
938   return GNUNET_NO;
939 }
940
941
942 /**
943  * Returns the string corresponding to the given topology
944  *
945  * @param topology the topology
946  * @return the string (freshly allocated) of given topology; NULL if topology cannot be
947  *           expressed as a string
948  */
949 char *
950 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
951 {
952   if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
953     return NULL;
954   return GNUNET_strdup (topology_strings[topology]);
955 }
956
957 /* end of testbed_api_topology.c */