minor testing changes
[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_YES
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 /**
358  * Scale free network construction as described in:
359  *
360  * "Emergence of Scaling in Random Networks." Science 286, 509-512, 1999.
361  *
362  * Start with a network of "one" peer, then progressively add
363  * peers up to the total number.  At each step, iterate over
364  * all possible peers and connect new peer based on number of
365  * existing connections of the target peer.
366  *
367  * @param pg the peer group we are dealing with
368  *
369  * @return the number of connections created
370  */
371 static int
372 create_scale_free (struct GNUNET_TESTING_PeerGroup *pg)
373 {
374
375   unsigned int total_connections;
376   unsigned int outer_count;
377   unsigned int i;
378   unsigned int previous_total_connections;
379   double random;
380   double probability;
381
382   GNUNET_assert(pg->total > 1);
383
384   /* Add a connection between the first two nodes */
385   total_connections = add_connections(pg, 0, 1);
386
387   for (outer_count = 1; outer_count < pg->total; outer_count++)
388     {
389       previous_total_connections = total_connections;
390       for (i = 0; i < outer_count; i++)
391         {
392           probability = pg->peers[i].num_connections / (double)previous_total_connections;
393           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
394                                                       (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
395 #if VERBOSE_TESTING
396           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
397                       "Considering connecting peer %d to peer %d\n",
398                       outer_count, i);
399 #endif
400           if (random < probability)
401             {
402 #if VERBOSE_TESTING
403               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
404                           "Connecting peer %d to peer %d\n",
405                           outer_count, i);
406 #endif
407               total_connections += add_connections(pg, outer_count, i);
408             }
409         }
410     }
411
412   return total_connections;
413 }
414
415 int
416 create_small_world_ring(struct GNUNET_TESTING_PeerGroup *pg)
417 {
418   unsigned int i, j;
419   int nodeToConnect;
420   unsigned int natLog;
421   unsigned int randomPeer;
422   double random, logNModifier, percentage;
423   unsigned int smallWorldConnections;
424   int connsPerPeer;
425   char *p_string;
426   int max;
427   int min;
428   unsigned int useAnd;
429   int connect_attempts;
430
431   logNModifier = 0.5; /* FIXME: default value? */
432   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
433                                                          "TESTING",
434                                                          "LOGNMODIFIER",
435                                                          &p_string))
436     {
437       if (sscanf(p_string, "%lf", &logNModifier) != 1)
438         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
439                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
440                     p_string,
441                     "LOGNMODIFIER",
442                     "TESTING");
443       GNUNET_free (p_string);
444     }
445   percentage = 0.5; /* FIXME: default percentage? */
446   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
447                                                          "TESTING",
448                                                          "PERCENTAGE",
449                                                          &p_string))
450     {
451       if (sscanf(p_string, "%lf", &percentage) != 1)
452         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
453                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
454                     p_string,
455                     "PERCENTAGE",
456                     "TESTING");
457       GNUNET_free (p_string);
458     }
459   natLog = log (pg->total);
460   connsPerPeer = ceil (natLog * logNModifier);
461
462   if (connsPerPeer % 2 == 1)
463     connsPerPeer += 1;
464
465   smallWorldConnections = 0;
466   connect_attempts = 0;
467   for (i = 0; i < pg->total; i++)
468     {
469       useAnd = 0;
470       max = i + connsPerPeer / 2;
471       min = i - connsPerPeer / 2;
472
473       if (max > pg->total - 1)
474         {
475           max = max - pg->total;
476           useAnd = 1;
477         }
478
479       if (min < 0)
480         {
481           min = pg->total - 1 + min;
482           useAnd = 1;
483         }
484
485       for (j = 0; j < connsPerPeer / 2; j++)
486         {
487           random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
488                                                       (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
489           if (random < percentage)
490             {
491               /* Connect to uniformly selected random peer */
492               randomPeer =
493                 GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
494                                    pg->total);
495               while ((((randomPeer < max) && (randomPeer > min))
496                       && (useAnd == 0)) || (((randomPeer > min)
497                                              || (randomPeer < max))
498                                             && (useAnd == 1)))
499                 {
500                   randomPeer =
501                       GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
502                                                          pg->total);
503                 }
504               smallWorldConnections +=
505                 add_connections (pg, i, randomPeer);
506             }
507           else
508             {
509               nodeToConnect = i + j + 1;
510               if (nodeToConnect > pg->total - 1)
511                 {
512                   nodeToConnect = nodeToConnect - pg->total;
513                 }
514               connect_attempts +=
515                 add_connections (pg, i, nodeToConnect);
516             }
517         }
518
519     }
520
521   connect_attempts += smallWorldConnections;
522
523   return connect_attempts;
524 }
525
526
527 static int
528 create_nated_internet (struct GNUNET_TESTING_PeerGroup *pg)
529 {
530   unsigned int outer_count, inner_count;
531   unsigned int cutoff;
532   int connect_attempts;
533   double nat_percentage;
534   char *p_string;
535
536   nat_percentage = 0.6; /* FIXME: default percentage? */
537   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
538                                                          "TESTING",
539                                                          "NATPERCENTAGE",
540                                                          &p_string))
541     {
542       if (sscanf(p_string, "%lf", &nat_percentage) != 1)
543         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
544                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
545                     p_string,
546                     "NATPERCENTAGE",
547                     "TESTING");
548       GNUNET_free (p_string);
549     }
550
551
552
553   cutoff = (unsigned int) (nat_percentage * pg->total);
554
555   connect_attempts = 0;
556
557   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
558     {
559       for (inner_count = outer_count + 1; inner_count < pg->total;
560            inner_count++)
561         {
562           if ((outer_count > cutoff) || (inner_count > cutoff))
563             {
564 #if VERBOSE_TESTING
565               GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
566                           "Connecting peer %d to peer %d\n",
567                           outer_count, inner_count);
568 #endif
569               connect_attempts += add_connections(pg, outer_count, inner_count);
570             }
571         }
572     }
573
574   return connect_attempts;
575
576 }
577
578
579
580 static int
581 create_small_world (struct GNUNET_TESTING_PeerGroup *pg)
582 {
583   unsigned int i, j, k;
584   unsigned int square;
585   unsigned int rows;
586   unsigned int cols;
587   unsigned int toggle = 1;
588   unsigned int nodeToConnect;
589   unsigned int natLog;
590   unsigned int node1Row;
591   unsigned int node1Col;
592   unsigned int node2Row;
593   unsigned int node2Col;
594   unsigned int distance;
595   double probability, random, percentage;
596   unsigned int smallWorldConnections;
597   char *p_string;
598   int connect_attempts;
599   square = floor (sqrt (pg->total));
600   rows = square;
601   cols = square;
602
603   percentage = 0.5; /* FIXME: default percentage? */
604   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
605                                                          "TESTING",
606                                                          "PERCENTAGE",
607                                                          &p_string))
608     {
609       if (sscanf(p_string, "%lf", &percentage) != 1)
610         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
611                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
612                     p_string,
613                     "PERCENTAGE",
614                     "TESTING");
615       GNUNET_free (p_string);
616     }
617   probability = 0.5; /* FIXME: default percentage? */
618   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
619                                                          "TESTING",
620                                                          "PROBABILITY",
621                                                          &p_string))
622     {
623       if (sscanf(p_string, "%lf", &probability) != 1)
624         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
625                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
626                     p_string,
627                     "PROBABILITY",
628                     "TESTING");
629       GNUNET_free (p_string);
630     }
631   if (square * square != pg->total)
632     {
633       while (rows * cols < pg->total)
634         {
635           if (toggle % 2 == 0)
636             rows++;
637           else
638             cols++;
639
640           toggle++;
641         }
642     }
643 #if VERBOSE_TESTING
644       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
645                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
646                   rows, cols);
647 #endif
648
649   connect_attempts = 0;
650   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
651    * to the node to its right and above.  Once this is over, we'll have our torus!
652    * Special case for the last node (if the rows and columns are not equal), connect
653    * to the first in the row to maintain topology.
654    */
655   for (i = 0; i < pg->total; i++)
656     {
657       /* First connect to the node to the right */
658       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
659         nodeToConnect = i + 1;
660       else if (i + 1 == pg->total)
661         nodeToConnect = rows * cols - cols;
662       else
663         nodeToConnect = i - cols + 1;
664
665       connect_attempts += add_connections (pg, i, nodeToConnect);
666
667       if (i < cols)
668         nodeToConnect = (rows * cols) - cols + i;
669       else
670         nodeToConnect = i - cols;
671
672       if (nodeToConnect < pg->total)
673         connect_attempts += add_connections (pg, i, nodeToConnect);
674     }
675   natLog = log (pg->total);
676 #if VERBOSE_TESTING > 2
677   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
678               _("natural log of %d is %d, will run %d iterations\n"),
679              pg->total, natLog, (int) (natLog * percentage));
680   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Total connections added thus far: %u!\n"), connect_attempts);
681 #endif
682   smallWorldConnections = 0;
683   for (i = 0; i < (int) (natLog * percentage); i++)
684     {
685       for (j = 0; j < pg->total; j++)
686         {
687           /* Determine the row and column of node at position j on the 2d torus */
688           node1Row = j / cols;
689           node1Col = j - (node1Row * cols);
690           for (k = 0; k < pg->total; k++)
691             {
692               /* Determine the row and column of node at position k on the 2d torus */
693               node2Row = k / cols;
694               node2Col = k - (node2Row * cols);
695               /* Simple Cartesian distance */
696               distance = abs (node1Row - node2Row) + abs (node1Col - node2Col);
697               if (distance > 1)
698                 {
699                   /* Calculate probability as 1 over the square of the distance */
700                   probability = 1.0 / (distance * distance);
701                   /* Choose a random value between 0 and 1 */
702                   random = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
703                                                               (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
704                   /* If random < probability, then connect the two nodes */
705                   if (random < probability)
706                     smallWorldConnections += add_connections (pg, j, k);
707
708                 }
709             }
710         }
711     }
712   connect_attempts += smallWorldConnections;
713 #if VERBOSE_TESTING > 2
714           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
715                       _("Total connections added for small world: %d!\n"),
716                       smallWorldConnections);
717 #endif
718   return connect_attempts;
719 }
720
721
722
723 static int
724 create_erdos_renyi (struct GNUNET_TESTING_PeerGroup *pg)
725 {
726   double temp_rand;
727   unsigned int outer_count;
728   unsigned int inner_count;
729   int connect_attempts;
730   double probability;
731   char *p_string;
732
733   probability = 0.5; /* FIXME: default percentage? */
734   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string(pg->cfg,
735                                                          "TESTING",
736                                                          "PROBABILITY",
737                                                          &p_string))
738     {
739       if (sscanf(p_string, "%lf", &probability) != 1)
740         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
741                     _("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
742                     p_string,
743                     "PROBABILITY",
744                     "TESTING");
745       GNUNET_free (p_string);
746     }
747   connect_attempts = 0;
748   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
749     {
750       for (inner_count = outer_count + 1; inner_count < pg->total;
751            inner_count++)
752         {
753           temp_rand = ((double) GNUNET_CRYPTO_random_u64(GNUNET_CRYPTO_QUALITY_WEAK,
754                                                          (uint64_t)-1LL)) / ( (double) (uint64_t) -1LL);
755 #if VERBOSE_TESTING
756           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
757                       _("rand is %f probability is %f\n"), temp_rand,
758                       probability);
759 #endif
760           if (temp_rand < probability)
761             {
762               connect_attempts += add_connections (pg, outer_count, inner_count);
763             }
764         }
765     }
766
767   return connect_attempts;
768 }
769
770 static int
771 create_2d_torus (struct GNUNET_TESTING_PeerGroup *pg)
772 {
773   unsigned int i;
774   unsigned int square;
775   unsigned int rows;
776   unsigned int cols;
777   unsigned int toggle = 1;
778   unsigned int nodeToConnect;
779   int connect_attempts;
780
781   connect_attempts = 0;
782
783   square = floor (sqrt (pg->total));
784   rows = square;
785   cols = square;
786
787   if (square * square != pg->total)
788     {
789       while (rows * cols < pg->total)
790         {
791           if (toggle % 2 == 0)
792             rows++;
793           else
794             cols++;
795
796           toggle++;
797         }
798     }
799 #if VERBOSE_TESTING
800       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
801                   _("Connecting nodes in 2d torus topology: %u rows %u columns\n"),
802                   rows, cols);
803 #endif
804   /* Rows and columns are all sorted out, now iterate over all nodes and connect each
805    * to the node to its right and above.  Once this is over, we'll have our torus!
806    * Special case for the last node (if the rows and columns are not equal), connect
807    * to the first in the row to maintain topology.
808    */
809   for (i = 0; i < pg->total; i++)
810     {
811       /* First connect to the node to the right */
812       if (((i + 1) % cols != 0) && (i + 1 != pg->total))
813         nodeToConnect = i + 1;
814       else if (i + 1 == pg->total)
815         nodeToConnect = rows * cols - cols;
816       else
817         nodeToConnect = i - cols + 1;
818 #if VERBOSE_TESTING
819           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820                       "Connecting peer %d to peer %d\n",
821                       i, nodeToConnect);
822 #endif
823       connect_attempts += add_connections(pg, i, nodeToConnect);
824
825       /* Second connect to the node immediately above */
826       if (i < cols)
827         nodeToConnect = (rows * cols) - cols + i;
828       else
829         nodeToConnect = i - cols;
830
831       if (nodeToConnect < pg->total)
832         {
833 #if VERBOSE_TESTING
834           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
835                       "Connecting peer %d to peer %d\n",
836                       i, nodeToConnect);
837 #endif
838           connect_attempts += add_connections(pg, i, nodeToConnect);
839         }
840
841     }
842
843   return connect_attempts;
844 }
845
846
847
848 static int
849 create_clique (struct GNUNET_TESTING_PeerGroup *pg)
850 {
851   unsigned int outer_count;
852   unsigned int inner_count;
853   int connect_attempts;
854
855   connect_attempts = 0;
856
857   for (outer_count = 0; outer_count < pg->total - 1; outer_count++)
858     {
859       for (inner_count = outer_count + 1; inner_count < pg->total;
860            inner_count++)
861         {
862 #if VERBOSE_TESTING
863           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864                       "Connecting peer %d to peer %d\n",
865                       outer_count, inner_count);
866 #endif
867           connect_attempts += add_connections(pg, outer_count, inner_count);
868         }
869     }
870
871   return connect_attempts;
872 }
873
874
875 static int
876 create_ring (struct GNUNET_TESTING_PeerGroup *pg)
877 {
878   unsigned int count;
879   int connect_attempts;
880
881   connect_attempts = 0;
882
883   /* Connect each peer to the next highest numbered peer */
884   for (count = 0; count < pg->total - 1; count++)
885     {
886 #if VERBOSE_TESTING
887           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
888                       "Connecting peer %d to peer %d\n",
889                       count, count + 1);
890 #endif
891       connect_attempts += add_connections(pg, count, count + 1);
892     }
893
894   /* Connect the last peer to the first peer */
895   connect_attempts += add_connections(pg, pg->total - 1, 0);
896
897   return connect_attempts;
898 }
899
900
901 /*
902  * Create the friend files based on the PeerConnection's
903  * of each peer in the peer group, and copy the files
904  * to the appropriate place
905  *
906  * @param pg the peer group we are dealing with
907  */
908 static int
909 create_and_copy_friend_files (struct GNUNET_TESTING_PeerGroup *pg)
910 {
911   FILE *temp_friend_handle;
912   unsigned int pg_iter;
913   struct PeerConnection *connection_iter;
914   struct GNUNET_CRYPTO_HashAsciiEncoded peer_enc;
915   char *temp_service_path;
916   pid_t *pidarr;
917   char *arg;
918   struct GNUNET_PeerIdentity *temppeer;
919   char * mytemp;
920   enum GNUNET_OS_ProcessStatusType type;
921   unsigned long return_code;
922   int count;
923   int ret;
924   int max_wait = 10;
925
926   pidarr = GNUNET_malloc(sizeof(pid_t) * pg->total);
927   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
928     {
929       mytemp = GNUNET_DISK_mktemp("friends");
930       temp_friend_handle = fopen (mytemp, "wt");
931       connection_iter = pg->peers[pg_iter].connected_peers;
932       while (connection_iter != NULL)
933         {
934           temppeer = &connection_iter->daemon->id;
935           GNUNET_CRYPTO_hash_to_enc(&temppeer->hashPubKey, &peer_enc);
936           fprintf(temp_friend_handle, "%s\n", (char *)&peer_enc);
937           connection_iter = connection_iter->next;
938         }
939
940       fclose(temp_friend_handle);
941
942       if (GNUNET_OK !=
943           GNUNET_CONFIGURATION_get_value_string(pg->peers[pg_iter].daemon->cfg, "PATHS", "SERVICEHOME", &temp_service_path))
944         {
945           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
946                       _("No `%s' specified in peer configuration in section `%s', cannot copy friends file!\n"),
947                       "SERVICEHOME",
948                       "PATHS");
949           if (UNLINK (mytemp) != 0)
950             GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", mytemp);
951           GNUNET_free (mytemp);
952           break;
953         }
954
955       if (pg->peers[pg_iter].daemon->hostname == NULL) /* Local, just copy the file */
956         {
957           GNUNET_asprintf (&arg, "%s/friends", temp_service_path);
958           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "mv",
959                                          "mv", mytemp, arg, NULL);
960 #if VERBOSE_TESTING
961           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
962                       _("Copying file with command cp %s %s\n"), mytemp, arg);
963 #endif
964
965           GNUNET_free(arg);
966         }
967       else /* Remote, scp the file to the correct place */
968         {
969           if (NULL != pg->peers[pg_iter].daemon->username)
970             GNUNET_asprintf (&arg, "%s@%s:%s/friends", pg->peers[pg_iter].daemon->username, pg->peers[pg_iter].daemon->hostname, temp_service_path);
971           else
972             GNUNET_asprintf (&arg, "%s:%s/friends", pg->peers[pg_iter].daemon->hostname, temp_service_path);
973           pidarr[pg_iter] = GNUNET_OS_start_process (NULL, NULL, "scp",
974                                          "scp", mytemp, arg, NULL);
975
976 #if VERBOSE_TESTING
977           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
978                       _("Copying file with command scp %s %s\n"), mytemp, arg);
979 #endif
980           GNUNET_free(arg);
981         }
982       GNUNET_free (temp_service_path);
983       GNUNET_free (mytemp);
984     }
985
986   count = 0;
987   ret = GNUNET_SYSERR;
988   while ((count < max_wait) && (ret != GNUNET_OK))
989     {
990       ret = GNUNET_OK;
991       for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
992         {
993 #if VERBOSE_TESTING
994           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
995                       _("Checking copy status of file %d\n"), pg_iter);
996 #endif
997           if (pidarr[pg_iter] != 0) /* Check for already completed! */
998             {
999               if (GNUNET_OS_process_status(pidarr[pg_iter], &type, &return_code) != GNUNET_OK)
1000                 {
1001                   ret = GNUNET_SYSERR;
1002                 }
1003               else if ((type != GNUNET_OS_PROCESS_EXITED) || (return_code != 0))
1004                 {
1005                   ret = GNUNET_SYSERR;
1006                 }
1007               else
1008                 {
1009                   pidarr[pg_iter] = 0;
1010 #if VERBOSE_TESTING
1011             GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1012                       _("File %d copied\n"), pg_iter);
1013 #endif
1014                 }
1015             }
1016         }
1017       count++;
1018       if (ret == GNUNET_SYSERR)
1019         {
1020           sleep(1);
1021         }
1022     }
1023
1024 #if VERBOSE_TESTING
1025     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1026                 _("Finished copying all friend files!\n"));
1027 #endif
1028   GNUNET_free(pidarr);
1029   return ret;
1030 }
1031
1032 /**
1033  * Internal notification of a connection, kept so that we can ensure some connections
1034  * happen instead of flooding all testing daemons with requests to connect.
1035  */
1036 static void internal_connect_notify (void *cls,
1037                                      const struct GNUNET_PeerIdentity *first,
1038                                      const struct GNUNET_PeerIdentity *second,
1039                                      const struct GNUNET_CONFIGURATION_Handle *first_cfg,
1040                                      const struct GNUNET_CONFIGURATION_Handle *second_cfg,
1041                                      struct GNUNET_TESTING_Daemon *first_daemon,
1042                                      struct GNUNET_TESTING_Daemon *second_daemon,
1043                                      const char *emsg)
1044 {
1045   struct GNUNET_TESTING_PeerGroup *pg = cls;
1046   outstanding_connects--;
1047
1048   pg->notify_connection(pg->notify_connection_cls, first, second, first_cfg, second_cfg, first_daemon, second_daemon, emsg);
1049
1050 }
1051
1052 static void schedule_connect(void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1053 {
1054   struct ConnectContext *connect_context = cls;
1055
1056   if (tc->reason == GNUNET_SCHEDULER_REASON_SHUTDOWN)
1057     return;
1058
1059   if (outstanding_connects > MAX_OUTSTANDING_CONNECTIONS)
1060     {
1061 #if VERBOSE_TESTING > 2
1062           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1063                       _("Delaying connect, we have too many outstanding connections!\n"));
1064 #endif
1065       GNUNET_SCHEDULER_add_delayed(connect_context->pg->sched, GNUNET_TIME_relative_multiply(GNUNET_TIME_UNIT_SECONDS, 3), &schedule_connect, connect_context);
1066     }
1067   else
1068     {
1069 #if VERBOSE_TESTING > 2
1070           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1071                       _("Creating connection, outstanding_connections is %d\n"), outstanding_connects);
1072 #endif
1073       outstanding_connects++;
1074       GNUNET_TESTING_daemons_connect (connect_context->first,
1075                                       connect_context->second,
1076                                       CONNECT_TIMEOUT,
1077                                       CONNECT_ATTEMPTS,
1078                                       &internal_connect_notify,
1079                                       connect_context->pg);
1080       GNUNET_free(connect_context);
1081     }
1082 }
1083
1084 /*
1085  * Connect the topology as specified by the PeerConnection's
1086  * of each peer in the peer group
1087  *
1088  * @param pg the peer group we are dealing with
1089  */
1090 static void
1091 connect_topology (struct GNUNET_TESTING_PeerGroup *pg)
1092 {
1093   unsigned int pg_iter;
1094   struct PeerConnection *connection_iter;
1095   struct ConnectContext *connect_context;
1096
1097   for (pg_iter = 0; pg_iter < pg->total; pg_iter++)
1098     {
1099       connection_iter = pg->peers[pg_iter].connected_peers;
1100       while (connection_iter != NULL)
1101         {
1102           connect_context = GNUNET_malloc(sizeof(struct ConnectContext));
1103           connect_context->pg = pg;
1104           connect_context->first = pg->peers[pg_iter].daemon;
1105           connect_context->second = connection_iter->daemon;
1106           GNUNET_SCHEDULER_add_now(pg->sched, &schedule_connect, connect_context);
1107           connection_iter = connection_iter->next;
1108         }
1109     }
1110 }
1111
1112
1113 /*
1114  * Takes a peer group and attempts to create a topology based on the
1115  * one specified in the configuration file.  Returns the number of connections
1116  * that will attempt to be created, but this will happen asynchronously(?) so
1117  * the caller will have to keep track (via the callback) of whether or not
1118  * the connection actually happened.
1119  *
1120  * @param pg the peer group struct representing the running peers
1121  * @param topology which topology to connect the peers in
1122  *
1123  * @return the number of connections should be created by the topology, so the
1124  * caller knows how many to wait for (if it so chooses)
1125  *
1126  */
1127 int
1128 GNUNET_TESTING_create_topology (struct GNUNET_TESTING_PeerGroup *pg, enum GNUNET_TESTING_Topology topology)
1129 {
1130   int ret;
1131   int num_connections;
1132
1133   GNUNET_assert (pg->notify_connection != NULL);
1134   ret = GNUNET_OK;
1135
1136   switch (topology)
1137     {
1138     case GNUNET_TESTING_TOPOLOGY_CLIQUE:
1139 #if VERBOSE_TESTING
1140       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1141                   _("Creating clique topology\n"));
1142 #endif
1143       num_connections = create_clique (pg);
1144       break;
1145     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD_RING:
1146 #if VERBOSE_TESTING
1147       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1148                   _("Creating small world (ring) topology\n"));
1149 #endif
1150       num_connections = create_small_world_ring (pg);
1151       break;
1152     case GNUNET_TESTING_TOPOLOGY_SMALL_WORLD:
1153 #if VERBOSE_TESTING
1154       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1155                   _("Creating small world (2d-torus) topology\n"));
1156 #endif
1157       num_connections = create_small_world (pg);
1158       break;
1159     case GNUNET_TESTING_TOPOLOGY_RING:
1160 #if VERBOSE_TESTING
1161       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1162                   _("Creating ring topology\n"));
1163 #endif
1164       num_connections = create_ring (pg);
1165       break;
1166     case GNUNET_TESTING_TOPOLOGY_2D_TORUS:
1167 #if VERBOSE_TESTING
1168       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1169                   _("Creating 2d torus topology\n"));
1170 #endif
1171       num_connections = create_2d_torus (pg);
1172       break;
1173     case GNUNET_TESTING_TOPOLOGY_ERDOS_RENYI:
1174 #if VERBOSE_TESTING
1175       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1176                   _("Creating Erdos-Renyi topology\n"));
1177 #endif
1178       num_connections = create_erdos_renyi (pg);
1179       break;
1180     case GNUNET_TESTING_TOPOLOGY_INTERNAT:
1181 #if VERBOSE_TESTING
1182       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1183                   _("Creating InterNAT topology\n"));
1184 #endif
1185       num_connections = create_nated_internet (pg);
1186       break;
1187     case GNUNET_TESTING_TOPOLOGY_SCALE_FREE:
1188 #if VERBOSE_TESTING
1189       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1190                   _("Creating Scale Free topology\n"));
1191 #endif
1192       num_connections = create_scale_free (pg);
1193       break;
1194     case GNUNET_TESTING_TOPOLOGY_NONE:
1195       num_connections = 0;
1196       break;
1197     default:
1198       num_connections = 0;
1199       break;
1200     }
1201   if (num_connections < 1)
1202     return GNUNET_SYSERR;
1203
1204   if (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (pg->cfg, "TESTING", "F2F"))
1205     ret = create_and_copy_friend_files(pg);
1206   if (ret == GNUNET_OK)
1207     connect_topology(pg);
1208   else
1209     {
1210 #if VERBOSE_TESTING
1211       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1212                   _("Failed during friend file copying!\n"));
1213 #endif
1214       return GNUNET_SYSERR;
1215     }
1216
1217   return num_connections;
1218 }
1219
1220 /**
1221  * Start count gnunetd processes with the same set of transports and
1222  * applications.  The port numbers (any option called "PORT") will be
1223  * adjusted to ensure that no two peers running on the same system
1224  * have the same port(s) in their respective configurations.
1225  *
1226  * @param sched scheduler to use
1227  * @param cfg configuration template to use
1228  * @param total number of daemons to start
1229  * @param cb function to call on each daemon that was started
1230  * @param cb_cls closure for cb
1231  * @param connect_callback function to call each time two hosts are connected
1232  * @param connect_callback_cls closure for connect_callback
1233  * @param hostnames space-separated list of hostnames to use; can be NULL (to run
1234  *        everything on localhost).
1235  * @return NULL on error, otherwise handle to control peer group
1236  */
1237 struct GNUNET_TESTING_PeerGroup *
1238 GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched,
1239                               const struct GNUNET_CONFIGURATION_Handle *cfg,
1240                               unsigned int total,
1241                               GNUNET_TESTING_NotifyDaemonRunning cb,
1242                               void *cb_cls,
1243                               GNUNET_TESTING_NotifyConnection
1244                               connect_callback, void *connect_callback_cls,
1245                               const char *hostnames)
1246 {
1247   struct GNUNET_TESTING_PeerGroup *pg;
1248   const char *rpos;
1249   char *pos;
1250   char *start;
1251   const char *hostname;
1252   char *baseservicehome;
1253   char *newservicehome;
1254   char *tmpdir;
1255   struct GNUNET_CONFIGURATION_Handle *pcfg;
1256   unsigned int off;
1257   unsigned int hostcnt;
1258   uint16_t minport;
1259
1260   if (0 == total)
1261     {
1262       GNUNET_break (0);
1263       return NULL;
1264     }
1265
1266   pg = GNUNET_malloc (sizeof (struct GNUNET_TESTING_PeerGroup));
1267   pg->sched = sched;
1268   pg->cfg = cfg;
1269   pg->cb = cb;
1270   pg->cb_cls = cb_cls;
1271   pg->notify_connection = connect_callback;
1272   pg->notify_connection_cls = connect_callback_cls;
1273   pg->total = total;
1274   pg->peers = GNUNET_malloc (total * sizeof (struct PeerData));
1275   if (NULL != hostnames)
1276     {
1277       off = 2;
1278       /* skip leading spaces */
1279       while ((0 != *hostnames) && (isspace (*hostnames)))
1280         hostnames++;
1281       rpos = hostnames;
1282       while ('\0' != *rpos)
1283         {
1284           if (isspace (*rpos))
1285             off++;
1286           rpos++;
1287         }
1288       pg->hosts = GNUNET_malloc (off * sizeof (struct HostData));
1289       off = 0;
1290       start = GNUNET_strdup (hostnames);
1291       pos = start;
1292       while ('\0' != *pos)
1293         {
1294           if (isspace (*pos))
1295             {
1296               *pos = '\0';
1297               if (strlen (start) > 0)
1298                 {
1299                   pg->hosts[off].minport = LOW_PORT;
1300                   pg->hosts[off++].hostname = start;
1301                 }
1302               start = pos + 1;
1303             }
1304           pos++;
1305         }
1306       if (strlen (start) > 0)
1307         {
1308           pg->hosts[off].minport = LOW_PORT;
1309           pg->hosts[off++].hostname = start;
1310         }
1311       if (off == 0)
1312         {
1313           GNUNET_free (start);
1314           GNUNET_free (pg->hosts);
1315           pg->hosts = NULL;
1316         }
1317       hostcnt = off;
1318       minport = 0;              /* make gcc happy */
1319     }
1320   else
1321     {
1322       hostcnt = 0;
1323       minport = LOW_PORT;
1324     }
1325   for (off = 0; off < total; off++)
1326     {
1327       if (hostcnt > 0)
1328         {
1329           hostname = pg->hosts[off % hostcnt].hostname;
1330           pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport, hostname);
1331         }
1332       else
1333         {
1334           hostname = NULL;
1335           pcfg = make_config (cfg, &minport, hostname);
1336         }
1337
1338       if (NULL == pcfg)
1339         {
1340           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1341                       _
1342                       ("Could not create configuration for peer number %u on `%s'!\n"),
1343                       off, hostname == NULL ? "localhost" : hostname);
1344           continue;
1345         }
1346
1347       if (GNUNET_YES ==
1348           GNUNET_CONFIGURATION_get_value_string (pcfg, "PATHS", "SERVICEHOME",
1349                                                  &baseservicehome))
1350         {
1351           GNUNET_asprintf (&newservicehome,
1352                            "%s/%d/", baseservicehome, off);
1353           GNUNET_free (baseservicehome);
1354         }
1355       else
1356         {
1357           tmpdir = getenv ("TMPDIR");
1358           tmpdir = tmpdir ? tmpdir : "/tmp";
1359           GNUNET_asprintf (&newservicehome,
1360                            "%s/%s/%d/",
1361                            tmpdir,
1362                            "gnunet-testing-test-test", off);
1363         }
1364       GNUNET_CONFIGURATION_set_value_string (pcfg,
1365                                              "PATHS",
1366                                              "SERVICEHOME", newservicehome);
1367       GNUNET_free (newservicehome);
1368       pg->peers[off].cfg = pcfg;
1369       pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched,
1370                                                            pcfg,
1371                                                            hostname,
1372                                                            cb, cb_cls);
1373       if (NULL == pg->peers[off].daemon)
1374         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1375                     _("Could not start peer number %u!\n"), off);
1376     }
1377   return pg;
1378 }
1379
1380 /*
1381  * Get a daemon by number, so callers don't have to do nasty
1382  * offsetting operation.
1383  */
1384 struct GNUNET_TESTING_Daemon *
1385 GNUNET_TESTING_daemon_get (struct GNUNET_TESTING_PeerGroup *pg, unsigned int position)
1386 {
1387   if (position < pg->total)
1388     return pg->peers[position].daemon;
1389   else
1390     return NULL;
1391 }
1392
1393 /**
1394  * Shutdown all peers started in the given group.
1395  *
1396  * @param pg handle to the peer group
1397  */
1398 void
1399 GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg)
1400 {
1401   unsigned int off;
1402   struct PeerConnection *pos;
1403   struct PeerConnection *next;
1404
1405   for (off = 0; off < pg->total; off++)
1406     {
1407       /* FIXME: should we wait for our
1408          continuations to be called here? This
1409          would require us to take a continuation
1410          as well... */
1411
1412       if (NULL != pg->peers[off].daemon)
1413         GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, NULL, NULL);
1414       if (NULL != pg->peers[off].cfg)
1415         GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg);
1416
1417       pos = pg->peers[off].connected_peers;
1418       while (pos != NULL)
1419         {
1420           next = pos->next;
1421           GNUNET_free(pos);
1422           pos = next;
1423         }
1424
1425     }
1426   GNUNET_free (pg->peers);
1427   if (NULL != pg->hosts)
1428     {
1429       GNUNET_free (pg->hosts[0].hostname);
1430       GNUNET_free (pg->hosts);
1431     }
1432   GNUNET_free (pg);
1433 }
1434
1435
1436 /* end of testing_group.c */