- fixes
[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 /**
189  * Callback to be called when an overlay_link operation complete
190  *
191  * @param cls element of the link_op array which points to the corresponding operation
192  * @param op the operation that has been finished
193  * @param emsg error message in case the operation has failed; will be NULL if
194  *          operation has executed successfully.
195  */
196 static void 
197 overlay_link_completed (void *cls,
198                         struct GNUNET_TESTBED_Operation *op, 
199                         const char *emsg)
200 {
201   struct OverlayLink *link = cls;
202   struct TopologyContext *tc;
203
204   GNUNET_assert (op == link->op);
205   GNUNET_TESTBED_operation_done (op);
206   link->op = NULL;
207   tc = link->tc;
208   if ((NULL != emsg) && (GNUNET_NO == tc->disable_retry))
209   {
210     LOG (GNUNET_ERROR_TYPE_WARNING,
211          "Error while establishing a link: %s -- Retrying\n", emsg);
212     link->op =
213         GNUNET_TESTBED_overlay_connect (tc->op_cls,
214                                         &overlay_link_completed,
215                                         link,
216                                         tc->peers[link->A],
217                                         tc->peers[link->B]);
218     return;
219   }
220 }
221
222
223
224 /**
225  * Function called when a overlay connect operation is ready
226  *
227  * @param cls the Topology context
228  */
229 static void
230 opstart_overlay_configure_topology (void *cls)
231 {
232   struct TopologyContext *tc = cls;
233   unsigned int p;
234   
235   for (p = 0; p < tc->link_array_size; p++)
236   {
237     tc->link_array[p].op =
238         GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
239                                         &tc->link_array[p],
240                                         tc->peers[tc->link_array[p].A],
241                                         tc->peers[tc->link_array[p].B]);                                                  
242   }
243 }
244
245
246 /**
247  * Callback which will be called when overlay connect operation is released
248  *
249  * @param cls the Topology context
250  */
251 static void
252 oprelease_overlay_configure_topology (void *cls)
253 {
254   struct TopologyContext *tc = cls;
255   unsigned int p;
256   
257   if (NULL != tc->link_array)
258   {
259     for (p = 0; p < tc->link_array_size; p++)
260       if (NULL != tc->link_array[p].op)
261         GNUNET_TESTBED_operation_done (tc->link_array[p].op);
262     GNUNET_free (tc->link_array);
263   }
264   GNUNET_free (tc);
265 }
266
267
268 /**
269  * Populates the OverlayLink structure
270  *
271  * @param link the OverlayLink
272  * @param A the peer A
273  * @param B the peer B
274  * @param tc the TopologyContext
275  * @return 
276  */
277 static void
278 make_link (struct OverlayLink *link,
279            uint32_t A, 
280            uint32_t B,
281            struct TopologyContext *tc)
282 {
283   link->A = A;
284   link->B = B;
285   link->op = NULL;
286   link->tc = tc;
287 }
288
289
290 /**
291  * Generates line topology
292  *
293  * @param tc the topology context
294  */
295 static void
296 gen_topo_line (struct TopologyContext *tc)
297 {
298   unsigned int cnt;
299
300   tc->link_array_size = tc->num_peers - 1;
301   tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink) *
302                                   tc->link_array_size);
303   for (cnt=0; cnt < (tc->num_peers - 1); cnt++)
304     make_link (&tc->link_array[cnt], cnt, cnt + 1, tc);
305 }
306
307
308 /**
309  * Generates ring topology
310  *
311  * @param tc the topology context
312  */
313 static void
314 gen_topo_ring (struct TopologyContext *tc)
315 {
316   gen_topo_line (tc);
317   tc->link_array_size++;
318   tc->link_array = GNUNET_realloc (tc->link_array,
319                                    sizeof (struct OverlayLink) *
320                                    tc->link_array_size);
321   make_link (&tc->link_array[tc->link_array_size - 1],
322              tc->num_peers - 1,
323              0,
324              tc);
325 }
326
327
328 /**
329  * Returns the number of links that are required to generate a 2d torus for the
330  * given number of peers. Also returns the arrangment (number of rows and the
331  * length of each row)
332  *
333  * @param num_peers number of peers
334  * @param rows number of rows in the 2d torus. Can be NULL
335  * @param rows_len the length of each row. This array will be allocated
336  *          fresh. The caller should free it. Can be NULL
337  */
338 unsigned int
339 GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers,
340                                    unsigned int *rows,
341                                    unsigned int **rows_len)
342 {
343   double sq;
344   unsigned int sq_floor;
345   unsigned int _rows;
346   unsigned int *_rows_len;
347   unsigned int x;
348   unsigned int y;
349   unsigned int _num_peers;
350   unsigned int cnt;
351   
352   sq = sqrt (num_peers);
353   sq = floor (sq);
354   sq_floor = (unsigned int) sq;
355   _rows = (sq_floor + 1);
356   _rows_len = GNUNET_malloc (sizeof (unsigned int) * _rows);  
357   for (y = 0; y < _rows - 1; y++)
358     _rows_len[y] = sq_floor;
359   _num_peers = sq_floor * sq_floor;
360   cnt = 2 * _num_peers;
361   x = 0;
362   y = 0;
363   while (_num_peers < num_peers)
364   {
365     if (x < y)
366       _rows_len[_rows - 1] = ++x;
367     else
368       _rows_len[y++]++;
369     _num_peers++;
370   }
371   cnt += (x < 2) ? x : 2 * x;
372   cnt += (y < 2) ? y : 2 * y;
373   if (0 == _rows_len[_rows - 1])
374     _rows--;
375   if (NULL != rows)
376     *rows = _rows;
377   if (NULL != rows_len)
378     *rows_len = _rows_len;
379   else
380     GNUNET_free (_rows_len);
381   return cnt;
382 }
383
384
385 /**
386  * Generates ring topology
387  *
388  * @param tc the topology context
389  */
390 static void
391 gen_topo_2dtorus (struct TopologyContext *tc)
392 {
393   unsigned int rows;
394   unsigned int *rows_len;
395   unsigned int x;
396   unsigned int y;
397   unsigned int cnt;
398   unsigned int offset;
399
400   tc->link_array_size = GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows,
401                                                            &rows_len);
402   tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink) *
403                                   tc->link_array_size);
404   cnt = 0;
405   offset = 0;
406   for (y = 0; y < rows; y++)
407   {
408     for (x = 0; x < rows_len[y] - 1; x++)
409     {
410       make_link (&tc->link_array[cnt],
411                  offset + x,
412                  offset + x + 1,
413                  tc);
414       cnt++;
415     }
416     if (0 == x)
417       break;
418     make_link (&tc->link_array[cnt],
419                offset + x,
420                offset,
421                tc);
422     cnt++;
423     offset += rows_len[y];
424   }
425   for (x = 0; x < rows_len[0]; x++)
426   {
427     offset = 0;
428     for (y = 0; y < rows - 1; y++)
429     {
430       if (x == rows_len[y+1])
431         break;
432       GNUNET_assert (x < rows_len[y+1]);
433       make_link (&tc->link_array[cnt],
434                  offset + x,
435                  offset + rows_len[y] + x,
436                  tc);
437       offset += rows_len[y];
438       cnt++;
439     }
440     if (0 == offset)
441       break;
442     make_link (&tc->link_array[cnt],
443                offset + x,
444                x,
445                tc);
446     cnt++;
447   }
448   GNUNET_assert (cnt == tc->link_array_size);
449   GNUNET_free (rows_len);
450 }
451
452
453 /**
454  * Generates ring topology
455  *
456  * @param tc the topology context
457  * @param links the number of random links to establish
458  * @param append GNUNET_YES to add links to existing link array; GNUNET_NO to
459  *          create a new link array
460  */
461 static void
462 gen_topo_random (struct TopologyContext *tc, unsigned int links, int append)
463 {
464   unsigned int cnt;
465   unsigned int index;
466   uint32_t A_rand;
467   uint32_t B_rand;
468   
469   if (GNUNET_YES == append)
470   {
471     GNUNET_assert ((0 < tc->link_array_size) && (NULL != tc->link_array));
472     index = tc->link_array_size;   
473     tc->link_array_size += links;
474     tc->link_array = GNUNET_realloc (tc->link_array,
475                                    sizeof (struct OverlayLink) *
476                                      tc->link_array_size);
477   }
478   else
479   {
480     GNUNET_assert ((0 == tc->link_array_size) && (NULL == tc->link_array));
481     index = 0;   
482     tc->link_array_size = links;
483     tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink) *
484                                     tc->link_array_size);
485   }
486   for (cnt = 0; cnt < links; cnt++)
487   {
488     do {
489       A_rand = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
490                                          tc->num_peers);
491       B_rand = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
492                                          tc->num_peers);
493     } while (A_rand == B_rand);
494     make_link (&tc->link_array[index + cnt], A_rand,  B_rand, tc);
495   }
496 }
497
498
499 /**
500  * Generates scale free network. Its construction is described in:
501  *
502  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
503  *
504  * @param tc the topology context
505  */
506 static void
507 gen_scale_free (struct TopologyContext *tc)
508 {
509   double random;
510   double probability;
511   unsigned int cnt;
512   unsigned int previous_connections;
513   unsigned int i;
514   uint16_t *popularity;
515
516   popularity = GNUNET_malloc (sizeof (uint16_t) * tc->num_peers);
517   /* Initially connect peer 1 to peer 0 */
518   tc->link_array_size = 1;
519   tc->link_array =  GNUNET_malloc (sizeof (struct OverlayLink));
520   make_link (&tc->link_array[0], 0, 1, tc);
521   popularity[0]++;  /* increase popularity of 0 as 1 connected to it*/ 
522   for (cnt = 1; cnt < tc->num_peers; cnt++)
523   {
524     previous_connections = tc->link_array_size;
525     for (i = 0; i < cnt; i++)
526     {
527       probability = ((double) popularity[i]) / ((double) previous_connections);
528       random = ((double)
529            GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
530                                      UINT64_MAX)) / ((double) UINT64_MAX);
531       if (random < probability)
532       {
533         tc->link_array_size++;
534         tc->link_array = GNUNET_realloc (tc->link_array,
535                                          (sizeof (struct OverlayLink) *
536                                           tc->link_array_size));
537         make_link (&tc->link_array[tc->link_array_size - 1], cnt, i, tc);
538         popularity[cnt]++;
539       }
540     }
541   }
542   GNUNET_free (popularity);
543 }
544
545
546 /**
547  * Generates topology from the given file
548  *
549  * @param tc the topology context
550  * @param filename the filename of the file containing topology data
551  */
552 static void
553 gen_topo_from_file (struct TopologyContext *tc, const char *filename)
554 {
555   char *data;
556   char *end;
557   char *buf;
558   uint64_t fs;
559   uint64_t offset;
560   unsigned long int peer_id;
561   unsigned long int other_peer_id;
562   enum ParseState {
563     
564     /**
565      * We read the peer index
566      */
567     PEER_INDEX,
568
569     /**
570      * We read the other peer indices
571      */
572     OTHER_PEER_INDEX,
573
574   } state;
575   int status;
576
577   status = GNUNET_SYSERR;
578   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
579   {
580     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s not found\n"), filename);
581     return;
582   }
583   if (GNUNET_OK !=
584       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
585   {
586     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s has no data\n"), filename);
587     return;
588   }
589   data = GNUNET_malloc (fs);
590   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
591   {
592     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s cannot be read\n"),
593          filename);
594     goto _exit;
595   }
596
597   offset = 0;
598   peer_id = 0;
599   state = PEER_INDEX;
600   buf = data;
601   while (offset < fs)
602   {
603     if (0 != isspace (data[offset]))
604     {
605       offset++;
606       continue;
607     }
608     switch (state)
609     {
610     case PEER_INDEX:
611       buf = strchr (&data[offset], ':');
612       if (NULL == buf)
613       {
614         LOG (GNUNET_ERROR_TYPE_ERROR,
615              _("Failed to read peer index from toology file: %s"), filename);
616         goto _exit;
617       }
618       *buf = '\0';
619       errno = 0;
620       peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
621       if (0 != errno)
622       {
623         LOG (GNUNET_ERROR_TYPE_ERROR,
624              _("Value in given topology file: %s out of range\n"), filename);
625         goto _exit;
626       }
627       if (&data[offset] == end)
628       {
629         LOG (GNUNET_ERROR_TYPE_ERROR,
630              _("Failed to read peer index from topology file: %s"), filename);
631         goto _exit;
632       }
633       if (tc->num_peers <= peer_id)
634       {
635         LOG (GNUNET_ERROR_TYPE_ERROR,
636              _("Topology file need more peers than the given ones\n"),
637              filename);
638         goto _exit;
639       }
640       state = OTHER_PEER_INDEX;
641       offset += ((unsigned int) (buf - &data[offset])) + 1;
642       break;
643     case OTHER_PEER_INDEX:
644       errno = 0;
645       other_peer_id = (unsigned int) strtoul (&data[offset],  &end, 10);
646       if (0 != errno)
647       {
648         LOG (GNUNET_ERROR_TYPE_ERROR,
649              _("Value in given topology file: %s out of range\n"), filename);
650         goto _exit;
651       }
652       if (&data[offset] == end)
653       {
654         LOG (GNUNET_ERROR_TYPE_ERROR,
655              _("Failed to read peer index from topology file: %s"), filename);
656         goto _exit;
657       }
658       if (tc->num_peers <= other_peer_id)
659       {
660         LOG (GNUNET_ERROR_TYPE_ERROR,
661              _("Topology file need more peers than the given ones\n"),
662              filename);
663         goto _exit;
664       }
665       tc->link_array_size++;
666       tc->link_array = GNUNET_realloc (tc->link_array, 
667                                        sizeof (struct OverlayLink) * 
668                                        tc->link_array_size);
669       offset += end - &data[offset];
670       make_link (&tc->link_array[tc->link_array_size - 1], peer_id,
671                  other_peer_id, tc);
672       while (('\n' != data[offset]) && ('|' != data[offset])
673              && (offset < fs))
674         offset++;
675       if ('\n' == data[offset])
676         state = PEER_INDEX;
677       else if ('|' == data[offset])
678       {
679         state = OTHER_PEER_INDEX;
680         offset++;
681       }
682       break;
683     }
684   }
685   status = GNUNET_OK;
686   
687  _exit:
688   GNUNET_free (data);
689   if (GNUNET_OK != status)
690   {
691     LOG (GNUNET_ERROR_TYPE_WARNING,
692          "Removing and link data read from the file\n");
693     tc->link_array_size = 0;
694     GNUNET_free_non_null (tc->link_array);
695     tc->link_array = NULL;
696   }
697 }
698
699
700 /**
701  * Configure overall network topology to have a particular shape.
702  *
703  * @param op_cls closure argument to give with the operation event
704  * @param num_peers number of peers in 'peers'
705  * @param peers array of 'num_peers' with the peers to configure
706  * @param topo desired underlay topology to use
707  * @param ap topology-specific options
708  * @return handle to the operation, NULL if configuring the topology
709  *         is not allowed at this time
710  */
711 struct GNUNET_TESTBED_Operation *
712 GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls,
713                                                unsigned int num_peers,
714                                                struct GNUNET_TESTBED_Peer
715                                                **peers,
716                                                enum
717                                                GNUNET_TESTBED_TopologyOption
718                                                topo, va_list ap)
719 {
720   GNUNET_break (0);
721   return NULL;
722 }
723
724
725 /**
726  * Configure overall network topology to have a particular shape.
727  *
728  * @param op_cls closure argument to give with the operation event
729  * @param num_peers number of peers in 'peers'
730  * @param peers array of 'num_peers' with the peers to configure
731  * @param topo desired underlay topology to use
732  * @param ... topology-specific options
733  * @return handle to the operation, NULL if configuring the topology
734  *         is not allowed at this time
735  */
736 struct GNUNET_TESTBED_Operation *
737 GNUNET_TESTBED_underlay_configure_topology (void *op_cls,
738                                             unsigned int num_peers,
739                                             struct GNUNET_TESTBED_Peer **peers,
740                                             enum GNUNET_TESTBED_TopologyOption
741                                             topo, ...)
742 {
743   GNUNET_break (0);
744   return NULL;
745 }
746
747
748 /**
749  * All peers must have been started before calling this function.
750  * This function then connects the given peers in the P2P overlay
751  * using the given topology.
752  *
753  * @param op_cls closure argument to give with the operation event
754  * @param num_peers number of peers in 'peers'
755  * @param peers array of 'num_peers' with the peers to configure
756  * @param max_connections the maximums number of overlay connections that will
757  *          be made to achieve the given topology
758  * @param topo desired underlay topology to use
759  * @param va topology-specific options
760  * @return handle to the operation, NULL if connecting these
761  *         peers is fundamentally not possible at this time (peers
762  *         not running or underlay disallows) or if num_peers is less than 2
763  */
764 struct GNUNET_TESTBED_Operation *
765 GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls,
766                                               unsigned int num_peers,
767                                               struct GNUNET_TESTBED_Peer **peers,
768                                               unsigned int *max_connections,
769                                               enum GNUNET_TESTBED_TopologyOption
770                                               topo, va_list va)
771 {
772   struct TopologyContext *tc;
773   struct GNUNET_TESTBED_Operation *op;
774   struct GNUNET_TESTBED_Controller *c;
775   enum GNUNET_TESTBED_TopologyOption secondary_option;
776   unsigned int cnt;
777
778   if (num_peers < 2)
779     return NULL;
780   c = peers[0]->controller;
781   tc = GNUNET_malloc (sizeof (struct TopologyContext));
782   tc->peers = peers;
783   tc->num_peers = num_peers;
784   tc->op_cls = op_cls;
785   switch (topo)
786   {
787   case GNUNET_TESTBED_TOPOLOGY_LINE:
788     gen_topo_line (tc);
789     break;
790   case GNUNET_TESTBED_TOPOLOGY_RING:
791     gen_topo_ring (tc);
792     break;
793   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
794     gen_topo_random (tc,
795                      va_arg (va, unsigned int),
796                      GNUNET_NO);
797     break;
798   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
799     gen_topo_ring (tc);
800     gen_topo_random (tc,
801                      va_arg (va, unsigned int),
802                      GNUNET_YES);
803     break;
804   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
805     tc->link_array_size = num_peers * (num_peers - 1);
806     tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink) *
807                                     tc->link_array_size);
808     {
809       unsigned int offset;
810       
811       offset = 0;
812       for (cnt=0; cnt < num_peers; cnt++)
813       {
814         unsigned int neighbour;
815         
816         for (neighbour=0; neighbour < num_peers; neighbour++)
817         {
818           if (neighbour == cnt)
819             continue;
820           tc->link_array[offset].A = cnt;
821           tc->link_array[offset].B = neighbour;
822           tc->link_array[offset].tc = tc;
823           offset++;
824         }
825       }
826     }
827     break;
828   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
829     gen_topo_2dtorus (tc);
830     break;
831   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
832     gen_topo_2dtorus (tc);
833     gen_topo_random (tc,
834                      va_arg (va, unsigned int),
835                      GNUNET_YES);
836     break;
837   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
838     gen_scale_free (tc);
839     break;
840   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
841     {
842       const char *filename;
843       
844       filename = va_arg (va, const char *);
845       GNUNET_assert (NULL != filename);
846       gen_topo_from_file (tc, filename);
847     }
848     break;
849   default:
850     GNUNET_break (0);
851     GNUNET_free (tc);
852     return NULL;
853   }
854   do
855   {
856     secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
857     switch (secondary_option)
858     {
859     case GNUNET_TESTBED_TOPOLOGY_DISABLE_AUTO_RETRY:
860       tc->disable_retry = GNUNET_YES;
861       break;
862     case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
863       break;
864     default:
865       GNUNET_break (0);         /* Should not use any other option apart from
866                                    the ones handled here */
867       GNUNET_free_non_null (tc->link_array);
868       GNUNET_free (tc);
869       return NULL;
870     }
871   } while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option);
872   op = GNUNET_TESTBED_operation_create_ (tc,
873                                          &opstart_overlay_configure_topology,
874                                          &oprelease_overlay_configure_topology);
875   GNUNET_TESTBED_operation_queue_insert_
876       (c->opq_parallel_topology_config_operations, op);
877   GNUNET_TESTBED_operation_begin_wait_ (op);
878   LOG (GNUNET_ERROR_TYPE_DEBUG,
879        "Generated %u connections\n", tc->link_array_size);
880   if (NULL != max_connections)
881     *max_connections = tc->link_array_size;
882   return op;
883 }
884
885
886 /**
887  * All peers must have been started before calling this function.
888  * This function then connects the given peers in the P2P overlay
889  * using the given topology.
890  *
891  * @param op_cls closure argument to give with the operation event
892  * @param num_peers number of peers in 'peers'
893  * @param peers array of 'num_peers' with the peers to configure
894  * @param max_connections the maximums number of overlay connections that will
895  *          be made to achieve the given topology
896  * @param topo desired underlay topology to use
897  * @param ... topology-specific options
898  * @return handle to the operation, NULL if connecting these
899  *         peers is fundamentally not possible at this time (peers
900  *         not running or underlay disallows) or if num_peers is less than 2
901  */
902 struct GNUNET_TESTBED_Operation *
903 GNUNET_TESTBED_overlay_configure_topology (void *op_cls, unsigned int num_peers,
904                                            struct GNUNET_TESTBED_Peer **peers,
905                                            unsigned int *max_connections,
906                                            enum GNUNET_TESTBED_TopologyOption
907                                            topo, ...)
908 {
909   struct GNUNET_TESTBED_Operation *op;
910   va_list vargs;
911
912   GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END);
913   va_start (vargs, topo);
914   op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers,
915                                                      max_connections,
916                                                      topo, vargs);
917   va_end (vargs);
918   return op;
919 }
920
921
922 /**
923  * Get a topology from a string input.
924  *
925  * @param topology where to write the retrieved topology
926  * @param topology_string The string to attempt to
927  *        get a configuration value from
928  * @return GNUNET_YES if topology string matched a
929  *         known topology, GNUNET_NO if not
930  */
931 int
932 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
933                               const char *topology_string)
934 {  
935   unsigned int cnt;
936
937   for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
938   {
939     if (0 == strcasecmp (topology_string, topology_strings[cnt]))
940     {
941       if (NULL != topology)
942         *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
943       return GNUNET_YES;
944     }
945   }
946   return GNUNET_NO;
947 }
948
949
950 /**
951  * Returns the string corresponding to the given topology
952  *
953  * @param topology the topology
954  * @return the string (freshly allocated) of given topology; NULL if topology cannot be
955  *           expressed as a string
956  */
957 char *
958 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
959 {
960   if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
961     return NULL;
962   return GNUNET_strdup (topology_strings[topology]);
963 }
964
965 /* end of testbed_api_topology.c */