- remove dead assignments
[oweals/gnunet.git] / src / testbed / testbed_api_topology.c
1 /*
2       This file is part of GNUnet
3       (C) 2008--2013 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  * Default number of retires
42  */
43 #define DEFAULT_RETRY_CNT 3
44
45
46 /**
47  * Context information for topology operations
48  */
49 struct TopologyContext;
50
51
52 /**
53  * Representation of an overlay link
54  */
55 struct OverlayLink
56 {
57
58   /**
59    * An operation corresponding to this link
60    */
61   struct GNUNET_TESTBED_Operation *op;
62
63   /**
64    * The topology context this link is a part of
65    */
66   struct TopologyContext *tc;
67
68   /**
69    * position of peer A's handle in peers array
70    */
71   uint32_t A;
72
73   /**
74    * position of peer B's handle in peers array
75    */
76   uint32_t B;
77
78 };
79
80
81 struct RetryListEntry
82 {
83   /**
84    * the next pointer for the DLL
85    */
86   struct RetryListEntry *next;
87
88   /**
89    * the prev pointer for the DLL
90    */
91   struct RetryListEntry *prev;
92   
93   /**
94    * The link to be retired
95    */
96   struct OverlayLink *link;
97 };
98
99
100 /**
101  * Context information for topology operations
102  */
103 struct TopologyContext
104 {
105   /**
106    * The array of peers
107    */
108   struct GNUNET_TESTBED_Peer **peers;
109
110   /**
111    * An array of links; this array is of size link_array_size
112    */
113   struct OverlayLink *link_array;
114
115   /**
116    * The operation closure
117    */
118   void *op_cls;
119
120   /**
121    * topology generation completion callback
122    */
123   GNUNET_TESTBED_TopologyCompletionCallback comp_cb;
124
125   /**
126    * The closure for the above callback
127    */
128   void *comp_cb_cls;
129
130   /**
131    * DLL head for retry list
132    */
133   struct RetryListEntry *rl_head;
134
135   /**
136    * DLL tail for retry list
137    */
138   struct RetryListEntry *rl_tail;
139   
140   /**
141    * The number of peers
142    */
143   unsigned int num_peers;
144
145   /**
146    * The size of the link array
147    */
148   unsigned int link_array_size;
149
150   /**
151    * How many retries to do before we give up
152    */
153   unsigned int retry_cnt;
154
155   /**
156    * Number of links to try
157    */
158   unsigned int nlinks;
159
160   /**
161    * How many links have been completed
162    */
163   unsigned int ncompleted;
164
165   /**
166    * Total successfully established overlay connections
167    */
168   unsigned int nsuccess;
169
170   /**
171    * Total failed overlay connections
172    */
173   unsigned int nfailures;
174 };
175
176
177 /**
178  * A array of names representing topologies. Should be in sync with enum
179  * GNUNET_TESTBED_TopologyOption
180  */
181 const char *topology_strings[] = {
182
183     /**
184      * A clique (everyone connected to everyone else).  No options. If there are N
185      * peers this topology results in (N * (N -1)) connections.
186      */
187   "CLIQUE",
188
189     /**
190      * Small-world network (2d torus plus random links).  Followed
191      * by the number of random links to add (unsigned int).
192      */
193   "SMALL_WORLD",
194
195     /**
196      * Small-world network (ring plus random links).  Followed
197      * by the number of random links to add (unsigned int).
198      */
199   "SMALL_WORLD_RING",
200
201     /**
202      * Ring topology.  No options.
203      */
204   "RING",
205
206     /**
207      * 2-d torus.  No options.
208      */
209   "2D_TORUS",
210
211     /**
212      * Random graph.  Followed by the number of random links to be established
213      * (unsigned int)
214      */
215   "RANDOM",                     // GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI
216
217     /**
218      * Certain percentage of peers are unable to communicate directly
219      * replicating NAT conditions.  Followed by the fraction of
220      * NAT'ed peers (float).
221      */
222   "INTERNAT",
223
224     /**
225      * Scale free topology. No options.
226      */
227   "SCALE_FREE",
228
229     /**
230      * Straight line topology.  No options.
231      */
232   "LINE",
233
234     /**
235      * Read a topology from a given file.  Followed by the name of the file (const char *).
236      */
237   "FROM_FILE",
238
239     /**
240      * All peers are disconnected.  No options.
241      */
242   "NONE",
243
244     /**
245      * End of strings
246      */
247   NULL
248 };
249
250
251 /**
252  * Callback to be called when an overlay_link operation complete
253  *
254  * @param cls element of the link_op array which points to the corresponding operation
255  * @param op the operation that has been finished
256  * @param emsg error message in case the operation has failed; will be NULL if
257  *          operation has executed successfully.
258  */
259 static void
260 overlay_link_completed (void *cls, struct GNUNET_TESTBED_Operation *op,
261                         const char *emsg)
262 {
263   struct OverlayLink *link = cls;
264   struct TopologyContext *tc;
265   struct RetryListEntry *retry_entry;
266
267   GNUNET_assert (op == link->op);
268   GNUNET_TESTBED_operation_done (op);
269   link->op = NULL;
270   tc = link->tc;
271   if (NULL != emsg)
272   {
273     tc->nfailures++;
274     if (0 != tc->retry_cnt)
275     {
276       LOG (GNUNET_ERROR_TYPE_WARNING,
277            "Error while establishing a link: %s -- Retrying\n", emsg);
278       retry_entry = GNUNET_malloc (sizeof (struct RetryListEntry));
279       retry_entry->link = link;
280       GNUNET_CONTAINER_DLL_insert_tail (tc->rl_head, tc->rl_tail, retry_entry);
281     }
282   }
283   else
284     tc->nsuccess++;
285   tc->ncompleted++;
286   if (tc->ncompleted < tc->nlinks)
287     return;
288   if ((0 != tc->retry_cnt) && (NULL != tc->rl_head))
289   {
290     tc->retry_cnt--;
291     tc->ncompleted = 0;
292     tc->nlinks = 0;
293     while (NULL != (retry_entry = tc->rl_head))
294     {
295       link = retry_entry->link;      
296       link->op =
297           GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
298                                           link, tc->peers[link->A],
299                                           tc->peers[link->B]);
300       tc->nlinks++;
301       GNUNET_CONTAINER_DLL_remove (tc->rl_head, tc->rl_tail, retry_entry);
302       GNUNET_free (retry_entry);
303     }
304     return;
305   }
306   if (NULL != tc->comp_cb)
307   {
308     tc->comp_cb (tc->comp_cb_cls, tc->nsuccess, tc->nfailures);
309   }
310 }
311
312
313
314 /**
315  * Function called when a overlay connect operation is ready
316  *
317  * @param cls the Topology context
318  */
319 static void
320 opstart_overlay_configure_topology (void *cls)
321 {
322   struct TopologyContext *tc = cls;
323   unsigned int p;
324
325   tc->nlinks = tc->link_array_size;
326   for (p = 0; p < tc->link_array_size; p++)
327   {
328     tc->link_array[p].op =
329         GNUNET_TESTBED_overlay_connect (tc->op_cls, &overlay_link_completed,
330                                         &tc->link_array[p],
331                                         tc->peers[tc->link_array[p].A],
332                                         tc->peers[tc->link_array[p].B]);
333   }
334 }
335
336
337 /**
338  * Callback which will be called when overlay connect operation is released
339  *
340  * @param cls the Topology context
341  */
342 static void
343 oprelease_overlay_configure_topology (void *cls)
344 {
345   struct TopologyContext *tc = cls;
346   struct RetryListEntry *retry_entry;
347   unsigned int p;
348
349   while (NULL != (retry_entry = tc->rl_head))
350   {
351     GNUNET_CONTAINER_DLL_remove (tc->rl_head, tc->rl_tail, retry_entry);
352     GNUNET_free (retry_entry);
353   }
354   if (NULL != tc->link_array)
355   {
356     for (p = 0; p < tc->link_array_size; p++)
357       if (NULL != tc->link_array[p].op)
358         GNUNET_TESTBED_operation_done (tc->link_array[p].op);
359     GNUNET_free (tc->link_array);
360   }
361   GNUNET_free (tc);
362 }
363
364
365 /**
366  * Populates the OverlayLink structure.
367  *
368  * @param link the OverlayLink
369  * @param A the peer A. Should be different from B
370  * @param B the peer B. Should be different from A
371  * @param tc the TopologyContext
372  * @return
373  */
374 static void
375 make_link (struct OverlayLink *link, uint32_t A, uint32_t B,
376            struct TopologyContext *tc)
377 {
378   GNUNET_assert (A != B);
379   LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %u to %u\n", B, A);
380   link->A = A;
381   link->B = B;
382   link->op = NULL;
383   link->tc = tc;
384 }
385
386
387 /**
388  * Generates line topology
389  *
390  * @param tc the topology context
391  */
392 static void
393 gen_topo_line (struct TopologyContext *tc)
394 {
395   unsigned int cnt;
396
397   tc->link_array_size = tc->num_peers - 1;
398   tc->link_array =
399       GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
400   for (cnt = 0; cnt < (tc->num_peers - 1); cnt++)
401     make_link (&tc->link_array[cnt], cnt, cnt + 1, tc);
402 }
403
404
405 /**
406  * Generates ring topology
407  *
408  * @param tc the topology context
409  */
410 static void
411 gen_topo_ring (struct TopologyContext *tc)
412 {
413   gen_topo_line (tc);
414   tc->link_array_size++;
415   tc->link_array =
416       GNUNET_realloc (tc->link_array,
417                       sizeof (struct OverlayLink) * tc->link_array_size);
418   make_link (&tc->link_array[tc->link_array_size - 1], tc->num_peers - 1, 0,
419              tc);
420 }
421
422
423 /**
424  * Returns the number of links that are required to generate a 2d torus for the
425  * given number of peers. Also returns the arrangment (number of rows and the
426  * length of each row)
427  *
428  * @param num_peers number of peers
429  * @param rows number of rows in the 2d torus. Can be NULL
430  * @param rows_len the length of each row. This array will be allocated
431  *          fresh. The caller should free it. Can be NULL
432  * @return the number of links that are required to generate a 2d torus for the
433  *           given number of peers
434  */
435 unsigned int
436 GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows,
437                                    unsigned int **rows_len)
438 {
439   double sq;
440   unsigned int sq_floor;
441   unsigned int _rows;
442   unsigned int *_rows_len;
443   unsigned int x;
444   unsigned int y;
445   unsigned int _num_peers;
446   unsigned int cnt;
447
448   sq = sqrt (num_peers);
449   sq = floor (sq);
450   sq_floor = (unsigned int) sq;
451   _rows = (sq_floor + 1);
452   _rows_len = GNUNET_malloc (sizeof (unsigned int) * _rows);
453   for (y = 0; y < _rows - 1; y++)
454     _rows_len[y] = sq_floor;
455   _num_peers = sq_floor * sq_floor;
456   cnt = (_num_peers < 2) ? _num_peers : 2 * _num_peers;
457   x = 0;
458   y = 0;
459   while (_num_peers < num_peers)
460   {
461     if (x < y)
462       _rows_len[_rows - 1] = ++x;
463     else
464       _rows_len[y++]++;
465     _num_peers++;
466   }
467   cnt += (x < 2) ? x : 2 * x;
468   cnt += (y < 2) ? y : 2 * y;
469   if (0 == _rows_len[_rows - 1])
470     _rows--;
471   if (NULL != rows)
472     *rows = _rows;
473   if (NULL != rows_len)
474     *rows_len = _rows_len;
475   else
476     GNUNET_free (_rows_len);
477   return cnt;
478 }
479
480
481 /**
482  * Generates ring topology
483  *
484  * @param tc the topology context
485  */
486 static void
487 gen_topo_2dtorus (struct TopologyContext *tc)
488 {
489   unsigned int rows;
490   unsigned int *rows_len;
491   unsigned int x;
492   unsigned int y;
493   unsigned int cnt;
494   unsigned int offset;
495
496   tc->link_array_size =
497       GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows, &rows_len);
498   tc->link_array =
499       GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
500   cnt = 0;
501   offset = 0;
502   for (y = 0; y < rows; y++)
503   {
504     for (x = 0; x < rows_len[y] - 1; x++)
505     {
506       make_link (&tc->link_array[cnt], offset + x, offset + x + 1, tc);
507       cnt++;
508     }
509     if (0 == x)
510       break;
511     make_link (&tc->link_array[cnt], offset + x, offset, tc);
512     cnt++;
513     offset += rows_len[y];
514   }
515   for (x = 0; x < rows_len[0]; x++)
516   {
517     offset = 0;
518     for (y = 0; y < rows - 1; y++)
519     {
520       if (x >= rows_len[y + 1])
521         break;
522       GNUNET_assert (x < rows_len[y + 1]);
523       make_link (&tc->link_array[cnt], offset + x, offset + rows_len[y] + x,
524                  tc);
525       offset += rows_len[y];
526       cnt++;
527     }
528     if (0 == offset)
529       break;
530     make_link (&tc->link_array[cnt], offset + x, x, tc);
531     cnt++;
532   }
533   GNUNET_assert (cnt == tc->link_array_size);
534   GNUNET_free (rows_len);
535 }
536
537
538 /**
539  * Generates ring topology
540  *
541  * @param tc the topology context
542  * @param links the number of random links to establish
543  * @param append GNUNET_YES to add links to existing link array; GNUNET_NO to
544  *          create a new link array
545  */
546 static void
547 gen_topo_random (struct TopologyContext *tc, unsigned int links, int append)
548 {
549   unsigned int cnt;
550   unsigned int index;
551   uint32_t A_rand;
552   uint32_t B_rand;
553
554   if (GNUNET_YES == append)
555   {
556     GNUNET_assert ((0 < tc->link_array_size) && (NULL != tc->link_array));
557     index = tc->link_array_size;
558     tc->link_array_size += links;
559     tc->link_array =
560         GNUNET_realloc (tc->link_array,
561                         sizeof (struct OverlayLink) * tc->link_array_size);
562   }
563   else
564   {
565     GNUNET_assert ((0 == tc->link_array_size) && (NULL == tc->link_array));
566     index = 0;
567     tc->link_array_size = links;
568     tc->link_array =
569         GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
570   }
571   for (cnt = 0; cnt < links; cnt++)
572   {
573     do
574     {
575       A_rand =
576           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
577       B_rand =
578           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
579     }
580     while (A_rand == B_rand);
581     make_link (&tc->link_array[index + cnt], A_rand, B_rand, tc);
582   }
583 }
584
585
586 /**
587  * Generates scale free network. Its construction is described in:
588  *
589  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
590  *
591  * @param tc the topology context
592  */
593 static void
594 gen_scale_free (struct TopologyContext *tc)
595 {
596   double random;
597   double probability;
598   unsigned int cnt;
599   unsigned int previous_connections;
600   unsigned int i;
601   uint16_t *popularity;
602
603   popularity = GNUNET_malloc (sizeof (uint16_t) * tc->num_peers);
604   /* Initially connect peer 1 to peer 0 */
605   tc->link_array_size = 1;
606   tc->link_array = GNUNET_malloc (sizeof (struct OverlayLink));
607   make_link (&tc->link_array[0], 0, 1, tc);
608   popularity[0]++;              /* increase popularity of 0 as 1 connected to it */
609   for (cnt = 1; cnt < tc->num_peers; cnt++)
610   {
611     previous_connections = tc->link_array_size;
612     for (i = 0; i < cnt; i++)
613     {
614       probability = ((double) popularity[i]) / ((double) previous_connections);
615       random =
616           ((double)
617            GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
618                                      UINT64_MAX)) / ((double) UINT64_MAX);
619       if (random < probability)
620       {
621         tc->link_array_size++;
622         tc->link_array =
623             GNUNET_realloc (tc->link_array,
624                             (sizeof (struct OverlayLink) *
625                              tc->link_array_size));
626         make_link (&tc->link_array[tc->link_array_size - 1], cnt, i, tc);
627         popularity[cnt]++;
628       }
629     }
630   }
631   GNUNET_free (popularity);
632 }
633
634
635 /**
636  * Generates topology from the given file
637  *
638  * @param tc the topology context
639  * @param filename the filename of the file containing topology data
640  */
641 static void
642 gen_topo_from_file (struct TopologyContext *tc, const char *filename)
643 {
644   char *data;
645   char *end;
646   char *buf;
647   uint64_t fs;
648   uint64_t offset;
649   unsigned long int peer_id;
650   unsigned long int other_peer_id;
651   enum ParseState
652   {
653
654     /**
655      * We read the peer index
656      */
657     PEER_INDEX,
658
659     /**
660      * We read the other peer indices
661      */
662     OTHER_PEER_INDEX,
663
664   } state;
665   int status;
666
667   status = GNUNET_SYSERR;
668   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
669   {
670     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s not found\n"), filename);
671     return;
672   }
673   if (GNUNET_OK !=
674       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
675   {
676     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s has no data\n"),
677          filename);
678     return;
679   }
680   data = GNUNET_malloc (fs);
681   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
682   {
683     LOG (GNUNET_ERROR_TYPE_ERROR, _("Topology file %s cannot be read\n"),
684          filename);
685     goto _exit;
686   }
687
688   offset = 0;
689   peer_id = 0;
690   state = PEER_INDEX;
691   while (offset < fs)
692   {
693     if (0 != isspace (data[offset]))
694     {
695       offset++;
696       continue;
697     }
698     switch (state)
699     {
700     case PEER_INDEX:
701       buf = strchr (&data[offset], ':');
702       if (NULL == buf)
703       {
704         LOG (GNUNET_ERROR_TYPE_ERROR,
705              _("Failed to read peer index from toology file: %s"), filename);
706         goto _exit;
707       }
708       *buf = '\0';
709       errno = 0;
710       peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
711       if (0 != errno)
712       {
713         LOG (GNUNET_ERROR_TYPE_ERROR,
714              _("Value in given topology file: %s out of range\n"), filename);
715         goto _exit;
716       }
717       if (&data[offset] == end)
718       {
719         LOG (GNUNET_ERROR_TYPE_ERROR,
720              _("Failed to read peer index from topology file: %s"), filename);
721         goto _exit;
722       }
723       if (tc->num_peers <= peer_id)
724       {
725         LOG (GNUNET_ERROR_TYPE_ERROR,
726              _("Topology file needs more peers than given ones\n"), filename);
727         goto _exit;
728       }
729       state = OTHER_PEER_INDEX;
730       offset += ((unsigned int) (buf - &data[offset])) + 1;
731       break;
732     case OTHER_PEER_INDEX:
733       errno = 0;
734       other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
735       if (0 != errno)
736       {
737         LOG (GNUNET_ERROR_TYPE_ERROR,
738              _("Value in given topology file: %s out of range\n"), filename);
739         goto _exit;
740       }
741       if (&data[offset] == end)
742       {
743         LOG (GNUNET_ERROR_TYPE_ERROR,
744              _("Failed to read peer index from topology file: %s"), filename);
745         goto _exit;
746       }
747       if (tc->num_peers <= other_peer_id)
748       {
749         LOG (GNUNET_ERROR_TYPE_ERROR,
750              _("Topology file needs more peers than given ones\n"), filename);
751         goto _exit;
752       }
753       if (peer_id != other_peer_id)
754       {
755         tc->link_array_size++;
756         tc->link_array =
757             GNUNET_realloc (tc->link_array,
758                             sizeof (struct OverlayLink) * tc->link_array_size);
759         offset += end - &data[offset];
760         make_link (&tc->link_array[tc->link_array_size - 1], peer_id,
761                    other_peer_id, tc);
762       }
763       else
764         LOG (GNUNET_ERROR_TYPE_WARNING,
765              _("Ignoring to connect peer %u to peer %u\n"), peer_id,
766              other_peer_id);
767       while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs))
768         offset++;
769       if ('\n' == data[offset])
770         state = PEER_INDEX;
771       else if ('|' == data[offset])
772       {
773         state = OTHER_PEER_INDEX;
774         offset++;
775       }
776       break;
777     }
778   }
779   status = GNUNET_OK;
780
781 _exit:
782   GNUNET_free (data);
783   if (GNUNET_OK != status)
784   {
785     LOG (GNUNET_ERROR_TYPE_WARNING, "Removing link data read from the file\n");
786     tc->link_array_size = 0;
787     GNUNET_free_non_null (tc->link_array);
788     tc->link_array = NULL;
789   }
790 }
791
792
793 /**
794  * Configure overall network topology to have a particular shape.
795  *
796  * @param op_cls closure argument to give with the operation event
797  * @param num_peers number of peers in 'peers'
798  * @param peers array of 'num_peers' with the peers to configure
799  * @param topo desired underlay topology to use
800  * @param ap topology-specific options
801  * @return handle to the operation, NULL if configuring the topology
802  *         is not allowed at this time
803  */
804 struct GNUNET_TESTBED_Operation *
805 GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls,
806                                                unsigned int num_peers,
807                                                struct GNUNET_TESTBED_Peer
808                                                **peers,
809                                                enum
810                                                GNUNET_TESTBED_TopologyOption
811                                                topo, va_list ap)
812 {
813   GNUNET_break (0);
814   return NULL;
815 }
816
817
818 /**
819  * Configure overall network topology to have a particular shape.
820  *
821  * @param op_cls closure argument to give with the operation event
822  * @param num_peers number of peers in 'peers'
823  * @param peers array of 'num_peers' with the peers to configure
824  * @param topo desired underlay topology to use
825  * @param ... topology-specific options
826  * @return handle to the operation, NULL if configuring the topology
827  *         is not allowed at this time
828  */
829 struct GNUNET_TESTBED_Operation *
830 GNUNET_TESTBED_underlay_configure_topology (void *op_cls,
831                                             unsigned int num_peers,
832                                             struct GNUNET_TESTBED_Peer **peers,
833                                             enum GNUNET_TESTBED_TopologyOption
834                                             topo, ...)
835 {
836   GNUNET_break (0);
837   return NULL;
838 }
839
840
841 /**
842  * All peers must have been started before calling this function.
843  * This function then connects the given peers in the P2P overlay
844  * using the given topology.
845  *
846  * @param op_cls closure argument to give with the peer connect operation events
847  *          generated through this function
848  * @param num_peers number of peers in 'peers'
849  * @param peers array of 'num_peers' with the peers to configure
850  * @param max_connections the maximums number of overlay connections that will
851  *          be made to achieve the given topology
852  * @param comp_cb the completion callback to call when the topology generation
853  *          is completed
854  * @param comp_cb_cls closure for the above completion callback
855  * @param topo desired underlay topology to use
856  * @param va topology-specific options
857  * @return handle to the operation, NULL if connecting these
858  *         peers is fundamentally not possible at this time (peers
859  *         not running or underlay disallows) or if num_peers is less than 2
860  */
861 struct GNUNET_TESTBED_Operation *
862 GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls,
863                                               unsigned int num_peers,
864                                               struct GNUNET_TESTBED_Peer **peers,
865                                               unsigned int *max_connections,
866                                               GNUNET_TESTBED_TopologyCompletionCallback
867                                               comp_cb,
868                                               void *comp_cb_cls,
869                                               enum GNUNET_TESTBED_TopologyOption topo,
870                                               va_list va)
871 {
872   struct TopologyContext *tc;
873   struct GNUNET_TESTBED_Operation *op;
874   struct GNUNET_TESTBED_Controller *c;
875   enum GNUNET_TESTBED_TopologyOption secondary_option;
876   unsigned int cnt;
877
878   if (num_peers < 2)
879     return NULL;
880   c = peers[0]->controller;
881   tc = GNUNET_malloc (sizeof (struct TopologyContext));
882   tc->peers = peers;
883   tc->num_peers = num_peers;
884   tc->op_cls = op_cls;
885   tc->retry_cnt = DEFAULT_RETRY_CNT;
886   tc->comp_cb = comp_cb;
887   tc->comp_cb_cls = comp_cb_cls;
888   switch (topo)
889   {
890   case GNUNET_TESTBED_TOPOLOGY_LINE:
891     gen_topo_line (tc);
892     break;
893   case GNUNET_TESTBED_TOPOLOGY_RING:
894     gen_topo_ring (tc);
895     break;
896   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
897     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO);
898     break;
899   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
900     gen_topo_ring (tc);
901     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
902     break;
903   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
904     tc->link_array_size = num_peers * (num_peers - 1);
905     tc->link_array =
906         GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
907     {
908       unsigned int offset;
909
910       offset = 0;
911       for (cnt = 0; cnt < num_peers; cnt++)
912       {
913         unsigned int neighbour;
914
915         for (neighbour = 0; neighbour < num_peers; neighbour++)
916         {
917           if (neighbour == cnt)
918             continue;
919           tc->link_array[offset].A = cnt;
920           tc->link_array[offset].B = neighbour;
921           tc->link_array[offset].tc = tc;
922           offset++;
923         }
924       }
925     }
926     break;
927   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
928     gen_topo_2dtorus (tc);
929     break;
930   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
931     gen_topo_2dtorus (tc);
932     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
933
934     break;
935   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
936     gen_scale_free (tc);
937     break;
938   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
939   {
940     const char *filename;
941
942     filename = va_arg (va, const char *);
943
944     GNUNET_assert (NULL != filename);
945     gen_topo_from_file (tc, filename);
946   }
947     break;
948   default:
949     GNUNET_break (0);
950     GNUNET_free (tc);
951     return NULL;
952   }
953   do
954   {
955     secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
956
957     switch (secondary_option)
958     {
959     case GNUNET_TESTBED_TOPOLOGY_RETRY_CNT:
960       tc->retry_cnt =  va_arg (va, unsigned int);
961       break;
962     case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
963       break;
964     default:
965       GNUNET_break (0);         /* Should not use any other option apart from
966                                  * the ones handled here */
967       GNUNET_free_non_null (tc->link_array);
968       GNUNET_free (tc);
969       return NULL;
970     }
971   }
972   while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option);
973   op = GNUNET_TESTBED_operation_create_ (tc,
974                                          &opstart_overlay_configure_topology,
975                                          &oprelease_overlay_configure_topology);
976   GNUNET_TESTBED_operation_queue_insert_
977       (c->opq_parallel_topology_config_operations, op);
978   GNUNET_TESTBED_operation_begin_wait_ (op);
979   LOG (GNUNET_ERROR_TYPE_DEBUG, "Generated %u connections\n",
980        tc->link_array_size);
981   if (NULL != max_connections)
982     *max_connections = tc->link_array_size;
983   return op;
984 }
985
986
987 /**
988  * All peers must have been started before calling this function.
989  * This function then connects the given peers in the P2P overlay
990  * using the given topology.
991  *
992  * @param op_cls closure argument to give with the peer connect operation events
993  *          generated through this function
994  * @param num_peers number of peers in 'peers'
995  * @param peers array of 'num_peers' with the peers to configure
996  * @param max_connections the maximums number of overlay connections that will
997  *          be made to achieve the given topology
998  * @param comp_cb the completion callback to call when the topology generation
999  *          is completed
1000  * @param comp_cb_cls closure for the above completion callback
1001  * @param topo desired underlay topology to use
1002  * @param ... topology-specific options
1003  * @return handle to the operation, NULL if connecting these
1004  *         peers is fundamentally not possible at this time (peers
1005  *         not running or underlay disallows) or if num_peers is less than 2
1006  */
1007 struct GNUNET_TESTBED_Operation *
1008 GNUNET_TESTBED_overlay_configure_topology (void *op_cls,
1009                                            unsigned int num_peers,
1010                                            struct GNUNET_TESTBED_Peer **peers,
1011                                            unsigned int *max_connections,
1012                                            GNUNET_TESTBED_TopologyCompletionCallback
1013                                            comp_cb,
1014                                            void *comp_cb_cls,
1015                                            enum GNUNET_TESTBED_TopologyOption topo,
1016                                            ...)
1017 {
1018   struct GNUNET_TESTBED_Operation *op;
1019   va_list vargs;
1020
1021   GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1022   va_start (vargs, topo);
1023   op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers,
1024                                                      max_connections, 
1025                                                      comp_cb, comp_cb_cls,
1026                                                      topo,
1027                                                      vargs);
1028   va_end (vargs);
1029   return op;
1030 }
1031
1032
1033 /**
1034  * Get a topology from a string input.
1035  *
1036  * @param topology where to write the retrieved topology
1037  * @param topology_string The string to attempt to
1038  *        get a configuration value from
1039  * @return GNUNET_YES if topology string matched a
1040  *         known topology, GNUNET_NO if not
1041  */
1042 int
1043 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
1044                               const char *topology_string)
1045 {
1046   unsigned int cnt;
1047
1048   for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
1049   {
1050     if (0 == strcasecmp (topology_string, topology_strings[cnt]))
1051     {
1052       if (NULL != topology)
1053         *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
1054       return GNUNET_YES;
1055     }
1056   }
1057   return GNUNET_NO;
1058 }
1059
1060
1061 /**
1062  * Returns the string corresponding to the given topology
1063  *
1064  * @param topology the topology
1065  * @return the string (freshly allocated) of given topology; NULL if topology cannot be
1066  *           expressed as a string
1067  */
1068 char *
1069 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
1070 {
1071   if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
1072     return NULL;
1073   return GNUNET_strdup (topology_strings[topology]);
1074 }
1075
1076 /* end of testbed_api_topology.c */