- docs
[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   buf = data;
692   while (offset < fs)
693   {
694     if (0 != isspace (data[offset]))
695     {
696       offset++;
697       continue;
698     }
699     switch (state)
700     {
701     case PEER_INDEX:
702       buf = strchr (&data[offset], ':');
703       if (NULL == buf)
704       {
705         LOG (GNUNET_ERROR_TYPE_ERROR,
706              _("Failed to read peer index from toology file: %s"), filename);
707         goto _exit;
708       }
709       *buf = '\0';
710       errno = 0;
711       peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
712       if (0 != errno)
713       {
714         LOG (GNUNET_ERROR_TYPE_ERROR,
715              _("Value in given topology file: %s out of range\n"), filename);
716         goto _exit;
717       }
718       if (&data[offset] == end)
719       {
720         LOG (GNUNET_ERROR_TYPE_ERROR,
721              _("Failed to read peer index from topology file: %s"), filename);
722         goto _exit;
723       }
724       if (tc->num_peers <= peer_id)
725       {
726         LOG (GNUNET_ERROR_TYPE_ERROR,
727              _("Topology file needs more peers than given ones\n"), filename);
728         goto _exit;
729       }
730       state = OTHER_PEER_INDEX;
731       offset += ((unsigned int) (buf - &data[offset])) + 1;
732       break;
733     case OTHER_PEER_INDEX:
734       errno = 0;
735       other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
736       if (0 != errno)
737       {
738         LOG (GNUNET_ERROR_TYPE_ERROR,
739              _("Value in given topology file: %s out of range\n"), filename);
740         goto _exit;
741       }
742       if (&data[offset] == end)
743       {
744         LOG (GNUNET_ERROR_TYPE_ERROR,
745              _("Failed to read peer index from topology file: %s"), filename);
746         goto _exit;
747       }
748       if (tc->num_peers <= other_peer_id)
749       {
750         LOG (GNUNET_ERROR_TYPE_ERROR,
751              _("Topology file needs more peers than given ones\n"), filename);
752         goto _exit;
753       }
754       if (peer_id != other_peer_id)
755       {
756         tc->link_array_size++;
757         tc->link_array =
758             GNUNET_realloc (tc->link_array,
759                             sizeof (struct OverlayLink) * tc->link_array_size);
760         offset += end - &data[offset];
761         make_link (&tc->link_array[tc->link_array_size - 1], peer_id,
762                    other_peer_id, tc);
763       }
764       else
765         LOG (GNUNET_ERROR_TYPE_WARNING,
766              _("Ignoring to connect peer %u to peer %u\n"), peer_id,
767              other_peer_id);
768       while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs))
769         offset++;
770       if ('\n' == data[offset])
771         state = PEER_INDEX;
772       else if ('|' == data[offset])
773       {
774         state = OTHER_PEER_INDEX;
775         offset++;
776       }
777       break;
778     }
779   }
780   status = GNUNET_OK;
781
782 _exit:
783   GNUNET_free (data);
784   if (GNUNET_OK != status)
785   {
786     LOG (GNUNET_ERROR_TYPE_WARNING, "Removing link data read from the file\n");
787     tc->link_array_size = 0;
788     GNUNET_free_non_null (tc->link_array);
789     tc->link_array = NULL;
790   }
791 }
792
793
794 /**
795  * Configure overall network topology to have a particular shape.
796  *
797  * @param op_cls closure argument to give with the operation event
798  * @param num_peers number of peers in 'peers'
799  * @param peers array of 'num_peers' with the peers to configure
800  * @param topo desired underlay topology to use
801  * @param ap topology-specific options
802  * @return handle to the operation, NULL if configuring the topology
803  *         is not allowed at this time
804  */
805 struct GNUNET_TESTBED_Operation *
806 GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls,
807                                                unsigned int num_peers,
808                                                struct GNUNET_TESTBED_Peer
809                                                **peers,
810                                                enum
811                                                GNUNET_TESTBED_TopologyOption
812                                                topo, va_list ap)
813 {
814   GNUNET_break (0);
815   return NULL;
816 }
817
818
819 /**
820  * Configure overall network topology to have a particular shape.
821  *
822  * @param op_cls closure argument to give with the operation event
823  * @param num_peers number of peers in 'peers'
824  * @param peers array of 'num_peers' with the peers to configure
825  * @param topo desired underlay topology to use
826  * @param ... topology-specific options
827  * @return handle to the operation, NULL if configuring the topology
828  *         is not allowed at this time
829  */
830 struct GNUNET_TESTBED_Operation *
831 GNUNET_TESTBED_underlay_configure_topology (void *op_cls,
832                                             unsigned int num_peers,
833                                             struct GNUNET_TESTBED_Peer **peers,
834                                             enum GNUNET_TESTBED_TopologyOption
835                                             topo, ...)
836 {
837   GNUNET_break (0);
838   return NULL;
839 }
840
841
842 /**
843  * All peers must have been started before calling this function.
844  * This function then connects the given peers in the P2P overlay
845  * using the given topology.
846  *
847  * @param op_cls closure argument to give with the peer connect operation events
848  *          generated through this function
849  * @param num_peers number of peers in 'peers'
850  * @param peers array of 'num_peers' with the peers to configure
851  * @param max_connections the maximums number of overlay connections that will
852  *          be made to achieve the given topology
853  * @param comp_cb the completion callback to call when the topology generation
854  *          is completed
855  * @param comp_cb_cls closure for the above completion callback
856  * @param topo desired underlay topology to use
857  * @param va topology-specific options
858  * @return handle to the operation, NULL if connecting these
859  *         peers is fundamentally not possible at this time (peers
860  *         not running or underlay disallows) or if num_peers is less than 2
861  */
862 struct GNUNET_TESTBED_Operation *
863 GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls,
864                                               unsigned int num_peers,
865                                               struct GNUNET_TESTBED_Peer **peers,
866                                               unsigned int *max_connections,
867                                               GNUNET_TESTBED_TopologyCompletionCallback
868                                               comp_cb,
869                                               void *comp_cb_cls,
870                                               enum GNUNET_TESTBED_TopologyOption topo,
871                                               va_list va)
872 {
873   struct TopologyContext *tc;
874   struct GNUNET_TESTBED_Operation *op;
875   struct GNUNET_TESTBED_Controller *c;
876   enum GNUNET_TESTBED_TopologyOption secondary_option;
877   unsigned int cnt;
878
879   if (num_peers < 2)
880     return NULL;
881   c = peers[0]->controller;
882   tc = GNUNET_malloc (sizeof (struct TopologyContext));
883   tc->peers = peers;
884   tc->num_peers = num_peers;
885   tc->op_cls = op_cls;
886   tc->retry_cnt = DEFAULT_RETRY_CNT;
887   tc->comp_cb = comp_cb;
888   tc->comp_cb_cls = comp_cb_cls;
889   switch (topo)
890   {
891   case GNUNET_TESTBED_TOPOLOGY_LINE:
892     gen_topo_line (tc);
893     break;
894   case GNUNET_TESTBED_TOPOLOGY_RING:
895     gen_topo_ring (tc);
896     break;
897   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
898     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO);
899     break;
900   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
901     gen_topo_ring (tc);
902     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
903     break;
904   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
905     tc->link_array_size = num_peers * (num_peers - 1);
906     tc->link_array =
907         GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
908     {
909       unsigned int offset;
910
911       offset = 0;
912       for (cnt = 0; cnt < num_peers; cnt++)
913       {
914         unsigned int neighbour;
915
916         for (neighbour = 0; neighbour < num_peers; neighbour++)
917         {
918           if (neighbour == cnt)
919             continue;
920           tc->link_array[offset].A = cnt;
921           tc->link_array[offset].B = neighbour;
922           tc->link_array[offset].tc = tc;
923           offset++;
924         }
925       }
926     }
927     break;
928   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
929     gen_topo_2dtorus (tc);
930     break;
931   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
932     gen_topo_2dtorus (tc);
933     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
934
935     break;
936   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
937     gen_scale_free (tc);
938     break;
939   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
940   {
941     const char *filename;
942
943     filename = va_arg (va, const char *);
944
945     GNUNET_assert (NULL != filename);
946     gen_topo_from_file (tc, filename);
947   }
948     break;
949   default:
950     GNUNET_break (0);
951     GNUNET_free (tc);
952     return NULL;
953   }
954   do
955   {
956     secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
957
958     switch (secondary_option)
959     {
960     case GNUNET_TESTBED_TOPOLOGY_RETRY_CNT:
961       tc->retry_cnt =  va_arg (va, unsigned int);
962       break;
963     case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
964       break;
965     default:
966       GNUNET_break (0);         /* Should not use any other option apart from
967                                  * the ones handled here */
968       GNUNET_free_non_null (tc->link_array);
969       GNUNET_free (tc);
970       return NULL;
971     }
972   }
973   while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option);
974   op = GNUNET_TESTBED_operation_create_ (tc,
975                                          &opstart_overlay_configure_topology,
976                                          &oprelease_overlay_configure_topology);
977   GNUNET_TESTBED_operation_queue_insert_
978       (c->opq_parallel_topology_config_operations, op);
979   GNUNET_TESTBED_operation_begin_wait_ (op);
980   LOG (GNUNET_ERROR_TYPE_DEBUG, "Generated %u connections\n",
981        tc->link_array_size);
982   if (NULL != max_connections)
983     *max_connections = tc->link_array_size;
984   return op;
985 }
986
987
988 /**
989  * All peers must have been started before calling this function.
990  * This function then connects the given peers in the P2P overlay
991  * using the given topology.
992  *
993  * @param op_cls closure argument to give with the peer connect operation events
994  *          generated through this function
995  * @param num_peers number of peers in 'peers'
996  * @param peers array of 'num_peers' with the peers to configure
997  * @param max_connections the maximums number of overlay connections that will
998  *          be made to achieve the given topology
999  * @param comp_cb the completion callback to call when the topology generation
1000  *          is completed
1001  * @param comp_cb_cls closure for the above completion callback
1002  * @param topo desired underlay topology to use
1003  * @param ... topology-specific options
1004  * @return handle to the operation, NULL if connecting these
1005  *         peers is fundamentally not possible at this time (peers
1006  *         not running or underlay disallows) or if num_peers is less than 2
1007  */
1008 struct GNUNET_TESTBED_Operation *
1009 GNUNET_TESTBED_overlay_configure_topology (void *op_cls,
1010                                            unsigned int num_peers,
1011                                            struct GNUNET_TESTBED_Peer **peers,
1012                                            unsigned int *max_connections,
1013                                            GNUNET_TESTBED_TopologyCompletionCallback
1014                                            comp_cb,
1015                                            void *comp_cb_cls,
1016                                            enum GNUNET_TESTBED_TopologyOption topo,
1017                                            ...)
1018 {
1019   struct GNUNET_TESTBED_Operation *op;
1020   va_list vargs;
1021
1022   GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1023   va_start (vargs, topo);
1024   op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers,
1025                                                      max_connections, 
1026                                                      comp_cb, comp_cb_cls,
1027                                                      topo,
1028                                                      vargs);
1029   va_end (vargs);
1030   return op;
1031 }
1032
1033
1034 /**
1035  * Get a topology from a string input.
1036  *
1037  * @param topology where to write the retrieved topology
1038  * @param topology_string The string to attempt to
1039  *        get a configuration value from
1040  * @return GNUNET_YES if topology string matched a
1041  *         known topology, GNUNET_NO if not
1042  */
1043 int
1044 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
1045                               const char *topology_string)
1046 {
1047   unsigned int cnt;
1048
1049   for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
1050   {
1051     if (0 == strcasecmp (topology_string, topology_strings[cnt]))
1052     {
1053       if (NULL != topology)
1054         *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
1055       return GNUNET_YES;
1056     }
1057   }
1058   return GNUNET_NO;
1059 }
1060
1061
1062 /**
1063  * Returns the string corresponding to the given topology
1064  *
1065  * @param topology the topology
1066  * @return the string (freshly allocated) of given topology; NULL if topology cannot be
1067  *           expressed as a string
1068  */
1069 char *
1070 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
1071 {
1072   if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
1073     return NULL;
1074   return GNUNET_strdup (topology_strings[topology]);
1075 }
1076
1077 /* end of testbed_api_topology.c */