Fixed reconnect
[oweals/gnunet.git] / src / testing / test_testing_topology_blacklist.c
1 /*
2      This file is part of GNUnet.
3      (C) 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 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file testing/test_testing_topology_blacklist.c
22  * @brief base testcase for testing transport level blacklisting
23  */
24 #include "platform.h"
25 #include "gnunet_testing_lib.h"
26 #include "gnunet_core_service.h"
27
28 #define VERBOSE GNUNET_NO
29
30 /**
31  * How long until we fail the whole testcase?
32  */
33 #define TEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
34
35 /**
36  * How long until we give up on starting the peers? (Must be longer than the connect timeout!)
37  */
38 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 300)
39
40 #define DEFAULT_NUM_PEERS 4
41
42 #define MAX_OUTSTANDING_CONNECTIONS 300
43
44 static int ok;
45
46 struct GNUNET_TIME_Relative connect_timeout;
47
48 static unsigned long long connect_attempts;
49
50 static unsigned long long num_peers;
51
52 static unsigned int total_connections;
53
54 static unsigned int failed_connections;
55
56 static unsigned int expected_connections;
57
58 static unsigned int expected_failed_connections;
59
60 static unsigned long long peers_left;
61
62 static struct GNUNET_TESTING_PeerGroup *pg;
63
64 const struct GNUNET_CONFIGURATION_Handle *main_cfg;
65
66 GNUNET_SCHEDULER_TaskIdentifier die_task;
67
68 static char *dotOutFileName;
69
70 static FILE *dotOutFile;
71
72 static char *blacklist_transports;
73
74 static enum GNUNET_TESTING_Topology topology = GNUNET_TESTING_TOPOLOGY_CLIQUE;  /* Overlay should allow all connections */
75
76 static enum GNUNET_TESTING_Topology blacklist_topology = GNUNET_TESTING_TOPOLOGY_RING;  /* Blacklist underlay into a ring */
77
78 static enum GNUNET_TESTING_Topology connection_topology = GNUNET_TESTING_TOPOLOGY_NONE; /* NONE actually means connect all allowed peers */
79
80 static enum GNUNET_TESTING_TopologyOption connect_topology_option = GNUNET_TESTING_TOPOLOGY_OPTION_ALL; /* Try to connect all possible OVERLAY connections */
81
82 static double connect_topology_option_modifier = 0.0;
83
84 static char *test_directory;
85
86 #define MTYPE 12345
87
88 struct GNUNET_TestMessage
89 {
90   /**
91    * Header of the message
92    */
93   struct GNUNET_MessageHeader header;
94
95   /**
96    * Unique identifier for this message.
97    */
98   uint32_t uid;
99 };
100
101
102 /**
103  * Check whether peers successfully shut down.
104  */
105 void
106 shutdown_callback (void *cls, const char *emsg)
107 {
108   if (emsg != NULL)
109   {
110 #if VERBOSE
111     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown of peers failed!\n");
112 #endif
113     if (ok == 0)
114       ok = 666;
115   }
116   else
117   {
118 #if VERBOSE
119     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All peers successfully shut down!\n");
120 #endif
121   }
122 }
123
124 static void
125 finish_testing ()
126 {
127   GNUNET_assert (pg != NULL);
128
129 #if VERBOSE
130   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131               "Called finish testing, stopping daemons.\n");
132 #endif
133   sleep (1);
134 #if VERBOSE
135   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Calling daemons_stop\n");
136 #endif
137   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
138 #if VERBOSE
139   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "daemons_stop finished\n");
140 #endif
141   if (dotOutFile != NULL)
142   {
143     fprintf (dotOutFile, "}");
144     fclose (dotOutFile);
145   }
146
147   ok = 0;
148 }
149
150 static void
151 end_badly (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
152 {
153   char *msg = cls;
154
155   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
156               "End badly was called (%s)... stopping daemons.\n", msg);
157
158   if (pg != NULL)
159   {
160     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
161     ok = 7331;                  /* Opposite of leet */
162   }
163   else
164     ok = 401;                   /* Never got peers started */
165
166   if (dotOutFile != NULL)
167   {
168     fprintf (dotOutFile, "}");
169     fclose (dotOutFile);
170   }
171 }
172
173
174
175 void
176 topology_callback (void *cls, const struct GNUNET_PeerIdentity *first,
177                    const struct GNUNET_PeerIdentity *second, uint32_t distance,
178                    const struct GNUNET_CONFIGURATION_Handle *first_cfg,
179                    const struct GNUNET_CONFIGURATION_Handle *second_cfg,
180                    struct GNUNET_TESTING_Daemon *first_daemon,
181                    struct GNUNET_TESTING_Daemon *second_daemon,
182                    const char *emsg)
183 {
184   if (emsg == NULL)
185   {
186     total_connections++;
187 #if VERBOSE
188     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "connected peer %s to peer %s\n",
189                 first_daemon->shortname, second_daemon->shortname);
190 #endif
191     if (dotOutFile != NULL)
192       fprintf (dotOutFile, "\tn%s -- n%s;\n", first_daemon->shortname,
193                second_daemon->shortname);
194   }
195
196   else
197   {
198     failed_connections++;
199 #if VERBOSE
200     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
201                 "Failed to connect peer %s to peer %s with error :\n%s\n",
202                 first_daemon->shortname, second_daemon->shortname, emsg);
203 #endif
204   }
205
206
207   if (total_connections == expected_connections)
208   {
209 #if VERBOSE
210     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211                 "Created %d total connections, which is our target number (that's bad)!\n",
212                 total_connections);
213 #endif
214
215     GNUNET_SCHEDULER_cancel (die_task);
216     die_task = GNUNET_SCHEDULER_NO_TASK;
217     die_task =
218         GNUNET_SCHEDULER_add_now (&end_badly,
219                                   "from topology_callback (too many successful connections)");
220   }
221   else if (total_connections + failed_connections == expected_connections)
222   {
223     if ((failed_connections == expected_failed_connections) &&
224         (total_connections ==
225          expected_connections - expected_failed_connections))
226     {
227       GNUNET_SCHEDULER_cancel (die_task);
228       die_task = GNUNET_SCHEDULER_NO_TASK;
229       die_task = GNUNET_SCHEDULER_add_now (&finish_testing, NULL);
230     }
231     else
232     {
233       GNUNET_SCHEDULER_cancel (die_task);
234       die_task =
235           GNUNET_SCHEDULER_add_now (&end_badly,
236                                     "from topology_callback (wrong number of failed connections)");
237     }
238   }
239   else
240   {
241 #if VERBOSE
242     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
243                 "Have %d total connections, %d failed connections, Want %d (failed) and %d (successful)\n",
244                 total_connections, failed_connections,
245                 expected_failed_connections,
246                 expected_connections - expected_failed_connections);
247 #endif
248   }
249 }
250
251 static void
252 connect_topology ()
253 {
254   expected_connections = -1;
255   if ((pg != NULL) && (peers_left == 0))
256   {
257     expected_connections =
258         GNUNET_TESTING_connect_topology (pg, connection_topology,
259                                          connect_topology_option,
260                                          connect_topology_option_modifier,
261                                          connect_timeout, connect_attempts,
262                                          NULL, NULL);
263 #if VERBOSE
264     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Have %d expected connections\n",
265                 expected_connections);
266 #endif
267   }
268
269   GNUNET_SCHEDULER_cancel (die_task);
270   if (expected_connections == GNUNET_SYSERR)
271   {
272     die_task =
273         GNUNET_SCHEDULER_add_now (&end_badly,
274                                   "from connect topology (bad return)");
275   }
276
277   die_task =
278       GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
279                                     "from connect topology (timeout)");
280 }
281
282 static void
283 create_topology ()
284 {
285   peers_left = num_peers;       /* Reset counter */
286   if (GNUNET_TESTING_create_topology
287       (pg, topology, blacklist_topology, blacklist_transports) != GNUNET_SYSERR)
288   {
289 #if VERBOSE
290     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
291                 "Topology set up, now starting peers!\n");
292 #endif
293     GNUNET_TESTING_daemons_continue_startup (pg);
294   }
295   else
296   {
297     GNUNET_SCHEDULER_cancel (die_task);
298     die_task =
299         GNUNET_SCHEDULER_add_now (&end_badly,
300                                   "from create topology (bad return)");
301   }
302   GNUNET_SCHEDULER_cancel (die_task);
303   die_task =
304       GNUNET_SCHEDULER_add_delayed (TEST_TIMEOUT, &end_badly,
305                                     "from continue startup (timeout)");
306 }
307
308
309 static void
310 peers_started_callback (void *cls, const struct GNUNET_PeerIdentity *id,
311                         const struct GNUNET_CONFIGURATION_Handle *cfg,
312                         struct GNUNET_TESTING_Daemon *d, const char *emsg)
313 {
314   if (emsg != NULL)
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
317                 "Failed to start daemon with error: `%s'\n", emsg);
318     return;
319   }
320   GNUNET_assert (id != NULL);
321 #if VERBOSE
322   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Started daemon %llu out of %llu\n",
323               (num_peers - peers_left) + 1, num_peers);
324 #endif
325   peers_left--;
326   if (peers_left == 0)
327   {
328 #if VERBOSE
329     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330                 "All %d daemons started, now creating topology!\n", num_peers);
331 #endif
332     GNUNET_SCHEDULER_cancel (die_task);
333     /* Set up task in case topology creation doesn't finish
334      * within a reasonable amount of time */
335     die_task =
336         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
337                                       (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
338                                       "from peers_started_callback");
339     connect_topology ();
340     ok = 0;
341   }
342 }
343
344 /**
345  * Callback indicating that the hostkey was created for a peer.
346  *
347  * @param cls NULL
348  * @param id the peer identity
349  * @param d the daemon handle (pretty useless at this point, remove?)
350  * @param emsg non-null on failure
351  */
352 void
353 hostkey_callback (void *cls, const struct GNUNET_PeerIdentity *id,
354                   struct GNUNET_TESTING_Daemon *d, const char *emsg)
355 {
356   if (emsg != NULL)
357   {
358     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
359                 "Hostkey callback received error: %s\n", emsg);
360   }
361
362 #if VERBOSE
363   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Hostkey created for peer `%s'\n",
364               GNUNET_i2s (id));
365 #endif
366   peers_left--;
367   if (peers_left == 0)
368   {
369 #if VERBOSE
370     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
371                 "All %d hostkeys created, now creating topology!\n", num_peers);
372 #endif
373     GNUNET_SCHEDULER_cancel (die_task);
374     /* Set up task in case topology creation doesn't finish
375      * within a reasonable amount of time */
376     die_task =
377         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
378                                       (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
379                                       "from hostkey_callback");
380     GNUNET_SCHEDULER_add_now (&create_topology, NULL);
381     ok = 0;
382   }
383 }
384
385 static void
386 run (void *cls, char *const *args, const char *cfgfile,
387      const struct GNUNET_CONFIGURATION_Handle *cfg)
388 {
389   unsigned long long topology_num;
390   unsigned long long connect_topology_num;
391   unsigned long long blacklist_topology_num;
392   unsigned long long connect_topology_option_num;
393   unsigned long long temp_connect;
394   char *connect_topology_option_modifier_string;
395
396   ok = 1;
397
398   dotOutFile = fopen (dotOutFileName, "w");
399   if (dotOutFile != NULL)
400   {
401     fprintf (dotOutFile, "strict graph G {\n");
402   }
403
404 #if VERBOSE
405   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
406               "Starting daemons based on config file %s\n", cfgfile);
407 #endif
408
409   if (GNUNET_YES !=
410       GNUNET_CONFIGURATION_get_value_string (cfg, "paths", "servicehome",
411                                              &test_directory))
412   {
413     ok = 404;
414     if (dotOutFile != NULL)
415     {
416       fclose (dotOutFile);
417     }
418     return;
419   }
420
421   if (GNUNET_YES ==
422       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "topology",
423                                              &topology_num))
424     topology = topology_num;
425
426   if (GNUNET_YES ==
427       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_topology",
428                                              &connect_topology_num))
429     connection_topology = connect_topology_num;
430
431   if (GNUNET_YES ==
432       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
433                                              "connect_topology_option",
434                                              &connect_topology_option_num))
435     connect_topology_option = connect_topology_option_num;
436
437   if (GNUNET_YES ==
438       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
439                                              "connect_topology_option_modifier",
440                                              &connect_topology_option_modifier_string))
441   {
442     if (sscanf
443         (connect_topology_option_modifier_string, "%lf",
444          &connect_topology_option_modifier) != 1)
445     {
446       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
447                   _
448                   ("Invalid value `%s' for option `%s' in section `%s': expected float\n"),
449                   connect_topology_option_modifier_string,
450                   "connect_topology_option_modifier", "TESTING");
451       GNUNET_free (connect_topology_option_modifier_string);
452       ok = 707;
453       if (dotOutFile != NULL)
454       {
455         fclose (dotOutFile);
456       }
457       return;
458     }
459     GNUNET_free (connect_topology_option_modifier_string);
460   }
461
462   if (GNUNET_OK !=
463       GNUNET_CONFIGURATION_get_value_string (cfg, "testing",
464                                              "blacklist_transports",
465                                              &blacklist_transports))
466   {
467     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
468                 "No transports specified for blacklisting in blacklist testcase (this shouldn't happen!)\n");
469     ok = 808;
470     if (dotOutFile != NULL)
471     {
472       fclose (dotOutFile);
473     }
474     return;
475   }
476
477   if (GNUNET_YES ==
478       GNUNET_CONFIGURATION_get_value_number (cfg, "testing",
479                                              "blacklist_topology",
480                                              &blacklist_topology_num))
481     blacklist_topology = blacklist_topology_num;
482
483   if (GNUNET_SYSERR ==
484       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "num_peers",
485                                              &num_peers))
486     num_peers = DEFAULT_NUM_PEERS;
487
488   if (GNUNET_OK ==
489       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_timeout",
490                                              &temp_connect))
491     connect_timeout =
492         GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, temp_connect);
493   else
494   {
495     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
496                 "testing", "connect_timeout");
497     return;
498   }
499
500
501   if (GNUNET_OK !=
502       GNUNET_CONFIGURATION_get_value_number (cfg, "testing", "connect_attempts",
503                                              &connect_attempts))
504   {
505     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Must provide option %s:%s!\n",
506                 "testing", "connect_attempts");
507     return;
508   }
509
510   main_cfg = cfg;
511
512   GNUNET_assert (num_peers > 0 && num_peers < (unsigned int) -1);
513   peers_left = num_peers;
514
515   /* For this specific test we only really want a CLIQUE topology as the
516    * overlay allowed topology, and a RING topology as the underlying connection
517    * allowed topology.  So we will expect only num_peers * 2 connections to
518    * work, and (num_peers * (num_peers - 1)) - (num_peers * 2) to fail.
519    */
520   expected_connections = num_peers * (num_peers - 1);
521   expected_failed_connections = expected_connections - (num_peers * 2);
522
523
524   /* Set up a task to end testing if peer start fails */
525   die_task =
526       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
527                                     (GNUNET_TIME_UNIT_MINUTES, 5), &end_badly,
528                                     "didn't start all daemons in reasonable amount of time!!!");
529
530   pg = GNUNET_TESTING_daemons_start (cfg, peers_left, peers_left, peers_left,
531                                      TIMEOUT, &hostkey_callback, NULL,
532                                      &peers_started_callback, NULL,
533                                      &topology_callback, NULL, NULL);
534
535 }
536
537 static int
538 check ()
539 {
540   int ret;
541
542   char *const argv[] = { "test-testing-topology-blacklist",
543     "-c",
544     "test_testing_data_topology_blacklist.conf",
545 #if VERBOSE
546     "-L", "DEBUG",
547 #endif
548     NULL
549   };
550   struct GNUNET_GETOPT_CommandLineOption options[] = {
551     GNUNET_GETOPT_OPTION_END
552   };
553   ret =
554       GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
555                           "test-testing-topology-blacklist", "nohelp", options,
556                           &run, &ok);
557   if (ret != GNUNET_OK)
558   {
559     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
560                 "`test-testing-topology-blacklist': Failed with error code %d\n",
561                 ret);
562   }
563
564   return ok;
565 }
566
567 int
568 main (int argc, char *argv[])
569 {
570   int ret;
571
572   GNUNET_log_setup ("test_testing_topology_blacklist",
573 #if VERBOSE
574                     "DEBUG",
575 #else
576                     "WARNING",
577 #endif
578                     NULL);
579   ret = check ();
580
581   /**
582    * Need to remove base directory, subdirectories taken care
583    * of by the testing framework.
584    */
585   if (test_directory != NULL)
586   {
587     if (GNUNET_DISK_directory_remove (test_directory) != GNUNET_OK)
588     {
589       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
590                   "Failed to remove testing directory %s\n", test_directory);
591     }
592   }
593
594   return ret;
595 }
596
597 /* end of test_testing_topology_blacklist.c */