testing bugfix, add scale free topology
[oweals/gnunet.git] / src / testing / testing_group.c
1 /*
2       This file is part of GNUnet
3       (C) 2008, 2009 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 2, 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 testing/testing_group.c
23  * @brief convenience API for writing testcases for GNUnet
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_arm_service.h"
28 #include "gnunet_testing_lib.h"
29
30 #define VERBOSE_TESTING GNUNET_NO
31
32 /**
33  * Lowest port used for GNUnet testing.  Should be high enough to not
34  * conflict with other applications running on the hosts but be low
35  * enough to not conflict with client-ports (typically starting around
36  * 32k).
37  */
38 #define LOW_PORT 10000
39
40 /**
41  * Highest port used for GNUnet testing.  Should be low enough to not
42  * conflict with the port range for "local" ports (client apps; see
43  * /proc/sys/net/ipv4/ip_local_port_range on Linux for example).
44  */
45 #define HIGH_PORT 32000
46
47 #define MAX_OUTSTANDING_CONNECTIONS 50
48
49 #define CONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 160)
50
51 #define CONNECT_ATTEMPTS 8
52
53 struct PeerConnection
54 {
55   /*
56    * Linked list
57    */
58   struct PeerConnection *next;
59
60   /*
61    * Pointer to daemon handle
62    */
63   struct GNUNET_TESTING_Daemon *daemon;
64
65 };
66
67 /**
68  * Data we keep per peer.
69  */
70 struct PeerData
71 {
72   /**
73    * (Initial) configuration of the host.
74    * (initial because clients could change
75    *  it and we would not know about those
76    *  updates).
77    */
78   struct GNUNET_CONFIGURATION_Handle *cfg;
79
80   /**
81    * Handle for controlling the daemon.
82    */
83   struct GNUNET_TESTING_Daemon *daemon;
84
85   /**
86    * Linked list of peer connections (simply indexes of PeerGroup)
87    */
88   struct PeerConnection *connected_peers;
89
90   /**
91    * Total number of connections this peer has
92    */
93   int num_connections;
94 };
95
96
97 /**
98  * Data we keep per host.
99  */
100 struct HostData
101 {
102   /**
103    * Name of the host.
104    */
105   char *hostname;
106
107   /**
108    * Lowest port that we have not yet used
109    * for GNUnet.
110    */
111   uint16_t minport;
112 };
113
114
115 /**
116  * Handle to a group of GNUnet peers.
117  */
118 struct GNUNET_TESTING_PeerGroup
119 {
120   /**
121    * Our scheduler.
122    */
123   struct GNUNET_SCHEDULER_Handle *sched;
124
125   /**
126    * Configuration template.
127    */
128   const struct GNUNET_CONFIGURATION_Handle *cfg;
129
130   /**
131    * Function to call on each started daemon.
132    */
133   GNUNET_TESTING_NotifyDaemonRunning cb;
134
135   /**
136    * Closure for cb.
137    */
138   void *cb_cls;
139
140   /*
141    * Function to call on each topology connection created
142    */
143   GNUNET_TESTING_NotifyConnection notify_connection;
144
145   /*
146    * Callback for notify_connection
147    */
148   void *notify_connection_cls;
149
150   /**
151    * NULL-terminated array of information about
152    * hosts.
153    */
154   struct HostData *hosts;
155
156   /**
157    * Array of "total" peers.
158    */
159   struct PeerData *peers;
160
161   /**
162    * Number of peers in this group.
163    */
164   unsigned int total;
165
166 };
167
168
169 struct UpdateContext
170 {
171   struct GNUNET_CONFIGURATION_Handle *ret;
172   unsigned int nport;
173   const char *hostname;
174 };
175
176
177 struct ConnectContext
178 {
179   struct GNUNET_TESTING_Daemon *first;
180
181   struct GNUNET_TESTING_Daemon *second;
182
183   struct GNUNET_TESTING_PeerGroup *pg;
184 };
185
186 /**
187  * Number of connects we are waiting on, allows us to rate limit
188  * connect attempts.
189  */
190 static int outstanding_connects;
191
192
193 /**
194  * Function to iterate over options.  Copies
195  * the options to the target configuration,
196  * updating PORT values as needed.
197  *
198  * @param cls closure
199  * @param section name of the section
200  * @param option name of the option
201  * @param value value of the option
202  */
203 static void
204 update_config (void *cls,
205                const char *section, const char *option, const char *value)
206 {
207   struct UpdateContext *ctx = cls;
208   unsigned int ival;
209   char cval[12];
210
211   if ((0 == strcmp (option, "PORT")) && (1 == sscanf (value, "%u", &ival)))
212     {
213       GNUNET_snprintf (cval, sizeof (cval), "%u", ctx->nport++);
214       value = cval;
215     }
216
217   if ((0 == strcmp (option, "HOSTNAME")) && (ctx->hostname != NULL))
218     {
219       value = ctx->hostname;
220     }
221
222   GNUNET_CONFIGURATION_set_value_string (ctx->ret, section, option, value);
223 }
224
225
226 /**
227  * Create a new configuration using the given configuration
228  * as a template; however, each PORT in the existing cfg
229  * must be renumbered by incrementing "*port".  If we run
230  * out of "*port" numbers, return NULL.
231  *
232  * @param cfg template configuration
233  * @param port port numbers to use, update to reflect
234  *             port numbers that were used
235  * @param hostname hostname of the controlling host, to allow control connections from
236  *
237  * @return new configuration, NULL on error
238  */
239 static struct GNUNET_CONFIGURATION_Handle *
240 make_config (const struct GNUNET_CONFIGURATION_Handle *cfg, uint16_t * port, const char *hostname)
241 {
242   struct UpdateContext uc;
243   uint16_t orig;
244   char *control_host;
245   char *allowed_hosts;
246
247   orig = *port;
248   uc.nport = *port;
249   uc.ret = GNUNET_CONFIGURATION_create ();
250   uc.hostname = hostname;
251
252   GNUNET_CONFIGURATION_iterate (cfg, &update_config, &uc);
253   if (uc.nport >= HIGH_PORT)
254     {
255       *port = orig;
256       GNUNET_CONFIGURATION_destroy (uc.ret);
257       return NULL;
258     }
259
260   if (GNUNET_CONFIGURATION_get_value_string(cfg, "testing", "control_host", &control_host) == GNUNET_OK)
261     {
262       GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", control_host);
263       GNUNET_CONFIGURATION_set_value_string(uc.ret, "core", "ACCEPT_FROM", allowed_hosts);
264       GNUNET_free_non_null(control_host);
265       GNUNET_free(allowed_hosts);
266     }
267
268
269   /* arm needs to know to allow connections from the host on which it is running,
270    * otherwise gnunet-arm is unable to connect to it in some instances */
271   if (hostname != NULL)
272     {
273       GNUNET_asprintf(&allowed_hosts, "%s; 127.0.0.1;", hostname);
274       GNUNET_CONFIGURATION_set_value_string(uc.ret, "arm", "ACCEPT_FROM", allowed_hosts);
275       GNUNET_free(allowed_hosts);
276     }
277
278   *port = (uint16_t) uc.nport;
279   return uc.ret;
280 }
281
282 /*
283  * Add entries to the peers connected list
284  *
285  * @param pg the peer group we are working with
286  * @param first index of the first peer
287  * @param second index of the second peer
288  *
289  * @return the number of connections added (can be 0, 1 or 2)
290  *
291  * FIXME: add both, or only add one?
292  *      - if both are added, then we have to keep track
293  *        when connecting so we don't double connect
294  *      - if only one is added, we need to iterate over
295  *        both lists to find out if connection already exists
296  *      - having both allows the whitelisting/friend file
297  *        creation to be easier
298  *
299  *      -- For now, add both, we have to iterate over each to
300  *         check for duplicates anyways, so we'll take the performance
301  *         hit assuming we don't have __too__ many connections
302  *
303  */
304 static int
305 add_connections(struct GNUNET_TESTING_PeerGroup *pg, unsigned int first, unsigned int second)
306 {
307   int added;
308   struct PeerConnection *first_iter;
309   struct PeerConnection *second_iter;
310   int add_first;
311   int add_second;
312   struct PeerConnection *new_first;
313   struct PeerConnection *new_second;
314
315   first_iter = pg->peers[first].connected_peers;
316   add_first = GNUNET_YES;
317   while (first_iter != NULL)
318     {
319       if (first_iter->daemon == pg->peers[second].daemon)
320         add_first = GNUNET_NO;
321       first_iter = first_iter->next;
322     }
323
324   second_iter = pg->peers[second].connected_peers;
325   add_second = GNUNET_YES;
326   while (second_iter != NULL)
327     {
328       if (second_iter->daemon == pg->peers[first].daemon)
329         add_second = GNUNET_NO;
330       second_iter = second_iter->next;
331     }
332
333   added = 0;
334   if (add_first)
335     {
336       new_first = GNUNET_malloc(sizeof(struct PeerConnection));
337       new_first->daemon = pg->peers[second].daemon;
338       new_first->next = pg->peers[first].connected_peers;
339       pg->peers[first].connected_peers = new_first;
340       pg->peers[first].num_connections++;
341       added++;
342     }
343
344   if (add_second)
345     {
346       new_second = GNUNET_malloc(sizeof(struct PeerConnection));
347       new_second->daemon = pg->peers[first].daemon;
348       new_second->next = pg->peers[second].connected_peers;
349       pg->peers[second].connected_peers = new_second;
350       pg->peers[first].num_connections++;
351       added++;
352     }
353
354   return added;
355 }
356
357 static int
358 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
359 {
360
361   int total_connections;
362   int outer_count;
363   int i;
364   int previous_total_connections;
365   double random;
366   double probability;
367
368   GNUNET_assert(pg->total > 1);
369
370   /* Add a connection between the first two nodes */
371   total_connections = add_connections(pg, 0, 1);
372
373   for (outer_count = 1; outer_count < pg->total - 1; outer_count++)
374     {
375       previous_total_connections = total_connections;
376       for (i = 0; i < outer_count; i++)
377         {
378           probability = pg->peers[i].num_connections / previous_total_connections;
379           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
380                                                       (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
381           if (random < probability)
382             {
383 #if VERBOSE_TESTING
384               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
385                           "Connecting peer %d to peer %d\n",
386                           outer_count, i);
387 #endif
388               total_connections += add_connections(pg, outer_count, i);
389             }
390         }
391     }
392
393   return total_connections;
394 }
395
396 int
397 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
398 {
399   unsigned int i, j;
400   int nodeToConnect;
401   unsigned int natLog;
402   unsigned int randomPeer;
403   double random, logNModifier, percentage;
404   unsigned int smallWorldConnections;
405   int connsPerPeer;
406   char *p_string;
407   int max;
408   int min;
409   unsigned int useAnd;
410   int connect_attempts;
411
412   logNModifier = 0.5; /* FIXME: default value? */
413   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
414                                                          "TESTING",
415                                                          "LOGNMODIFIER",
416                                                          &p_string))
417     {
418       if (sscanf(p_string, "%lf", &logNModifier) != 1)
419         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
420                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
421                     p_string,
422                     "LOGNMODIFIER",
423                     "TESTING");
424       GNUNET_free (p_string);
425     }
426   percentage = 0.5; /* FIXME: default percentage? */
427   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
428                                                          "TESTING",
429                                                          "PERCENTAGE",
430                                                          &p_string))
431     {
432       if (sscanf(p_string, "%lf", &percentage) != 1)
433         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
434                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
435                     p_string,
436                     "PERCENTAGE",
437                     "TESTING");
438       GNUNET_free (p_string);
439     }
440   natLog = log (pg->total);
441   connsPerPeer = ceil (natLog * logNModifier);
442
443   if (connsPerPeer % 2 == 1)
444     connsPerPeer += 1;
445
446   smallWorldConnections = 0;
447   connect_attempts = 0;
448   for (i = 0; i < pg->total; i++)
449     {
450       useAnd = 0;
451       max = i + connsPerPeer / 2;
452       min = i - connsPerPeer / 2;
453
454       if (max > pg->total - 1)
455         {
456           max = max - pg->total;
457           useAnd = 1;
458         }
459
460       if (min < 0)
461         {
462           min = pg->total - 1 + min;
463           useAnd = 1;
464         }
465
466       for (j = 0; j < connsPerPeer / 2; j++)
467         {
468           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
469                                                       (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
470           if (random < percentage)
471             {
472               /* Connect to uniformly selected random peer */
473               randomPeer =
474                 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
475                                    pg->total);
476               while ((((randomPeer < max) && (randomPeer > min))
477                       && (useAnd == 0)) || (((randomPeer > min)
478                                              || (randomPeer < max))
479                                             && (useAnd == 1)))
480                 {
481                   randomPeer =
482                       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
483                                                          pg->total);
484                 }
485               smallWorldConnections +=
486                 add_connections (pg, i, randomPeer);
487             }
488           else
489             {
490               nodeToConnect = i + j + 1;
491               if (nodeToConnect > pg->total - 1)
492                 {
493                   nodeToConnect = nodeToConnect - pg->total;
494                 }
495               connect_attempts +=
496                 add_connections (pg, i, nodeToConnect);
497             }
498         }
499
500     }
501
502   connect_attempts += smallWorldConnections;
503
504   return connect_attempts;
505 }
506
507
508 static int
509 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
510 {
511   unsigned int outer_count, inner_count;
512   unsigned int cutoff;
513   int connect_attempts;
514   double nat_percentage;
515   char *p_string;
516
517   nat_percentage = 0.6; /* FIXME: default percentage? */
518   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
519                                                          "TESTING",
520                                                          "NATPERCENTAGE",
521                                                          &p_string))
522     {
523       if (sscanf(p_string, "%lf", &nat_percentage) != 1)
524         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
525                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
526                     p_string,
527                     "NATPERCENTAGE",
528                     "TESTING");
529       GNUNET_free (p_string);
530     }
531
532
533
534   cutoff = (unsigned int) (nat_percentage * pg->total);
535
536   connect_attempts = 0;
537
538   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
539     {
540       for (inner_count = outer_count + 1; inner_count < pg->total;
541            inner_count++)
542         {
543           if ((outer_count > cutoff) || (inner_count > cutoff))
544             {
545 #if VERBOSE_TESTING
546               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
547                           "Connecting peer %d to peer %d\n",
548                           outer_count, inner_count);
549 #endif
550               connect_attempts += add_connections(pg, outer_count, inner_count);
551             }
552         }
553     }
554
555   return connect_attempts;
556
557 }
558
559
560
561 static int
562 create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
563 {
564   unsigned int i, j, k;
565   unsigned int square;
566   unsigned int rows;
567   unsigned int cols;
568   unsigned int toggle = 1;
569   unsigned int nodeToConnect;
570   unsigned int natLog;
571   unsigned int node1Row;
572   unsigned int node1Col;
573   unsigned int node2Row;
574   unsigned int node2Col;
575   unsigned int distance;
576   double probability, random, percentage;
577   unsigned int smallWorldConnections;
578   char *p_string;
579   int connect_attempts;
580   square = floor (sqrt (pg->total));
581   rows = square;
582   cols = square;
583
584   percentage = 0.5; /* FIXME: default percentage? */
585   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
586                                                          "TESTING",
587                                                          "PERCENTAGE",
588                                                          &p_string))
589     {
590       if (sscanf(p_string, "%lf", &percentage) != 1)
591         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
592                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
593                     p_string,
594                     "PERCENTAGE",
595                     "TESTING");
596       GNUNET_free (p_string);
597     }
598   probability = 0.5; /* FIXME: default percentage? */
599   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
600                                                          "TESTING",
601                                                          "PROBABILITY",
602                                                          &p_string))
603     {
604       if (sscanf(p_string, "%lf", &probability) != 1)
605         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
606                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
607                     p_string,
608                     "PROBABILITY",
609                     "TESTING");
610       GNUNET_free (p_string);
611     }
612   if (square * square != pg->total)
613     {
614       while (rows * cols < pg->total)
615         {
616           if (toggle % 2 == 0)
617             rows++;
618           else
619             cols++;
620
621           toggle++;
622         }
623     }
624 #if VERBOSE_TESTING
625       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
626                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
627                   rows, cols);
628 #endif
629
630   connect_attempts = 0;
631   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
632    * to the node to its right and above.  Once this is over, we'll have our torus!
633    * Special case for the last node (if the rows and columns are not equal), connect
634    * to the first in the row to maintain topology.
635    */
636   for (i = 0; i < pg->total; i++)
637     {
638       /* First connect to the node to the right */
639       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
640         nodeToConnect = i + 1;
641       else if (i + 1 == pg->total)
642         nodeToConnect = rows * cols - cols;
643       else
644         nodeToConnect = i - cols + 1;
645
646       connect_attempts += add_connections (pg, i, nodeToConnect);
647
648       if (i < cols)
649         nodeToConnect = (rows * cols) - cols + i;
650       else
651         nodeToConnect = i - cols;
652
653       if (nodeToConnect < pg->total)
654         connect_attempts += add_connections (pg, i, nodeToConnect);
655     }
656   natLog = log (pg->total);
657 #if VERBOSE_TESTING > 2
658   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659               _("natural log of %d is %d, will run %d iterations\n"),
660              pg->total, natLog, (int) (natLog * percentage));
661   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
662 #endif
663   smallWorldConnections = 0;
664   for (i = 0; i < (int) (natLog * percentage); i++)
665     {
666       for (j = 0; j < pg->total; j++)
667         {
668           /* Determine the row and column of node at position j on the 2d torus */
669           node1Row = j / cols;
670           node1Col = j - (node1Row * cols);
671           for (k = 0; k < pg->total; k++)
672             {
673               /* Determine the row and column of node at position k on the 2d torus */
674               node2Row = k / cols;
675               node2Col = k - (node2Row * cols);
676               /* Simple Cartesian distance */
677               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
678               if (distance > 1)
679                 {
680                   /* Calculate probability as 1 over the square of the distance */
681                   probability = 1.0 / (distance * distance);
682                   /* Choose a random value between 0 and 1 */
683                   random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
684                                                               (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
685                   /* If random < probability, then connect the two nodes */
686                   if (random < probability)
687                     smallWorldConnections += add_connections (pg, j, k);
688
689                 }
690             }
691         }
692     }
693   connect_attempts += smallWorldConnections;
694 #if VERBOSE_TESTING > 2
695           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
696                       _("Total connections added for small world: %d!\n"),
697                       smallWorldConnections);
698 #endif
699   return connect_attempts;
700 }
701
702
703
704 static int
705 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
706 {
707   double temp_rand;
708   unsigned int outer_count;
709   unsigned int inner_count;
710   int connect_attempts;
711   double probability;
712   char *p_string;
713
714   probability = 0.5; /* FIXME: default percentage? */
715   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
716                                                          "TESTING",
717                                                          "PROBABILITY",
718                                                          &p_string))
719     {
720       if (sscanf(p_string, "%lf", &probability) != 1)
721         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
722                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
723                     p_string,
724                     "PROBABILITY",
725                     "TESTING");
726       GNUNET_free (p_string);
727     }
728   connect_attempts = 0;
729   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
730     {
731       for (inner_count = outer_count + 1; inner_count < pg->total;
732            inner_count++)
733         {
734           temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
735                                                          (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
736 #if VERBOSE_TESTING
737           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
738                       _("rand is %f probability is %f\n"), temp_rand,
739                       probability);
740 #endif
741           if (temp_rand < probability)
742             {
743               connect_attempts += add_connections (pg, outer_count, inner_count);
744             }
745         }
746     }
747
748   return connect_attempts;
749 }
750
751 static int
752 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
753 {
754   unsigned int i;
755   unsigned int square;
756   unsigned int rows;
757   unsigned int cols;
758   unsigned int toggle = 1;
759   unsigned int nodeToConnect;
760   int connect_attempts;
761
762   connect_attempts = 0;
763
764   square = floor (sqrt (pg->total));
765   rows = square;
766   cols = square;
767
768   if (square * square != pg->total)
769     {
770       while (rows * cols < pg->total)
771         {
772           if (toggle % 2 == 0)
773             rows++;
774           else
775             cols++;
776
777           toggle++;
778         }
779     }
780 #if VERBOSE_TESTING
781       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
783                   rows, cols);
784 #endif
785   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
786    * to the node to its right and above.  Once this is over, we'll have our torus!
787    * Special case for the last node (if the rows and columns are not equal), connect
788    * to the first in the row to maintain topology.
789    */
790   for (i = 0; i < pg->total; i++)
791     {
792       /* First connect to the node to the right */
793       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
794         nodeToConnect = i + 1;
795       else if (i + 1 == pg->total)
796         nodeToConnect = rows * cols - cols;
797       else
798         nodeToConnect = i - cols + 1;
799 #if VERBOSE_TESTING
800           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                       "Connecting peer %d to peer %d\n",
802                       i, nodeToConnect);
803 #endif
804       connect_attempts += add_connections(pg, i, nodeToConnect);
805
806       /* Second connect to the node immediately above */
807       if (i < cols)
808         nodeToConnect = (rows * cols) - cols + i;
809       else
810         nodeToConnect = i - cols;
811
812       if (nodeToConnect < pg->total)
813         {
814 #if VERBOSE_TESTING
815           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
816                       "Connecting peer %d to peer %d\n",
817                       i, nodeToConnect);
818 #endif
819           connect_attempts += add_connections(pg, i, nodeToConnect);
820         }
821
822     }
823
824   return connect_attempts;
825 }
826
827
828
829 static int
830 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
831 {
832   unsigned int outer_count;
833   unsigned int inner_count;
834   int connect_attempts;
835
836   connect_attempts = 0;
837
838   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
839     {
840       for (inner_count = outer_count + 1; inner_count < pg->total;
841            inner_count++)
842         {
843 #if VERBOSE_TESTING
844           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
845                       "Connecting peer %d to peer %d\n",
846                       outer_count, inner_count);
847 #endif
848           connect_attempts += add_connections(pg, outer_count, inner_count);
849         }
850     }
851
852   return connect_attempts;
853 }
854
855
856 static int
857 create_ring (struct GNUNET_TESTING_PeerGroup *pg)
858 {
859   unsigned int count;
860   int connect_attempts;
861
862   connect_attempts = 0;
863
864   /* Connect each peer to the next highest numbered peer */
865   for (count = 0; count < pg->total - 1; count++)
866     {
867 #if VERBOSE_TESTING
868           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869                       "Connecting peer %d to peer %d\n",
870                       count, count + 1);
871 #endif
872       connect_attempts += add_connections(pg, count, count + 1);
873     }
874
875   /* Connect the last peer to the first peer */
876   connect_attempts += add_connections(pg, pg->total - 1, 0);
877
878   return connect_attempts;
879 }
880
881
882 /*
883  * Create the friend files based on the PeerConnection's
884  * of each peer in the peer group, and copy the files
885  * to the appropriate place
886  *
887  * @param pg the peer group we are dealing with
888  */
889 static int
890 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
891 {
892   FILE *temp_friend_handle;
893   unsigned int pg_iter;
894   struct PeerConnection *connection_iter;
895   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
896   char *temp_service_path;
897   pid_t *pidarr;
898   char *arg;
899   struct GNUNET_PeerIdentity *temppeer;
900   char * mytemp;
901   enum GNUNET_OS_ProcessStatusType type;
902   unsigned long return_code;
903   int count;
904   int ret;
905   int max_wait = 10;
906
907   pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
908   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
909     {
910       mytemp = GNUNET_DISK_mktemp("friends");
911       temp_friend_handle = fopen (mytemp, "wt");
912       connection_iter = pg->peers[pg_iter].connected_peers;
913       while (connection_iter != NULL)
914         {
915           temppeer = &connection_iter->daemon->id;
916           GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
917           fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
918           connection_iter = connection_iter->next;
919         }
920
921       fclose(temp_friend_handle);
922
923       if (GNUNET_OK !=
924           GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
925         {
926           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
927                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
928                       "SERVICEHOME",
929                       "PATHS");
930           if (UNLINK (mytemp) != 0)
931             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
932           GNUNET_free (mytemp);
933           break;
934         }
935
936       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
937         {
938           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
939           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
940                                          "mv", mytemp, arg, NULL);
941 #if VERBOSE_TESTING
942           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
943                       _("Copying file with command cp %s %s\n"), mytemp, arg);
944 #endif
945
946           GNUNET_free(arg);
947         }
948       else /* Remote, scp the file to the correct place */
949         {
950           if (NULL != pg->peers[pg_iter].daemon->username)
951             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
952           else
953             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
954           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
955                                          "scp", mytemp, arg, NULL);
956
957 #if VERBOSE_TESTING
958           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
959                       _("Copying file with command scp %s %s\n"), mytemp, arg);
960 #endif
961           GNUNET_free(arg);
962         }
963       GNUNET_free (temp_service_path);
964       GNUNET_free (mytemp);
965     }
966
967   count = 0;
968   ret = GNUNET_SYSERR;
969   while ((count < max_wait) && (ret != GNUNET_OK))
970     {
971       ret = GNUNET_OK;
972       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
973         {
974 #if VERBOSE_TESTING
975           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
976                       _("Checking copy status of file %d\n"), pg_iter);
977 #endif
978           if (pidarr[pg_iter] != 0) /* Check for already completed! */
979             {
980               if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
981                 {
982                   ret = GNUNET_SYSERR;
983                 }
984               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
985                 {
986                   ret = GNUNET_SYSERR;
987                 }
988               else
989                 {
990                   pidarr[pg_iter] = 0;
991 #if VERBOSE_TESTING
992             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
993                       _("File %d copied\n"), pg_iter);
994 #endif
995                 }
996             }
997         }
998       count++;
999       if (ret == GNUNET_SYSERR)
1000         {
1001           sleep(1);
1002         }
1003     }
1004
1005 #if VERBOSE_TESTING
1006     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1007                 _("Finished copying all friend files!\n"));
1008 #endif
1009   GNUNET_free(pidarr);
1010   return ret;
1011 }
1012
1013 /**
1014  * Internal notification of a connection, kept so that we can ensure some connections
1015  * happen instead of flooding all testing daemons with requests to connect.
1016  */
1017 static void internal_connect_notify (void *cls,
1018                                      const struct GNUNET_PeerIdentity *first,
1019                                      const struct GNUNET_PeerIdentity *second,
1020                                      const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1021                                      const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1022                                      struct GNUNET_TESTING_Daemon *first_daemon,
1023                                      struct GNUNET_TESTING_Daemon *second_daemon,
1024                                      const char *emsg)
1025 {
1026   struct GNUNET_TESTING_PeerGroup *pg = cls;
1027   outstanding_connects--;
1028
1029   pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
1030
1031 }
1032
1033 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1034 {
1035   struct ConnectContext *connect_context = cls;
1036
1037   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1038     return;
1039
1040   if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1041     {
1042 #if VERBOSE_TESTING > 2
1043           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1044                       _("Delaying connect, we have too many outstanding connections!\n"));
1045 #endif
1046       GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1047     }
1048   else
1049     {
1050 #if VERBOSE_TESTING > 2
1051           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1052                       _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1053 #endif
1054       outstanding_connects++;
1055       GNUNET_TESTING_daemons_connect (connect_context->first,
1056                                       connect_context->second,
1057                                       CONNECT_TIMEOUT,
1058                                       CONNECT_ATTEMPTS,
1059                                       &internal_connect_notify,
1060                                       connect_context->pg);
1061       GNUNET_free(connect_context);
1062     }
1063 }
1064
1065 /*
1066  * Connect the topology as specified by the PeerConnection's
1067  * of each peer in the peer group
1068  *
1069  * @param pg the peer group we are dealing with
1070  */
1071 static void
1072 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1073 {
1074   unsigned int pg_iter;
1075   struct PeerConnection *connection_iter;
1076   struct ConnectContext *connect_context;
1077
1078   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1079     {
1080       connection_iter = pg->peers[pg_iter].connected_peers;
1081       while (connection_iter != NULL)
1082         {
1083           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1084           connect_context->pg = pg;
1085           connect_context->first = pg->peers[pg_iter].daemon;
1086           connect_context->second = connection_iter->daemon;
1087           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1088           connection_iter = connection_iter->next;
1089         }
1090     }
1091 }
1092
1093
1094 /*
1095  * Takes a peer group and attempts to create a topology based on the
1096  * one specified in the configuration file.  Returns the number of connections
1097  * that will attempt to be created, but this will happen asynchronously(?) so
1098  * the caller will have to keep track (via the callback) of whether or not
1099  * the connection actually happened.
1100  *
1101  * @param pg the peer group struct representing the running peers
1102  *
1103  * @return the number of connections should be created by the topology, so the
1104  * caller knows how many to wait for (if it so chooses)
1105  *
1106  */
1107 int
1108 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg)
1109 {
1110   unsigned long long topology_num;
1111   int ret;
1112   int num_connections;
1113
1114   GNUNET_assert (pg->notify_connection != NULL);
1115   ret = GNUNET_OK;
1116   if (GNUNET_YES ==
1117       GNUNET_CONFIGURATION_get_value_number (pg->cfg, "testing", "topology",
1118                                              &topology_num))
1119     {
1120       switch (topology_num)
1121         {
1122         case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1123 #if VERBOSE_TESTING
1124           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1125                       _("Creating clique topology\n"));
1126 #endif
1127           num_connections = create_clique (pg);
1128           break;
1129         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1130 #if VERBOSE_TESTING
1131           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1132                       _("Creating small world (ring) topology\n"));
1133 #endif
1134           num_connections = create_small_world_ring (pg);
1135           break;
1136         case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1137 #if VERBOSE_TESTING
1138           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1139                       _("Creating small world (2d-torus) topology\n"));
1140 #endif
1141           num_connections = create_small_world (pg);
1142           break;
1143         case GNUNET_TESTING_TOPOLOGY_RING:
1144 #if VERBOSE_TESTING
1145           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1146                       _("Creating ring topology\n"));
1147 #endif
1148           num_connections = create_ring (pg);
1149           break;
1150         case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1151 #if VERBOSE_TESTING
1152           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1153                       _("Creating 2d torus topology\n"));
1154 #endif
1155           num_connections = create_2d_torus (pg);
1156           break;
1157         case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1158 #if VERBOSE_TESTING
1159           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1160                       _("Creating Erdos-Renyi topology\n"));
1161 #endif
1162           num_connections = create_erdos_renyi (pg);
1163           break;
1164         case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1165 #if VERBOSE_TESTING
1166           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1167                       _("Creating InterNAT topology\n"));
1168 #endif
1169           num_connections = create_nated_internet (pg);
1170           break;
1171         case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1172 #if VERBOSE_TESTING
1173           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1174                       _("Creating Scale Free topology\n"));
1175 #endif
1176           num_connections = create_scale_free (pg);
1177           break;
1178         case GNUNET_TESTING_TOPOLOGY_NONE:
1179           num_connections = 0;
1180           break;
1181         default:
1182           num_connections = 0;
1183           break;
1184         }
1185       if (num_connections < 1)
1186         return GNUNET_SYSERR;
1187
1188       if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1189         ret = create_and_copy_friend_files(pg);
1190       if (ret == GNUNET_OK)
1191         connect_topology(pg);
1192       else
1193         {
1194 #if VERBOSE_TESTING
1195           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1196                       _("Failed during friend file copying!\n"));
1197 #endif
1198           return GNUNET_SYSERR;
1199         }
1200     }
1201   else
1202     {
1203       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1204                   _("No topology specified, was one intended?\n"));
1205       return GNUNET_SYSERR;
1206     }
1207
1208   return num_connections;
1209 }
1210
1211 /**
1212  * Start count gnunetd processes with the same set of transports and
1213  * applications.  The port numbers (any option called "PORT") will be
1214  * adjusted to ensure that no two peers running on the same system
1215  * have the same port(s) in their respective configurations.
1216  *
1217  * @param sched scheduler to use
1218  * @param cfg configuration template to use
1219  * @param total number of daemons to start
1220  * @param cb function to call on each daemon that was started
1221  * @param cb_cls closure for cb
1222  * @param connect_callback function to call each time two hosts are connected
1223  * @param connect_callback_cls closure for connect_callback
1224  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
1225  *        everything on localhost).
1226  * @return NULL on error, otherwise handle to control peer group
1227  */
1228 struct GNUNET_TESTING_PeerGroup *
1229 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
1230                               const struct GNUNET_CONFIGURATION_Handle *cfg,
1231                               unsigned int total,
1232                               GNUNET_TESTING_NotifyDaemonRunning cb,
1233                               void *cb_cls,
1234                               GNUNET_TESTING_NotifyConnection
1235                               connect_callback, void *connect_callback_cls,
1236                               const char *hostnames)
1237 {
1238   struct GNUNET_TESTING_PeerGroup *pg;
1239   const char *rpos;
1240   char *pos;
1241   char *start;
1242   const char *hostname;
1243   char *baseservicehome;
1244   char *newservicehome;
1245   char *tmpdir;
1246   struct GNUNET_CONFIGURATION_Handle *pcfg;
1247   unsigned int off;
1248   unsigned int hostcnt;
1249   uint16_t minport;
1250
1251   if (0 == total)
1252     {
1253       GNUNET_break (0);
1254       return NULL;
1255     }
1256
1257   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
1258   pg->sched = sched;
1259   pg->cfg = cfg;
1260   pg->cb = cb;
1261   pg->cb_cls = cb_cls;
1262   pg->notify_connection = connect_callback;
1263   pg->notify_connection_cls = connect_callback_cls;
1264   pg->total = total;
1265   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
1266   if (NULL != hostnames)
1267     {
1268       off = 2;
1269       /* skip leading spaces */
1270       while ((0 != *hostnames) && (isspace (*hostnames)))
1271         hostnames++;
1272       rpos = hostnames;
1273       while ('\0' != *rpos)
1274         {
1275           if (isspace (*rpos))
1276             off++;
1277           rpos++;
1278         }
1279       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
1280       off = 0;
1281       start = GNUNET_strdup (hostnames);
1282       pos = start;
1283       while ('\0' != *pos)
1284         {
1285           if (isspace (*pos))
1286             {
1287               *pos = '\0';
1288               if (strlen (start) > 0)
1289                 {
1290                   pg->hosts[off].minport = LOW_PORT;
1291                   pg->hosts[off++].hostname = start;
1292                 }
1293               start = pos + 1;
1294             }
1295           pos++;
1296         }
1297       if (strlen (start) > 0)
1298         {
1299           pg->hosts[off].minport = LOW_PORT;
1300           pg->hosts[off++].hostname = start;
1301         }
1302       if (off == 0)
1303         {
1304           GNUNET_free (start);
1305           GNUNET_free (pg->hosts);
1306           pg->hosts = NULL;
1307         }
1308       hostcnt = off;
1309       minport = 0;              /* make gcc happy */
1310     }
1311   else
1312     {
1313       hostcnt = 0;
1314       minport = LOW_PORT;
1315     }
1316   for (off = 0; off < total; off++)
1317     {
1318       if (hostcnt > 0)
1319         {
1320           hostname = pg->hosts[off % hostcnt].hostname;
1321           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
1322         }
1323       else
1324         {
1325           hostname = NULL;
1326           pcfg = make_config (cfg, &minport, hostname);
1327         }
1328
1329       if (NULL == pcfg)
1330         {
1331           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1332                       _
1333                       ("Could not create configuration for peer number %u on `%s'!\n"),
1334                       off, hostname == NULL ? "localhost" : hostname);
1335           continue;
1336         }
1337
1338       if (GNUNET_YES ==
1339           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
1340                                                  &baseservicehome))
1341         {
1342           GNUNET_asprintf (&newservicehome,
1343                            "%s/%d/", baseservicehome, off);
1344           GNUNET_free (baseservicehome);
1345         }
1346       else
1347         {
1348           tmpdir = getenv ("TMPDIR");
1349           tmpdir = tmpdir ? tmpdir : "/tmp";
1350           GNUNET_asprintf (&newservicehome,
1351                            "%s/%s/%d/",
1352                            tmpdir,
1353                            "gnunet-testing-test-test", off);
1354         }
1355       GNUNET_CONFIGURATION_set_value_string (pcfg,
1356                                              "PATHS",
1357                                              "SERVICEHOME", newservicehome);
1358       GNUNET_free (newservicehome);
1359       pg->peers[off].cfg = pcfg;
1360       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
1361                                                            pcfg,
1362                                                            hostname,
1363                                                            cb, cb_cls);
1364       if (NULL == pg->peers[off].daemon)
1365         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1366                     _("Could not start peer number %u!\n"), off);
1367     }
1368   return pg;
1369 }
1370
1371 /*
1372  * Get a daemon by number, so callers don't have to do nasty
1373  * offsetting operation.
1374  */
1375 struct GNUNET_TESTING_Daemon *
1376 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
1377 {
1378   if (position < pg->total)
1379     return pg->peers[position].daemon;
1380   else
1381     return NULL;
1382 }
1383
1384 /**
1385  * Shutdown all peers started in the given group.
1386  *
1387  * @param pg handle to the peer group
1388  */
1389 void
1390 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
1391 {
1392   unsigned int off;
1393   struct PeerConnection *pos;
1394   struct PeerConnection *next;
1395
1396   for (off = 0; off < pg->total; off++)
1397     {
1398       /* FIXME: should we wait for our
1399          continuations to be called here? This
1400          would require us to take a continuation
1401          as well... */
1402
1403       if (NULL != pg->peers[off].daemon)
1404         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
1405       if (NULL != pg->peers[off].cfg)
1406         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
1407
1408       pos = pg->peers[off].connected_peers;
1409       while (pos != NULL)
1410         {
1411           next = pos->next;
1412           GNUNET_free(pos);
1413           pos = next;
1414         }
1415
1416     }
1417   GNUNET_free (pg->peers);
1418   if (NULL != pg->hosts)
1419     {
1420       GNUNET_free (pg->hosts[0].hostname);
1421       GNUNET_free (pg->hosts);
1422     }
1423   GNUNET_free (pg);
1424 }
1425
1426
1427 /* end of testing_group.c */