plugin datastore mysql
[oweals/gnunet.git] / src / testbed / testbed_api_topology.c
1 /*
2       This file is part of GNUnet
3       Copyright (C) 2008--2013 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18       Boston, MA 02110-1301, 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 /**
82  * Representation of an underlay link
83  */
84 struct UnderlayLink
85 {
86   /**
87    * position of peer A's handle in peers array
88    */
89   uint32_t A;
90
91   /**
92    * position of peer B's handle in peers array
93    */
94   uint32_t B;
95
96   /**
97    * Bandwidth of the link in bytes per second
98    */
99   uint32_t bandwidth;
100
101   /**
102    * Latency of the link in milliseconds
103    */
104   uint32_t latency;
105
106   /**
107    * Loss in the link in percentage of message dropped
108    */
109   uint32_t loss;
110 };
111
112
113 struct RetryListEntry
114 {
115   /**
116    * the next pointer for the DLL
117    */
118   struct RetryListEntry *next;
119
120   /**
121    * the prev pointer for the DLL
122    */
123   struct RetryListEntry *prev;
124
125   /**
126    * The link to be retired
127    */
128   struct OverlayLink *link;
129 };
130
131
132 /**
133  * Context information for overlay topologies
134  */
135 struct TopologyContextOverlay
136 {
137   /**
138    * The array of peers
139    */
140   struct GNUNET_TESTBED_Peer **peers;
141
142   /**
143    * An array of links; this array is of size link_array_size
144    */
145   struct OverlayLink *link_array;
146
147   /**
148    * The operation closure
149    */
150   void *op_cls;
151
152   /**
153    * topology generation completion callback
154    */
155   GNUNET_TESTBED_TopologyCompletionCallback comp_cb;
156
157   /**
158    * The closure for the above callback
159    */
160   void *comp_cb_cls;
161
162   /**
163    * DLL head for retry list
164    */
165   struct RetryListEntry *rl_head;
166
167   /**
168    * DLL tail for retry list
169    */
170   struct RetryListEntry *rl_tail;
171
172   /**
173    * How many retries to do before we give up
174    */
175   unsigned int retry_cnt;
176
177   /**
178    * Number of links to try
179    */
180   unsigned int nlinks;
181
182   /**
183    * How many links have been completed
184    */
185   unsigned int ncompleted;
186
187   /**
188    * Total successfully established overlay connections
189    */
190   unsigned int nsuccess;
191
192   /**
193    * Total failed overlay connections
194    */
195   unsigned int nfailures;
196 };
197
198
199 /**
200  * Topology context information for underlay topologies
201  */
202 struct TopologyContextUnderlay
203 {
204   /**
205    * The link array
206    */
207   struct UnderlayLink *link_array;
208 };
209
210
211 /**
212  * Context information for topology operations
213  */
214 struct TopologyContext
215 {
216   /**
217    * The type of this context
218    */
219   enum {
220
221     /**
222      * Type for underlay topology
223      */
224     TOPOLOGYCONTEXT_TYPE_UNDERLAY = 0,
225
226     /**
227      * Type for overlay topology
228      */
229     TOPOLOGYCONTEXT_TYPE_OVERLAY
230
231   } type;
232
233   union {
234
235     /**
236      * Topology context information for overlay topology
237      */
238     struct TopologyContextOverlay overlay;
239
240     /**
241      * Topology context information for underlay topology
242      */
243     struct TopologyContextUnderlay underlay;
244   } u;
245
246   /**
247    * The number of peers
248    */
249   unsigned int num_peers;
250
251   /**
252    * The size of the link array
253    */
254   unsigned int link_array_size;
255
256 };
257
258
259 /**
260  * A array of names representing topologies. Should be in sync with enum
261  * GNUNET_TESTBED_TopologyOption
262  */
263 const char *topology_strings[] = {
264
265     /**
266      * A clique (everyone connected to everyone else).  No options. If there are N
267      * peers this topology results in (N * (N -1)) connections.
268      */
269   "CLIQUE",
270
271     /**
272      * Small-world network (2d torus plus random links).  Followed
273      * by the number of random links to add (unsigned int).
274      */
275   "SMALL_WORLD",
276
277     /**
278      * Small-world network (ring plus random links).  Followed
279      * by the number of random links to add (unsigned int).
280      */
281   "SMALL_WORLD_RING",
282
283     /**
284      * Ring topology.  No options.
285      */
286   "RING",
287
288     /**
289      * 2-d torus.  No options.
290      */
291   "2D_TORUS",
292
293     /**
294      * Random graph.  Followed by the number of random links to be established
295      * (unsigned int)
296      */
297   "RANDOM",                     // GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI
298
299     /**
300      * Certain percentage of peers are unable to communicate directly
301      * replicating NAT conditions.  Followed by the fraction of
302      * NAT'ed peers (float).
303      */
304   "INTERNAT",
305
306     /**
307      * Scale free topology. Followed by the maximum number of links a node can
308      * have (unsigned int); and the number of links a new node should have when
309      * it is added to the network (unsigned int)
310      */
311   "SCALE_FREE",
312
313     /**
314      * Straight line topology.  No options.
315      */
316   "LINE",
317
318     /**
319      * Read a topology from a given file.  Followed by the name of the file (const char *).
320      */
321   "FROM_FILE",
322
323     /**
324      * All peers are disconnected.  No options.
325      */
326   "NONE",
327
328     /**
329      * End of strings
330      */
331   NULL
332 };
333
334
335 /**
336  * Callback to be called when an overlay_link operation complete
337  *
338  * @param cls element of the link_op array which points to the corresponding operation
339  * @param op the operation that has been finished
340  * @param emsg error message in case the operation has failed; will be NULL if
341  *          operation has executed successfully.
342  */
343 static void
344 overlay_link_completed (void *cls, struct GNUNET_TESTBED_Operation *op,
345                         const char *emsg)
346 {
347   struct OverlayLink *link = cls;
348   struct TopologyContext *tc;
349   struct TopologyContextOverlay *overlay;
350   struct RetryListEntry *retry_entry;
351
352   GNUNET_assert (op == link->op);
353   GNUNET_TESTBED_operation_done (op);
354   link->op = NULL;
355   tc = link->tc;
356   GNUNET_assert (TOPOLOGYCONTEXT_TYPE_OVERLAY == tc->type);
357   overlay = &tc->u.overlay;
358   if (NULL != emsg)
359   {
360     overlay->nfailures++;
361     if (0 != overlay->retry_cnt)
362     {
363       LOG (GNUNET_ERROR_TYPE_WARNING,
364            "Error while establishing a link: %s -- Retrying\n", emsg);
365       retry_entry = GNUNET_new (struct RetryListEntry);
366       retry_entry->link = link;
367       GNUNET_CONTAINER_DLL_insert_tail (overlay->rl_head, overlay->rl_tail, retry_entry);
368     }
369   }
370   else
371     overlay->nsuccess++;
372   overlay->ncompleted++;
373   if (overlay->ncompleted < overlay->nlinks)
374     return;
375   if ((0 != overlay->retry_cnt) && (NULL != overlay->rl_head))
376   {
377     overlay->retry_cnt--;
378     overlay->ncompleted = 0;
379     overlay->nlinks = 0;
380     while (NULL != (retry_entry = overlay->rl_head))
381     {
382       link = retry_entry->link;
383       link->op =
384           GNUNET_TESTBED_overlay_connect (overlay->op_cls, &overlay_link_completed,
385                                           link, overlay->peers[link->A],
386                                           overlay->peers[link->B]);
387       overlay->nlinks++;
388       GNUNET_CONTAINER_DLL_remove (overlay->rl_head, overlay->rl_tail, retry_entry);
389       GNUNET_free (retry_entry);
390     }
391     return;
392   }
393   if (NULL != overlay->comp_cb)
394   {
395     overlay->comp_cb (overlay->comp_cb_cls, overlay->nsuccess, overlay->nfailures);
396   }
397 }
398
399
400
401 /**
402  * Function called when a overlay connect operation is ready
403  *
404  * @param cls the Topology context
405  */
406 static void
407 opstart_overlay_configure_topology (void *cls)
408 {
409   struct TopologyContext *tc = cls;
410   struct TopologyContextOverlay *overlay;
411   unsigned int p;
412
413   GNUNET_assert (TOPOLOGYCONTEXT_TYPE_OVERLAY == tc->type);
414   overlay = &tc->u.overlay;
415   overlay->nlinks = tc->link_array_size;
416   for (p = 0; p < tc->link_array_size; p++)
417   {
418     overlay->link_array[p].op =
419         GNUNET_TESTBED_overlay_connect (overlay->op_cls, &overlay_link_completed,
420                                         &overlay->link_array[p],
421                                         overlay->peers[overlay->link_array[p].A],
422                                         overlay->peers[overlay->link_array[p].B]);
423   }
424 }
425
426
427 /**
428  * Callback which will be called when overlay connect operation is released
429  *
430  * @param cls the Topology context
431  */
432 static void
433 oprelease_overlay_configure_topology (void *cls)
434 {
435   struct TopologyContext *tc = cls;
436   struct TopologyContextOverlay *overlay;
437   struct RetryListEntry *retry_entry;
438   unsigned int p;
439
440   GNUNET_assert (TOPOLOGYCONTEXT_TYPE_OVERLAY == tc->type);
441   overlay = &tc->u.overlay;
442   while (NULL != (retry_entry = overlay->rl_head))
443   {
444     GNUNET_CONTAINER_DLL_remove (overlay->rl_head, overlay->rl_tail, retry_entry);
445     GNUNET_free (retry_entry);
446   }
447   if (NULL != overlay->link_array)
448   {
449     for (p = 0; p < tc->link_array_size; p++)
450       if (NULL != overlay->link_array[p].op)
451         GNUNET_TESTBED_operation_done (overlay->link_array[p].op);
452     GNUNET_free (overlay->link_array);
453   }
454   GNUNET_free (tc);
455 }
456
457
458 /**
459  * Populates the OverlayLink structure.
460  *
461  * @param offset the offset of the link array to use
462  * @param A the peer A. Should be different from B
463  * @param B the peer B. Should be different from A
464  * @param tc the TopologyContext
465  * @return
466  */
467 static void
468 make_link (unsigned int offset, uint32_t A, uint32_t B,
469            struct TopologyContext *tc)
470 {
471   GNUNET_assert (A != B);
472   switch (tc->type)
473   {
474   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
475     {
476       struct TopologyContextOverlay *overlay;
477       struct OverlayLink *olink;
478
479       overlay = &tc->u.overlay;
480       GNUNET_assert (offset < tc->link_array_size);
481       olink = &overlay->link_array[offset];
482       LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting peer %u to %u\n", B, A);
483       olink->A = A;
484       olink->B = B;
485       olink->op = NULL;
486       olink->tc = tc;
487     }
488     break;
489   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
490     {
491       struct TopologyContextUnderlay *underlay;
492       struct UnderlayLink *ulink;
493
494       underlay = &tc->u.underlay;
495       GNUNET_assert (offset < tc->link_array_size);
496       ulink = &underlay->link_array[offset];
497       ulink->A = A;
498       ulink->B = B;
499     }
500     break;
501   }
502 }
503
504
505 /**
506  * Generates line topology
507  *
508  * @param tc the topology context
509  */
510 static void
511 gen_topo_line (struct TopologyContext *tc)
512 {
513   unsigned int cnt;
514
515   tc->link_array_size = tc->num_peers - 1;
516   switch (tc->type)
517   {
518   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
519     {
520       struct TopologyContextOverlay *overlay;
521
522       overlay = &tc->u.overlay;
523       overlay->link_array =
524           GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
525     }
526     break;
527   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
528     {
529       struct TopologyContextUnderlay *underlay;
530
531       underlay = &tc->u.underlay;
532       underlay->link_array =
533           GNUNET_malloc (sizeof (struct UnderlayLink) * tc->link_array_size);
534     }
535     break;
536   }
537   for (cnt = 0; cnt < (tc->link_array_size); cnt++)
538     make_link (cnt, cnt, cnt + 1, tc);
539 }
540
541
542 /**
543  * Generates ring topology
544  *
545  * @param tc the topology context
546  */
547 static void
548 gen_topo_ring (struct TopologyContext *tc)
549 {
550   gen_topo_line (tc);
551   tc->link_array_size++;
552   switch (tc->type)
553   {
554   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
555     {
556       struct TopologyContextOverlay *overlay;
557
558       overlay = &tc->u.overlay;
559       overlay->link_array =
560           GNUNET_realloc (overlay->link_array, sizeof (struct OverlayLink) *
561                           tc->link_array_size);
562     }
563     break;
564   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
565     {
566       struct TopologyContextUnderlay *underlay;
567
568       underlay = &tc->u.underlay;
569       underlay->link_array =
570           GNUNET_realloc (underlay->link_array, sizeof (struct UnderlayLink) *
571       tc->link_array_size);
572     }
573     break;
574   }
575   make_link (tc->link_array_size - 1, tc->num_peers - 1, 0, tc);
576 }
577
578
579 /**
580  * Returns the number of links that are required to generate a 2d torus for the
581  * given number of peers. Also returns the arrangment (number of rows and the
582  * length of each row)
583  *
584  * @param num_peers number of peers
585  * @param rows number of rows in the 2d torus. Can be NULL
586  * @param rows_len the length of each row. This array will be allocated
587  *          fresh. The caller should free it. Can be NULL
588  * @return the number of links that are required to generate a 2d torus for the
589  *           given number of peers
590  */
591 unsigned int
592 GNUNET_TESTBED_2dtorus_calc_links (unsigned int num_peers, unsigned int *rows,
593                                    unsigned int **rows_len)
594 {
595   double sq;
596   unsigned int sq_floor;
597   unsigned int _rows;
598   unsigned int *_rows_len;
599   unsigned int x;
600   unsigned int y;
601   unsigned int _num_peers;
602   unsigned int cnt;
603
604   sq = sqrt (num_peers);
605   sq = floor (sq);
606   sq_floor = (unsigned int) sq;
607   _rows = (sq_floor + 1);
608   _rows_len = GNUNET_malloc (sizeof (unsigned int) * _rows);
609   for (y = 0; y < _rows - 1; y++)
610     _rows_len[y] = sq_floor;
611   _num_peers = sq_floor * sq_floor;
612   cnt = (_num_peers < 2) ? _num_peers : 2 * _num_peers;
613   x = 0;
614   y = 0;
615   while (_num_peers < num_peers)
616   {
617     if (x < y)
618       _rows_len[_rows - 1] = ++x;
619     else
620       _rows_len[y++]++;
621     _num_peers++;
622   }
623   cnt += (x < 2) ? x : 2 * x;
624   cnt += (y < 2) ? y : 2 * y;
625   if (0 == _rows_len[_rows - 1])
626     _rows--;
627   if (NULL != rows)
628     *rows = _rows;
629   if (NULL != rows_len)
630     *rows_len = _rows_len;
631   else
632     GNUNET_free (_rows_len);
633   return cnt;
634 }
635
636
637 /**
638  * Generates ring topology
639  *
640  * @param tc the topology context
641  */
642 static void
643 gen_topo_2dtorus (struct TopologyContext *tc)
644 {
645   unsigned int rows;
646   unsigned int *rows_len;
647   unsigned int x;
648   unsigned int y;
649   unsigned int cnt;
650   unsigned int offset;
651
652   tc->link_array_size =
653       GNUNET_TESTBED_2dtorus_calc_links (tc->num_peers, &rows, &rows_len);
654   switch (tc->type)
655   {
656   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
657     {
658       struct TopologyContextOverlay *overlay;
659
660       overlay = &tc->u.overlay;
661       overlay->link_array =
662           GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
663     }
664     break;
665   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
666     {
667       struct TopologyContextUnderlay *underlay;
668
669       underlay = &tc->u.underlay;
670       underlay->link_array =
671           GNUNET_malloc (sizeof (struct UnderlayLink) * tc->link_array_size);
672       break;
673     }
674   }
675   cnt = 0;
676   offset = 0;
677   for (y = 0; y < rows; y++)
678   {
679     for (x = 0; x < rows_len[y] - 1; x++)
680     {
681       make_link (cnt, offset + x, offset + x + 1, tc);
682       cnt++;
683     }
684     if (0 == x)
685       break;
686     make_link (cnt, offset + x, offset, tc);
687     cnt++;
688     offset += rows_len[y];
689   }
690   for (x = 0; x < rows_len[0]; x++)
691   {
692     offset = 0;
693     for (y = 0; y < rows - 1; y++)
694     {
695       if (x >= rows_len[y + 1])
696         break;
697       GNUNET_assert (x < rows_len[y + 1]);
698       make_link (cnt, offset + x, offset + rows_len[y] + x, tc);
699       offset += rows_len[y];
700       cnt++;
701     }
702     if (0 == offset)
703       break;
704     make_link (cnt, offset + x, x, tc);
705     cnt++;
706   }
707   GNUNET_assert (cnt == tc->link_array_size);
708   GNUNET_free (rows_len);
709 }
710
711
712 /**
713  * Generates ring topology
714  *
715  * @param tc the topology context
716  * @param links the number of random links to establish
717  * @param append #GNUNET_YES to add links to existing link array; #GNUNET_NO to
718  *          create a new link array
719  */
720 static void
721 gen_topo_random (struct TopologyContext *tc,
722                  unsigned int links,
723                  int append)
724 {
725   unsigned int cnt;
726   unsigned int index;
727   uint32_t A_rand;
728   uint32_t B_rand;
729
730   if (1 == tc->num_peers)
731     return;
732   if (GNUNET_YES == append)
733   {
734     index = tc->link_array_size;
735     tc->link_array_size += links;
736   }
737   else
738   {
739     index = 0;
740     tc->link_array_size = links;
741   }
742   switch (tc->type)
743   {
744   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
745     {
746       struct TopologyContextOverlay *overlay;
747
748       overlay = &tc->u.overlay;
749       if (GNUNET_YES != append)
750       {
751         GNUNET_assert (NULL == overlay->link_array);
752         overlay->link_array =
753             GNUNET_malloc (sizeof (struct OverlayLink) * tc->link_array_size);
754         break;
755       }
756       GNUNET_assert ((0 < tc->link_array_size) && (NULL != overlay->link_array));
757       overlay->link_array =
758           GNUNET_realloc (overlay->link_array,
759                           sizeof (struct OverlayLink) * tc->link_array_size);
760       break;
761     }
762   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
763     {
764       struct TopologyContextUnderlay *underlay;
765
766       underlay = &tc->u.underlay;
767       if (GNUNET_YES != append)
768       {
769         GNUNET_assert (NULL == underlay->link_array);
770         underlay->link_array =
771             GNUNET_malloc (sizeof (struct UnderlayLink) * tc->link_array_size);
772         break;
773       }
774       GNUNET_assert ((0 < tc->link_array_size) && (NULL != underlay->link_array));
775       underlay->link_array =
776           GNUNET_realloc (underlay->link_array,
777                           sizeof (struct UnderlayLink) * tc->link_array_size);
778       break;
779     }
780   }
781   for (cnt = 0; cnt < links; cnt++)
782   {
783     do
784     {
785       A_rand =
786           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
787       B_rand =
788           GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, tc->num_peers);
789     }
790     while (A_rand == B_rand);
791     make_link (index+cnt, A_rand, B_rand, tc);
792   }
793 }
794
795
796 /**
797  * Generates scale free network. Its construction is described in:
798  *
799  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
800  *
801  * @param tc the topology context
802  * @param cap maximum allowed node degree
803  * @param m number of edges to establish for a new node when it is added to the
804  *   network
805  */
806 static void
807 gen_topo_scale_free (struct TopologyContext *tc,
808                      uint16_t cap,
809                      uint8_t m)
810 {
811   unsigned int *deg;
812   unsigned int *etab;
813   unsigned int *used;
814   unsigned int etaboff;
815   unsigned int cnt;
816   unsigned int cnt2;
817   unsigned int peer;
818   unsigned int random_peer;
819   unsigned int links;
820   unsigned int off;
821   unsigned int redo_threshold;
822
823   etaboff = 0;
824   tc->link_array_size = tc->num_peers * m;
825   switch (tc->type)
826   {
827   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
828     {
829       struct TopologyContextOverlay *overlay;
830
831       overlay = &tc->u.overlay;
832       overlay->link_array = GNUNET_malloc_large (sizeof (struct OverlayLink) *
833                                                  tc->link_array_size);
834     }
835     break;
836   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
837     {
838       struct TopologyContextUnderlay *underlay;
839
840       underlay = &tc->u.underlay;
841       underlay->link_array = GNUNET_malloc_large (sizeof (struct UnderlayLink) *
842                                                   tc->link_array_size);
843     }
844     break;
845   }
846   etab = GNUNET_malloc_large (sizeof (unsigned int) * 2 * tc->link_array_size);
847   deg = GNUNET_malloc (sizeof (unsigned int) * tc->num_peers);
848   used = GNUNET_malloc (sizeof (unsigned int) * m);
849   /* start by connecting peer 1 to peer 0 */
850   make_link (0, 0, 1, tc);
851   deg[0]++;
852   deg[1]++;
853   etab[etaboff++] = 0;
854   etab[etaboff++] = 1;
855   links = 1;
856   for (peer = 2; peer < tc->num_peers; peer++)
857   {
858     if (cap < deg[peer])
859       continue;
860     for (cnt = 0; cnt < GNUNET_MIN (peer, m); cnt++)
861     {
862       redo_threshold = 0;
863     redo:
864       off = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, etaboff);
865       random_peer = etab[off];
866       if (cap < deg[random_peer])
867       {
868         if (++redo_threshold > GNUNET_MAX (1, cap / 2))
869         {
870           redo_threshold = 0;
871           off = 0;
872           for (cnt2 = 0; cnt2 < etaboff; cnt2++)
873           {
874             if (random_peer == etab[cnt2])
875             {
876               off++;
877               continue;
878             }
879             etab[cnt2 - off] = etab[cnt2];
880           }
881           etaboff -= off;
882         }
883         goto redo;
884       }
885       for (cnt2 = 0; cnt2 < cnt; cnt2++)
886         if (random_peer == used[cnt2])
887           goto redo;
888       make_link (links + cnt, random_peer, peer, tc);
889       deg[random_peer]++;
890       deg[peer]++;
891       used[cnt] = random_peer;
892     }
893     for (cnt = 0; cnt < GNUNET_MIN (peer, m); cnt++)
894     {
895       etab[etaboff++] = used[cnt];
896       etab[etaboff++] = peer;
897     }
898     links += GNUNET_MIN (peer, m);
899   }
900   GNUNET_free (etab);
901   GNUNET_free (used);
902   GNUNET_free (deg);
903   GNUNET_assert (links <= tc->link_array_size);
904   tc->link_array_size = links;
905   switch (tc->type)
906   {
907   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
908     {
909       struct TopologyContextOverlay *overlay;
910
911       overlay = &tc->u.overlay;
912       overlay->link_array =
913           GNUNET_realloc (overlay->link_array, sizeof (struct OverlayLink) * tc->link_array_size);
914     }
915     break;
916   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
917     {
918       struct TopologyContextUnderlay *underlay;
919
920       underlay = &tc->u.underlay;
921       underlay->link_array =
922           GNUNET_realloc (underlay->link_array, sizeof (struct UnderlayLink) * tc->link_array_size);
923     }
924     break;
925   }
926 }
927
928
929 /**
930  * Generates topology from the given file
931  *
932  * @param tc the topology context
933  * @param filename the filename of the file containing topology data
934  */
935 static void
936 gen_topo_from_file (struct TopologyContext *tc,
937                     const char *filename)
938 {
939   char *data;
940   char *end;
941   char *buf;
942   uint64_t fs;
943   uint64_t offset;
944   unsigned long int peer_id;
945   unsigned long int other_peer_id;
946   enum ParseState
947   {
948
949     /**
950      * We read the peer index
951      */
952     PEER_INDEX,
953
954     /**
955      * We read the other peer indices
956      */
957     OTHER_PEER_INDEX,
958
959   } state;
960   int status;
961
962   status = GNUNET_SYSERR;
963   if (GNUNET_YES != GNUNET_DISK_file_test (filename))
964   {
965     LOG (GNUNET_ERROR_TYPE_ERROR,
966          _("Topology file %s not found\n"),
967          filename);
968     return;
969   }
970   if (GNUNET_OK !=
971       GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES))
972   {
973     LOG (GNUNET_ERROR_TYPE_ERROR,
974          _("Topology file %s has no data\n"),
975          filename);
976     return;
977   }
978   data = GNUNET_malloc (fs);
979   if (fs != GNUNET_DISK_fn_read (filename, data, fs))
980   {
981     LOG (GNUNET_ERROR_TYPE_ERROR,
982          _("Topology file %s cannot be read\n"),
983          filename);
984     goto _exit;
985   }
986
987   offset = 0;
988   peer_id = 0;
989   state = PEER_INDEX;
990   while (offset < fs)
991   {
992     if (0 != isspace (data[offset]))
993     {
994       offset++;
995       continue;
996     }
997     switch (state)
998     {
999     case PEER_INDEX:
1000       buf = strchr (&data[offset], ':');
1001       if (NULL == buf)
1002       {
1003         LOG (GNUNET_ERROR_TYPE_ERROR,
1004              _("Failed to read peer index from toology file: %s"), filename);
1005         goto _exit;
1006       }
1007       *buf = '\0';
1008       errno = 0;
1009       peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
1010       if (0 != errno)
1011       {
1012         LOG (GNUNET_ERROR_TYPE_ERROR,
1013              _("Value in given topology file: %s out of range\n"), filename);
1014         goto _exit;
1015       }
1016       if (&data[offset] == end)
1017       {
1018         LOG (GNUNET_ERROR_TYPE_ERROR,
1019              _("Failed to read peer index from topology file: %s"), filename);
1020         goto _exit;
1021       }
1022       if (tc->num_peers <= peer_id)
1023       {
1024         LOG (GNUNET_ERROR_TYPE_ERROR,
1025              _("Topology file needs more peers than given ones\n"), filename);
1026         goto _exit;
1027       }
1028       state = OTHER_PEER_INDEX;
1029       offset += ((unsigned int) (buf - &data[offset])) + 1;
1030       break;
1031     case OTHER_PEER_INDEX:
1032       errno = 0;
1033       other_peer_id = (unsigned int) strtoul (&data[offset], &end, 10);
1034       if (0 != errno)
1035       {
1036         LOG (GNUNET_ERROR_TYPE_ERROR,
1037              _("Value in given topology file: %s out of range\n"), filename);
1038         goto _exit;
1039       }
1040       if (&data[offset] == end)
1041       {
1042         LOG (GNUNET_ERROR_TYPE_ERROR,
1043              _("Failed to read peer index from topology file: %s"), filename);
1044         goto _exit;
1045       }
1046       if (tc->num_peers <= other_peer_id)
1047       {
1048         LOG (GNUNET_ERROR_TYPE_ERROR,
1049              _("Topology file needs more peers than given ones\n"), filename);
1050         goto _exit;
1051       }
1052       if (peer_id != other_peer_id)
1053       {
1054         tc->link_array_size++;
1055         switch (tc->type)
1056         {
1057         case TOPOLOGYCONTEXT_TYPE_OVERLAY:
1058           {
1059             struct TopologyContextOverlay *overlay;
1060
1061             overlay = &tc->u.overlay;
1062             overlay->link_array =
1063                 GNUNET_realloc (overlay->link_array,
1064                                 sizeof (struct OverlayLink) * tc->link_array_size);
1065           }
1066           break;
1067         case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
1068           {
1069             struct TopologyContextUnderlay *underlay;
1070
1071             underlay = &tc->u.underlay;
1072             underlay->link_array =
1073                 GNUNET_realloc (underlay->link_array,
1074                                 sizeof (struct UnderlayLink) * tc->link_array_size);
1075           }
1076           break;
1077         }
1078         offset += end - &data[offset];
1079         make_link (tc->link_array_size - 1, peer_id, other_peer_id, tc);
1080       }
1081       else
1082         LOG (GNUNET_ERROR_TYPE_WARNING,
1083              _("Ignoring to connect peer %u to peer %u\n"),
1084              peer_id,
1085              other_peer_id);
1086       while (('\n' != data[offset]) && ('|' != data[offset]) && (offset < fs))
1087         offset++;
1088       if ('\n' == data[offset])
1089         state = PEER_INDEX;
1090       else if ('|' == data[offset])
1091       {
1092         state = OTHER_PEER_INDEX;
1093         offset++;
1094       }
1095       break;
1096     }
1097   }
1098   status = GNUNET_OK;
1099
1100 _exit:
1101   GNUNET_free (data);
1102   if (GNUNET_OK != status)
1103   {
1104     LOG (GNUNET_ERROR_TYPE_WARNING,
1105          "Removing link data read from the file\n");
1106     tc->link_array_size = 0;
1107     switch (tc->type)
1108     {
1109     case TOPOLOGYCONTEXT_TYPE_OVERLAY:
1110       {
1111         struct TopologyContextOverlay *overlay;
1112
1113         overlay = &tc->u.overlay;
1114         GNUNET_free_non_null (overlay->link_array);
1115         overlay->link_array = NULL;
1116       }
1117       break;
1118     case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
1119       {
1120         struct TopologyContextUnderlay *underlay;
1121
1122         underlay = &tc->u.underlay;
1123         GNUNET_free_non_null (underlay->link_array);
1124         underlay->link_array = NULL;
1125       }
1126       break;
1127     }
1128   }
1129 }
1130
1131
1132 /**
1133  * Generates clique topology
1134  *
1135  * @param tc the topology context
1136  */
1137 static void
1138 gen_topo_clique (struct TopologyContext *tc)
1139 {
1140   unsigned int cnt;
1141   unsigned int offset;
1142   unsigned int neighbour;
1143
1144   tc->link_array_size = tc->num_peers * (tc->num_peers - 1);
1145   switch (tc->type)
1146   {
1147   case TOPOLOGYCONTEXT_TYPE_OVERLAY:
1148     {
1149       struct TopologyContextOverlay *overlay;
1150
1151       overlay = &tc->u.overlay;
1152       overlay->link_array = GNUNET_new_array (tc->link_array_size,
1153                                               struct OverlayLink);
1154     }
1155     break;
1156   case TOPOLOGYCONTEXT_TYPE_UNDERLAY:
1157     {
1158       struct TopologyContextUnderlay *underlay;
1159
1160       underlay = &tc->u.underlay;
1161       underlay->link_array = GNUNET_new_array (tc->link_array_size,
1162                                                struct UnderlayLink);
1163     }
1164   }
1165   offset = 0;
1166   for (cnt = 0; cnt < tc->num_peers; cnt++)
1167   {
1168     for (neighbour = 0; neighbour < tc->num_peers; neighbour++)
1169     {
1170       if (neighbour == cnt)
1171         continue;
1172       make_link (offset, cnt, neighbour, tc);
1173       offset++;
1174     }
1175   }
1176 }
1177
1178
1179 /**
1180  * Configure overall network topology to have a particular shape.
1181  *
1182  * @param op_cls closure argument to give with the operation event
1183  * @param num_peers number of peers in @a peers
1184  * @param peers array of @a num_peers with the peers to configure
1185  * @param topo desired underlay topology to use
1186  * @param ap topology-specific options
1187  * @return handle to the operation, NULL if configuring the topology
1188  *         is not allowed at this time
1189  */
1190 struct GNUNET_TESTBED_Operation *
1191 GNUNET_TESTBED_underlay_configure_topology_va (void *op_cls,
1192                                                unsigned int num_peers,
1193                                                struct GNUNET_TESTBED_Peer
1194                                                **peers,
1195                                                enum
1196                                                GNUNET_TESTBED_TopologyOption
1197                                                topo, va_list ap)
1198 {
1199   GNUNET_break (0);
1200   return NULL;
1201 }
1202
1203
1204 /**
1205  * Configure overall network topology to have a particular shape.
1206  *
1207  * @param op_cls closure argument to give with the operation event
1208  * @param num_peers number of peers in @a peers
1209  * @param peers array of @a num_peers with the peers to configure
1210  * @param topo desired underlay topology to use
1211  * @param ... topology-specific options
1212  * @return handle to the operation, NULL if configuring the topology
1213  *         is not allowed at this time
1214  */
1215 struct GNUNET_TESTBED_Operation *
1216 GNUNET_TESTBED_underlay_configure_topology (void *op_cls,
1217                                             unsigned int num_peers,
1218                                             struct GNUNET_TESTBED_Peer **peers,
1219                                             enum GNUNET_TESTBED_TopologyOption
1220                                             topo, ...)
1221 {
1222   GNUNET_break (0);
1223   return NULL;
1224 }
1225
1226
1227 /**
1228  * All peers must have been started before calling this function.
1229  * This function then connects the given peers in the P2P overlay
1230  * using the given topology.
1231  *
1232  * @param op_cls closure argument to give with the peer connect operation events
1233  *          generated through this function
1234  * @param num_peers number of peers in @a peers
1235  * @param peers array of @a num_peers with the peers to configure
1236  * @param max_connections the maximums number of overlay connections that will
1237  *          be made to achieve the given topology
1238  * @param comp_cb the completion callback to call when the topology generation
1239  *          is completed
1240  * @param comp_cb_cls closure for the above completion callback
1241  * @param topo desired underlay topology to use
1242  * @param va topology-specific options
1243  * @return handle to the operation, NULL if connecting these
1244  *         peers is fundamentally not possible at this time (peers
1245  *         not running or underlay disallows) or if num_peers is less than 2
1246  */
1247 struct GNUNET_TESTBED_Operation *
1248 GNUNET_TESTBED_overlay_configure_topology_va (void *op_cls,
1249                                               unsigned int num_peers,
1250                                               struct GNUNET_TESTBED_Peer **peers,
1251                                               unsigned int *max_connections,
1252                                               GNUNET_TESTBED_TopologyCompletionCallback
1253                                               comp_cb,
1254                                               void *comp_cb_cls,
1255                                               enum GNUNET_TESTBED_TopologyOption topo,
1256                                               va_list va)
1257 {
1258   struct TopologyContext *tc;
1259   struct TopologyContextOverlay *overlay;
1260   struct GNUNET_TESTBED_Operation *op;
1261   struct GNUNET_TESTBED_Controller *c;
1262   enum GNUNET_TESTBED_TopologyOption secondary_option;
1263
1264   if (num_peers < 2)
1265     return NULL;
1266   c = peers[0]->controller;
1267   tc = GNUNET_new (struct TopologyContext);
1268   tc->type = TOPOLOGYCONTEXT_TYPE_OVERLAY;
1269   overlay = &tc->u.overlay;
1270   overlay->peers = peers;
1271   tc->num_peers = num_peers;
1272   overlay->op_cls = op_cls;
1273   overlay->retry_cnt = DEFAULT_RETRY_CNT;
1274   overlay->comp_cb = comp_cb;
1275   overlay->comp_cb_cls = comp_cb_cls;
1276   switch (topo)
1277   {
1278   case GNUNET_TESTBED_TOPOLOGY_LINE:
1279     gen_topo_line (tc);
1280     break;
1281   case GNUNET_TESTBED_TOPOLOGY_RING:
1282     gen_topo_ring (tc);
1283     break;
1284   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
1285     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_NO);
1286     break;
1287   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
1288     gen_topo_ring (tc);
1289     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
1290     break;
1291   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
1292     gen_topo_clique (tc);
1293     break;
1294   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
1295     gen_topo_2dtorus (tc);
1296     break;
1297   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
1298     gen_topo_2dtorus (tc);
1299     gen_topo_random (tc, va_arg (va, unsigned int), GNUNET_YES);
1300
1301     break;
1302   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
1303     {
1304       uint16_t cap;
1305       uint8_t m;
1306
1307       cap = (uint16_t) va_arg (va, unsigned int);
1308       m = (uint8_t) va_arg (va, unsigned int);
1309       gen_topo_scale_free (tc, cap, m);
1310     }
1311     break;
1312   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
1313   {
1314     const char *filename;
1315
1316     filename = va_arg (va, const char *);
1317
1318     GNUNET_assert (NULL != filename);
1319     gen_topo_from_file (tc, filename);
1320   }
1321     break;
1322   default:
1323     GNUNET_break (0);
1324     GNUNET_free (tc);
1325     return NULL;
1326   }
1327   do
1328   {
1329     secondary_option = va_arg (va, enum GNUNET_TESTBED_TopologyOption);
1330
1331     switch (secondary_option)
1332     {
1333     case GNUNET_TESTBED_TOPOLOGY_RETRY_CNT:
1334       overlay->retry_cnt =  va_arg (va, unsigned int);
1335       break;
1336     case GNUNET_TESTBED_TOPOLOGY_OPTION_END:
1337       break;
1338     default:
1339       GNUNET_break (0);         /* Should not use any other option apart from
1340                                  * the ones handled here */
1341       GNUNET_free_non_null (overlay->link_array);
1342       GNUNET_free (tc);
1343       return NULL;
1344     }
1345   }
1346   while (GNUNET_TESTBED_TOPOLOGY_OPTION_END != secondary_option);
1347   op = GNUNET_TESTBED_operation_create_ (tc,
1348                                          &opstart_overlay_configure_topology,
1349                                          &oprelease_overlay_configure_topology);
1350   GNUNET_TESTBED_operation_queue_insert_
1351       (c->opq_parallel_topology_config_operations, op);
1352   GNUNET_TESTBED_operation_begin_wait_ (op);
1353   LOG (GNUNET_ERROR_TYPE_DEBUG,
1354        "Generated %u connections\n",
1355        tc->link_array_size);
1356   if (NULL != max_connections)
1357     *max_connections = tc->link_array_size;
1358   return op;
1359 }
1360
1361
1362 /**
1363  * All peers must have been started before calling this function.
1364  * This function then connects the given peers in the P2P overlay
1365  * using the given topology.
1366  *
1367  * @param op_cls closure argument to give with the peer connect operation events
1368  *          generated through this function
1369  * @param num_peers number of peers in 'peers'
1370  * @param peers array of 'num_peers' with the peers to configure
1371  * @param max_connections the maximums number of overlay connections that will
1372  *          be made to achieve the given topology
1373  * @param comp_cb the completion callback to call when the topology generation
1374  *          is completed
1375  * @param comp_cb_cls closure for the above completion callback
1376  * @param topo desired underlay topology to use
1377  * @param ... topology-specific options
1378  * @return handle to the operation, NULL if connecting these
1379  *         peers is fundamentally not possible at this time (peers
1380  *         not running or underlay disallows) or if num_peers is less than 2
1381  */
1382 struct GNUNET_TESTBED_Operation *
1383 GNUNET_TESTBED_overlay_configure_topology (void *op_cls,
1384                                            unsigned int num_peers,
1385                                            struct GNUNET_TESTBED_Peer **peers,
1386                                            unsigned int *max_connections,
1387                                            GNUNET_TESTBED_TopologyCompletionCallback
1388                                            comp_cb,
1389                                            void *comp_cb_cls,
1390                                            enum GNUNET_TESTBED_TopologyOption topo,
1391                                            ...)
1392 {
1393   struct GNUNET_TESTBED_Operation *op;
1394   va_list vargs;
1395
1396   GNUNET_assert (topo < GNUNET_TESTBED_TOPOLOGY_OPTION_END);
1397   va_start (vargs, topo);
1398   op = GNUNET_TESTBED_overlay_configure_topology_va (op_cls, num_peers, peers,
1399                                                      max_connections,
1400                                                      comp_cb, comp_cb_cls,
1401                                                      topo,
1402                                                      vargs);
1403   va_end (vargs);
1404   return op;
1405 }
1406
1407
1408 /**
1409  * Get a topology from a string input.
1410  *
1411  * @param topology where to write the retrieved topology
1412  * @param topology_string The string to attempt to
1413  *        get a configuration value from
1414  * @return #GNUNET_YES if topology string matched a
1415  *         known topology, #GNUNET_NO if not
1416  */
1417 int
1418 GNUNET_TESTBED_topology_get_ (enum GNUNET_TESTBED_TopologyOption *topology,
1419                               const char *topology_string)
1420 {
1421   unsigned int cnt;
1422
1423   for (cnt = 0; NULL != topology_strings[cnt]; cnt++)
1424   {
1425     if (0 == strcasecmp (topology_string, topology_strings[cnt]))
1426     {
1427       if (NULL != topology)
1428         *topology = (enum GNUNET_TESTBED_TopologyOption) cnt;
1429       return GNUNET_YES;
1430     }
1431   }
1432   return GNUNET_NO;
1433 }
1434
1435
1436 /**
1437  * Returns the string corresponding to the given topology
1438  *
1439  * @param topology the topology
1440  * @return the string (freshly allocated) of given topology; NULL if topology cannot be
1441  *           expressed as a string
1442  */
1443 char *
1444 GNUNET_TESTBED_topology_to_str_ (enum GNUNET_TESTBED_TopologyOption topology)
1445 {
1446   if (GNUNET_TESTBED_TOPOLOGY_OPTION_END <= topology)
1447     return NULL;
1448   return GNUNET_strdup (topology_strings[topology]);
1449 }
1450
1451
1452 /**
1453  * Function to construct an underlay topology
1454  *
1455  * @param num_peers the number of peers for which the topology should be
1456  *          generated
1457  * @param proc the underlay link processor callback.  Will be called for each
1458  *          underlay link generated unless a previous call to this callback
1459  *          returned #GNUNET_SYSERR.  Cannot be NULL.
1460  * @param cls closure for @a proc
1461  * @param ... variable arguments denoting the topology and its parameters.  They
1462  *          should start with the type of topology to generate followed by their
1463  *          options.
1464  * @return #GNUNET_OK if underlay link generation is successful; #GNUNET_SYSERR
1465  *          upon error in generating the underlay or if any calls to the
1466  *          underlay link processor returned #GNUNET_SYSERR
1467  */
1468 int
1469 GNUNET_TESTBED_underlay_construct_ (int num_peers,
1470                                     underlay_link_processor proc,
1471                                     void *cls,
1472                                     ...)
1473 {
1474   struct TopologyContext tc;
1475   struct TopologyContextUnderlay *underlay;
1476   struct UnderlayLink *ulink;
1477   va_list vargs;
1478   enum GNUNET_TESTBED_TopologyOption topology;
1479   unsigned int cnt;
1480   int ret;
1481
1482   GNUNET_assert (NULL != proc);
1483   ret = GNUNET_OK;
1484   memset (&tc, 0, sizeof (tc));
1485   tc.num_peers = num_peers;
1486   tc.type = TOPOLOGYCONTEXT_TYPE_UNDERLAY;
1487   underlay = &tc.u.underlay;
1488   va_start (vargs, cls);
1489   topology = va_arg (vargs, enum GNUNET_TESTBED_TopologyOption);
1490   switch (topology)
1491   {
1492   case GNUNET_TESTBED_TOPOLOGY_LINE:
1493     gen_topo_line (&tc);
1494     break;
1495   case GNUNET_TESTBED_TOPOLOGY_RING:
1496     gen_topo_ring (&tc);
1497     break;
1498   case GNUNET_TESTBED_TOPOLOGY_CLIQUE:
1499     gen_topo_clique (&tc);
1500     break;
1501   case GNUNET_TESTBED_TOPOLOGY_2D_TORUS:
1502     gen_topo_2dtorus (&tc);
1503     break;
1504   case GNUNET_TESTBED_TOPOLOGY_ERDOS_RENYI:
1505     gen_topo_random (&tc, va_arg (vargs, unsigned int), GNUNET_NO);
1506     break;
1507   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD_RING:
1508     gen_topo_ring (&tc);
1509     gen_topo_random (&tc, va_arg (vargs, unsigned int), GNUNET_YES);
1510     break;
1511   case GNUNET_TESTBED_TOPOLOGY_SMALL_WORLD:
1512     gen_topo_2dtorus (&tc);
1513     gen_topo_random (&tc, va_arg (vargs, unsigned int), GNUNET_YES);
1514     break;
1515   case GNUNET_TESTBED_TOPOLOGY_FROM_FILE:
1516     {
1517       const char *filename;
1518       filename = va_arg (vargs, char *);
1519       GNUNET_assert (NULL != filename);
1520       gen_topo_from_file (&tc, filename);
1521     }
1522     break;
1523   case GNUNET_TESTBED_TOPOLOGY_SCALE_FREE:
1524     {
1525       uint16_t cap;
1526       uint8_t m;
1527       cap = (uint16_t) va_arg (vargs, unsigned int);
1528       m = (uint8_t) va_arg (vargs, unsigned int);
1529       gen_topo_scale_free (&tc, cap, m);
1530     }
1531     break;
1532   default:
1533     GNUNET_assert (0);
1534   }
1535   va_end (vargs);
1536   for (cnt = 0; cnt < tc.link_array_size; cnt++)
1537   {
1538     ulink = &underlay->link_array[cnt];
1539     if (GNUNET_SYSERR == proc (cls,
1540                                ulink->A,
1541                                ulink->B,
1542                                ulink->bandwidth,
1543                                ulink->latency,
1544                                ulink->loss))
1545     {
1546       ret = GNUNET_SYSERR;
1547       break;
1548     }
1549   }
1550   GNUNET_free_non_null (underlay->link_array);
1551   return ret;
1552 }
1553
1554 /* end of testbed_api_topology.c */