- allow slow botnets enough time
[oweals/gnunet.git] / src / mesh / test_mesh_small.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011 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 mesh/test_mesh_small.c
22  *
23  * @brief Test for the mesh service: retransmission of traffic.
24  */
25 #include <stdio.h>
26 #include "platform.h"
27 #include "gnunet_testing_lib.h"
28 #include "gnunet_mesh_service.h"
29 #include <gauger.h>
30
31
32 #define VERBOSE GNUNET_YES
33 #define REMOVE_DIR GNUNET_YES
34
35 struct MeshPeer
36 {
37   struct MeshPeer *prev;
38
39   struct MeshPeer *next;
40
41   struct GNUNET_TESTING_Daemon *daemon;
42
43   struct GNUNET_MESH_Handle *mesh_handle;
44 };
45
46 /**
47  * How namy messages to send
48  */
49 #define TOTAL_PACKETS 1000
50
51 /**
52  * How long until we give up on connecting the peers?
53  */
54 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 120)
55
56 /**
57  * Time to wait for stuff that should be rather fast
58  */
59 #define SHORT_TIME GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 20)
60
61 /**
62  * DIFFERENT TESTS TO RUN
63  */
64 #define SETUP 0
65 #define UNICAST 1
66 #define MULTICAST 2
67 #define SPEED 3
68 #define SPEED_ACK 4
69 #define SPEED_MIN 5
70 #define SPEED_NOBUF 6
71
72 /**
73  * Which test are we running?
74  */
75 static int test;
76
77 /**
78  * String with test name
79  */
80 char *test_name;
81
82 /**
83  * Flag to send traffic leaf->root in speed tests to test BCK_ACK logic.
84  */
85 static int test_backwards = GNUNET_NO;
86
87 /**
88  * How many events have happened
89  */
90 static int ok;
91
92  /**
93   * Each peer is supposed to generate the following callbacks:
94   * 1 incoming tunnel (@dest)
95   * 1 connected peer (@orig)
96   * 1 received data packet (@dest)
97   * 1 received data packet (@orig)
98   * 1 received tunnel destroy (@dest)
99   * _________________________________
100   * 5 x ok expected per peer
101   */
102 int ok_goal;
103
104
105 /**
106  * Is the setup initialized?
107  */
108 static int initialized;
109
110 /**
111  * Peers that have been connected
112  */
113 static int peers_in_tunnel;
114
115 /**
116  * Peers that have responded
117  */
118 static int peers_responded;
119
120 /**
121  * Number of payload packes sent
122  */
123 static int data_sent;
124
125 /**
126  * Number of payload packets received
127  */
128 static int data_received;
129
130 /**
131  * Number of payload packed explicitly (app level) acknowledged
132  */
133 static int data_ack;
134
135 /**
136  * Be verbose
137  */
138 static int verbose;
139
140 /**
141  * Total number of peers in the test.
142  */
143 static unsigned long long num_peers;
144
145 /**
146  * Global configuration file
147  */
148 static struct GNUNET_CONFIGURATION_Handle *testing_cfg;
149
150 /**
151  * Total number of currently running peers.
152  */
153 static unsigned long long peers_running;
154
155 /**
156  * Total number of connections in the whole network.
157  */
158 static unsigned int total_connections;
159
160 /**
161  * The currently running peer group.
162  */
163 static struct GNUNET_TESTING_PeerGroup *pg;
164
165 /**
166  * File to report results to.
167  */
168 static struct GNUNET_DISK_FileHandle *output_file;
169
170 /**
171  * File to log connection info, statistics to.
172  */
173 static struct GNUNET_DISK_FileHandle *data_file;
174
175 /**
176  * Wait time
177  */
178 static struct GNUNET_TIME_Relative wait_time;
179
180 /**
181  * Task called to disconnect peers.
182  */
183 static GNUNET_SCHEDULER_TaskIdentifier disconnect_task;
184
185 /**
186  * Task To perform tests
187  */
188 static GNUNET_SCHEDULER_TaskIdentifier test_task;
189
190 /**
191  * Task called to shutdown test.
192  */
193 static GNUNET_SCHEDULER_TaskIdentifier shutdown_handle;
194
195 /**
196  * Filename of the file containing the topology.
197  */
198 static char *topology_file;
199
200 /**
201  * Testbed handle for the root peer
202  */
203 static struct GNUNET_TESTING_Daemon *d1;
204
205 /**
206  * Testbed handle for the first leaf peer
207  */
208 static struct GNUNET_TESTING_Daemon *d2;
209
210 /**
211  * Testbed handle for the second leaf peer
212  */
213 static struct GNUNET_TESTING_Daemon *d3;
214
215 /**
216  * Mesh handle for the root peer
217  */
218 static struct GNUNET_MESH_Handle *h1;
219
220 /**
221  * Mesh handle for the first leaf peer
222  */
223 static struct GNUNET_MESH_Handle *h2;
224
225 /**
226  * Mesh handle for the second leaf peer
227  */
228 static struct GNUNET_MESH_Handle *h3;
229
230 /**
231  * Tunnel handle for the root peer
232  */
233 static struct GNUNET_MESH_Tunnel *t;
234
235 /**
236  * Tunnel handle for the first leaf peer
237  */
238 static struct GNUNET_MESH_Tunnel *incoming_t;
239
240 /**
241  * Tunnel handle for the second leaf peer
242  */
243 static struct GNUNET_MESH_Tunnel *incoming_t2;
244
245 /**
246  * Time we started the data transmission (after tunnel has been established
247  * and initilized).
248  */
249 static struct GNUNET_TIME_Absolute start_time;
250
251
252 /**
253  * Show the results of the test (banwidth acheived) and log them to GAUGER
254  */
255 static void
256 show_end_data (void)
257 {
258   static struct GNUNET_TIME_Absolute end_time;
259   static struct GNUNET_TIME_Relative total_time;
260
261   end_time = GNUNET_TIME_absolute_get();
262   total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
263   FPRINTF (stderr, "\nResults of test \"%s\"\n", test_name);
264   FPRINTF (stderr, "Test time %llu ms\n",
265             (unsigned long long) total_time.rel_value);
266   FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
267             4 * TOTAL_PACKETS * 1.0 / total_time.rel_value); // 4bytes * ms
268   FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
269             TOTAL_PACKETS * 1000.0 / total_time.rel_value); // packets * ms
270   GAUGER ("MESH", test_name,
271           TOTAL_PACKETS * 1000.0 / total_time.rel_value,
272           "packets/s");
273 }
274
275
276 /**
277  * Check whether peers successfully shut down.
278  * 
279  * @param cls Closure (unused).
280  * @param emsg Error message.
281  */
282 static void
283 shutdown_callback (void *cls, const char *emsg)
284 {
285   if (emsg != NULL)
286   {
287 #if VERBOSE
288     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
289                 "Shutdown of peers failed!\n");
290 #endif
291     ok--;
292   }
293   else
294   {
295 #if VERBOSE
296     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
297                 "All peers successfully shut down!\n");
298 #endif
299   }
300   GNUNET_CONFIGURATION_destroy (testing_cfg);
301 }
302
303
304 /**
305  * Shut down peergroup, clean up.
306  * 
307  * @param cls Closure (unused).
308  * @param tc Task Context.
309  */
310 static void
311 shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
312 {
313 #if VERBOSE
314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315               "Ending test.\n");
316 #endif
317
318   if (disconnect_task != GNUNET_SCHEDULER_NO_TASK)
319   {
320     GNUNET_SCHEDULER_cancel (disconnect_task);
321     disconnect_task = GNUNET_SCHEDULER_NO_TASK;
322   }
323
324   if (NULL != h1)
325   {
326     GNUNET_MESH_disconnect (h1);
327     h1 = NULL;
328   }
329   if (NULL != h2)
330   {
331     GNUNET_MESH_disconnect (h2);
332     h2 = NULL;
333   }
334   if (test == MULTICAST && NULL != h3)
335   {
336     GNUNET_MESH_disconnect (h3);
337     h3 = NULL;
338   }
339   
340   if (data_file != NULL)
341     GNUNET_DISK_file_close (data_file);
342   GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
343 }
344
345
346 /**
347  * Disconnect from mesh services af all peers, call shutdown.
348  * 
349  * @param cls Closure (unused).
350  * @param tc Task Context.
351  */
352 static void
353 disconnect_mesh_peers (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
354 {
355   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356               "disconnecting mesh service of peers\n");
357   disconnect_task = GNUNET_SCHEDULER_NO_TASK;
358   if (NULL != t)
359   {
360     GNUNET_MESH_tunnel_destroy(t);
361     t = NULL;
362   }
363   if (NULL != incoming_t)
364   {
365     GNUNET_MESH_tunnel_destroy(incoming_t);
366     incoming_t = NULL;
367   }
368   if (NULL != incoming_t2)
369   {
370     GNUNET_MESH_tunnel_destroy(incoming_t2);
371     incoming_t2 = NULL;
372   }
373   GNUNET_MESH_disconnect (h1);
374   GNUNET_MESH_disconnect (h2);
375   h1 = h2 = NULL;
376   if (test == MULTICAST)
377   {
378     GNUNET_MESH_disconnect (h3);
379     h3 = NULL;
380   }
381   if (GNUNET_SCHEDULER_NO_TASK != shutdown_handle)
382   {
383     GNUNET_SCHEDULER_cancel (shutdown_handle);
384     shutdown_handle = GNUNET_SCHEDULER_add_now (&shutdown_task, NULL);
385   }
386 }
387
388
389 /**
390  * Transmit ready callback.
391  * 
392  * @param cls Closure (message type).
393  * @param size Size of the tranmist buffer.
394  * @param buf Pointer to the beginning of the buffer.
395  * 
396  * @return Number of bytes written to buf.
397  */
398 static size_t
399 tmt_rdy (void *cls, size_t size, void *buf);
400
401
402 /**
403  * Task to schedule a new data transmission.
404  * 
405  * @param cls Closure (peer #).
406  * @param tc Task Context.
407  */
408 static void
409 data_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
410 {
411   struct GNUNET_MESH_TransmitHandle *th;
412   struct GNUNET_MESH_Tunnel *tunnel;
413   struct GNUNET_PeerIdentity *destination;
414
415   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
416     return;
417
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data task\n");
419   if (GNUNET_YES == test_backwards)
420   {
421     tunnel = incoming_t;
422     destination = &d1->id;
423   }
424   else
425   {
426     tunnel = t;
427     destination = &d2->id;
428   }
429   th = GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
430                                           GNUNET_TIME_UNIT_FOREVER_REL,
431                                           destination,
432                                           sizeof (struct GNUNET_MessageHeader),
433                                           &tmt_rdy, (void *) 1L);
434   if (NULL == th)
435   {
436     unsigned long i = (unsigned long) cls;
437
438     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Retransmission\n");
439     if (0 == i)
440     {
441       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "  in 1 ms\n");
442       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
443                                     &data_task, (void *)1UL);
444     }
445     else
446     {
447       i++;
448       GNUNET_log (GNUNET_ERROR_TYPE_INFO, "in %u ms\n", i);
449       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply(
450                                       GNUNET_TIME_UNIT_MILLISECONDS,
451                                       i),
452                                     &data_task, (void *)i);
453     }
454   }
455 }
456
457
458 /**
459  * Transmit ready callback
460  *
461  * @param cls Closure (message type).
462  * @param size Size of the buffer we have.
463  * @param buf Buffer to copy data to.
464  */
465 size_t
466 tmt_rdy (void *cls, size_t size, void *buf)
467 {
468   struct GNUNET_MessageHeader *msg = buf;
469
470   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471               " tmt_rdy called\n");
472   if (size < sizeof (struct GNUNET_MessageHeader) || NULL == buf)
473     return 0;
474   msg->size = htons (sizeof (struct GNUNET_MessageHeader));
475   msg->type = htons ((long) cls);
476   if (SPEED == test && GNUNET_YES == initialized)
477   {
478     data_sent++;
479     if (data_sent < TOTAL_PACKETS)
480     {
481       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               " Scheduling %d packet\n", data_sent);
483       GNUNET_SCHEDULER_add_now(&data_task, NULL);
484     }
485   }
486   return sizeof (struct GNUNET_MessageHeader);
487 }
488
489
490 /**
491  * Function is called whenever a message is received.
492  *
493  * @param cls closure (set from GNUNET_MESH_connect)
494  * @param tunnel connection to the other end
495  * @param tunnel_ctx place to store local state associated with the tunnel
496  * @param sender who sent the message
497  * @param message the actual message
498  * @param atsi performance data for the connection
499  * @return GNUNET_OK to keep the connection open,
500  *         GNUNET_SYSERR to close it (signal serious error)
501  */
502 int
503 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
504                const struct GNUNET_PeerIdentity *sender,
505                const struct GNUNET_MessageHeader *message,
506                const struct GNUNET_ATS_Information *atsi)
507 {
508   long client = (long) cls;
509   long expected_target_client;
510
511   ok++;
512
513   if ((ok % 20) == 0)
514   {
515     if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
516     {
517       GNUNET_SCHEDULER_cancel (disconnect_task);
518       disconnect_task =
519               GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
520                                             NULL);
521     }
522   }
523
524   switch (client)
525   {
526   case 1L:
527     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
528     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
529     peers_responded++;
530     if (test == MULTICAST && peers_responded < 2)
531       return GNUNET_OK;
532     break;
533   case 2L:
534   case 3L:
535     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
536                 "Leaf client %li got a message.\n",
537                 client);
538     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
539     client = 2L;
540     break;
541   default:
542     GNUNET_assert (0);
543     break;
544   }
545
546   if (SPEED == test && GNUNET_YES == test_backwards)
547   {
548     expected_target_client = 1L;
549   }
550   else
551   {
552     expected_target_client = 2L;
553   }
554
555   if (GNUNET_NO == initialized)
556   {
557     initialized = GNUNET_YES;
558     start_time = GNUNET_TIME_absolute_get ();
559     if (SPEED == test)
560     {
561       GNUNET_assert (2L == client);
562       GNUNET_SCHEDULER_add_now (&data_task, NULL);
563       return GNUNET_OK;
564     }
565   }
566
567   if (client == expected_target_client) // Normally 2 or 3
568   {
569     data_received++;
570     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
571                 " received data %u\n", data_received);
572     if (SPEED != test || (ok_goal - 2) == ok)
573     {
574       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
575                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
576                                         sizeof (struct GNUNET_MessageHeader),
577                                         &tmt_rdy, (void *) 1L);
578     }
579     else
580     {
581       if (data_received < TOTAL_PACKETS)
582         return GNUNET_OK;
583     }
584   }
585   else // Normally 1
586   {
587     if (test == SPEED_ACK || test == SPEED)
588     {
589       data_ack++;
590       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
591               " received ack %u\n", data_ack);
592       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
593                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
594                                         sizeof (struct GNUNET_MessageHeader),
595                                         &tmt_rdy, (void *) 1L);
596       if (data_ack < TOTAL_PACKETS && SPEED != test)
597         return GNUNET_OK;
598       if (ok == 2 && SPEED == test)
599         return GNUNET_OK;
600       show_end_data();
601     }
602     GNUNET_MESH_tunnel_destroy (t);
603     t = NULL;
604   }
605
606   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
607   {
608     GNUNET_SCHEDULER_cancel (disconnect_task);
609     disconnect_task =
610         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
611                                       NULL);
612   }
613
614   return GNUNET_OK;
615 }
616
617
618 /**
619  * Handlers, for diverse services
620  */
621 static struct GNUNET_MESH_MessageHandler handlers[] = {
622   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
623   {NULL, 0, 0}
624 };
625
626
627 /**
628  * Method called whenever another peer has added us to a tunnel
629  * the other peer initiated.
630  *
631  * @param cls closure
632  * @param tunnel new handle to the tunnel
633  * @param initiator peer that started the tunnel
634  * @param atsi performance information for the tunnel
635  * @return initial tunnel context for the tunnel
636  *         (can be NULL -- that's not an error)
637  */
638 static void *
639 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
640                  const struct GNUNET_PeerIdentity *initiator,
641                  const struct GNUNET_ATS_Information *atsi)
642 {
643   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
644               "Incoming tunnel from %s to peer %d\n",
645               GNUNET_i2s (initiator), (long) cls);
646   ok++;
647   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
648   if ((long) cls == 2L)
649     incoming_t = tunnel;
650   else if ((long) cls == 3L)
651     incoming_t2 = tunnel;
652   else
653   {
654     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
655                 "Incoming tunnel for unknown client %lu\n", (long) cls);
656     GNUNET_break(0);
657   }
658   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
659   {
660     GNUNET_SCHEDULER_cancel (disconnect_task);
661     disconnect_task =
662         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
663   }
664
665   return NULL;
666 }
667
668 /**
669  * Function called whenever an inbound tunnel is destroyed.  Should clean up
670  * any associated state.
671  *
672  * @param cls closure (set from GNUNET_MESH_connect)
673  * @param tunnel connection to the other end (henceforth invalid)
674  * @param tunnel_ctx place where local state associated
675  *                   with the tunnel is stored
676  */
677 static void
678 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
679                 void *tunnel_ctx)
680 {
681   long i = (long) cls;
682
683   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
684               "Incoming tunnel disconnected at peer %d\n",
685               i);
686   if (2L == i)
687   {
688     ok++;
689     incoming_t = NULL;
690   }
691   else if (3L == i)
692   {
693     ok++;
694     incoming_t2 = NULL;
695   }
696   else
697     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
698                 "Unknown peer! %d\n", i);
699   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
700   peers_in_tunnel--;
701   if (peers_in_tunnel > 0)
702     return;
703
704   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
705   {
706     GNUNET_SCHEDULER_cancel (disconnect_task);
707     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, NULL);
708   }
709
710   return;
711 }
712
713
714 /**
715  * Method called whenever a tunnel falls apart.
716  *
717  * @param cls closure
718  * @param peer peer identity the tunnel stopped working with
719  */
720 static void
721 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
722 {
723   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
724               "peer %s disconnected\n",
725               GNUNET_i2s (peer));
726   return;
727 }
728
729
730 /**
731  * Method called whenever a peer connects to a tunnel.
732  *
733  * @param cls closure
734  * @param peer peer identity the tunnel was created to, NULL on timeout
735  * @param atsi performance data for the connection
736  */
737 static void
738 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
739     const struct GNUNET_ATS_Information *atsi)
740 {
741   struct GNUNET_PeerIdentity *dest;
742
743   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
744               "peer %s connected\n", GNUNET_i2s (peer));
745
746   if (0 == memcmp (&d2->id, peer, sizeof (d2->id)) && (long) cls == 1L)
747   {
748     ok++;
749   }
750   if (test == MULTICAST && 0 == memcmp (&d3->id, peer, sizeof (d3->id)) &&
751       (long) cls == 1L)
752   {
753     ok++;
754   }
755   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
756   switch (test)
757   {
758     case UNICAST:
759     case SPEED:
760     case SPEED_ACK:
761       // incoming_t is NULL unless we send a relevant data packet
762       dest = &d2->id;
763       break;
764     case MULTICAST:
765       peers_in_tunnel++;
766       if (peers_in_tunnel < 2)
767         return;
768       dest = NULL;
769       break;
770     default:
771       GNUNET_assert (0);
772       return;
773   }
774   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
775   {
776     GNUNET_SCHEDULER_cancel (disconnect_task);
777     disconnect_task =
778         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
779     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780                 "Sending data initializer...\n");
781     peers_responded = 0;
782     data_ack = 0;
783     data_received = 0;
784     data_sent = 0;
785     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
786                                        GNUNET_TIME_UNIT_FOREVER_REL, dest,
787                                        sizeof (struct GNUNET_MessageHeader),
788                                        &tmt_rdy, (void *) 1L);
789   }
790   else
791   {
792     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
793                 "Disconnect already run?\n");
794     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
795                 "Aborting...\n");
796   }
797   return;
798 }
799
800
801 /**
802  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
803  * 
804  * Testcase continues when the root receives confirmation of connected peers,
805  * on callback funtion ch.
806  * 
807  * @param cls Closure (unsued).
808  * @param tc Task Context.
809  */
810 static void
811 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
812 {
813   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
814   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n");
815   GNUNET_MESH_peer_request_connect_add (t, &d2->id);
816
817   if (test == MULTICAST)
818   {
819     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
820                 "add peer 3\n");
821     GNUNET_MESH_peer_request_connect_add (t, &d3->id);
822   }
823
824   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
825               "schedule timeout in SHORT_TIME\n");
826   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
827   {
828     GNUNET_SCHEDULER_cancel (disconnect_task);
829     disconnect_task =
830         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
831   }
832 }
833
834
835 /**
836  * connect_mesh_service: connect to the mesh service of one of the peers
837  *
838  * @param cls Closure (unsued).
839  * @param tc Task Context.
840  */
841 static void
842 connect_mesh_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
843 {
844   GNUNET_MESH_ApplicationType app;
845
846   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
847     return;
848
849   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
850               "connect_mesh_service\n");
851
852   d2 = GNUNET_TESTING_daemon_get (pg, num_peers - 1);
853   if (test == MULTICAST)
854   {
855     d3 = GNUNET_TESTING_daemon_get (pg, num_peers - 2);
856   }
857   app = (GNUNET_MESH_ApplicationType) 0;
858
859 #if VERBOSE
860   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
861               "connecting to mesh service of peer %s\n",
862               GNUNET_i2s (&d1->id));
863   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
864               "connecting to mesh service of peer %s\n",
865               GNUNET_i2s (&d2->id));
866   if (test == MULTICAST)
867   {
868     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
869                 "connecting to mesh service of peer %s\n",
870                 GNUNET_i2s (&d3->id));
871   }
872 #endif
873   h1 = GNUNET_MESH_connect (d1->cfg, (void *) 1L, NULL, &tunnel_cleaner,
874                             handlers, &app);
875   h2 = GNUNET_MESH_connect (d2->cfg, (void *) 2L, &incoming_tunnel,
876                             &tunnel_cleaner, handlers, &app);
877   if (test == MULTICAST)
878   {
879     h3 = GNUNET_MESH_connect (d3->cfg, (void *) 3L, &incoming_tunnel,
880                               &tunnel_cleaner, handlers, &app);
881   }
882   t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L);
883   if (SPEED_MIN == test)
884   {
885     GNUNET_MESH_tunnel_speed_min(t);
886     test = SPEED;
887   }
888   if (SPEED_NOBUF == test)
889   {
890     GNUNET_MESH_tunnel_buffer(t, GNUNET_NO);
891     test = SPEED;
892   }
893   peers_in_tunnel = 0;
894   test_task =
895       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
896                                     (GNUNET_TIME_UNIT_SECONDS, 1), &do_test,
897                                     NULL);
898 }
899
900
901
902 /**
903  * peergroup_ready: start test when all peers are connected
904  * 
905  * @param cls closure
906  * @param emsg error message
907  */
908 static void
909 peergroup_ready (void *cls, const char *emsg)
910 {
911   char *buf;
912   int buf_len;
913   unsigned int i;
914
915   if (emsg != NULL)
916   {
917     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
918                 "Peergroup callback called with error, aborting test!\n");
919     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
920                 "Error from testing: `%s'\n", emsg);
921     ok--;
922     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
923     return;
924   }
925 #if VERBOSE
926   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
927               "************************************************************\n");
928   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
929               "Peer Group started successfully!\n");
930   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
931               "Have %u connections\n",
932               total_connections);
933 #endif
934
935   if (data_file != NULL)
936   {
937     buf = NULL;
938     buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections);
939     if (buf_len > 0)
940       GNUNET_DISK_file_write (data_file, buf, buf_len);
941     GNUNET_free (buf);
942   }
943   peers_running = GNUNET_TESTING_daemons_running (pg);
944   for (i = 0; i < num_peers; i++)
945   {
946     GNUNET_PEER_Id peer_id;
947
948     d1 = GNUNET_TESTING_daemon_get (pg, i);
949     peer_id = GNUNET_PEER_intern (&d1->id);
950     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  %u: %s\n",
951                 peer_id, GNUNET_i2s (&d1->id));
952   }
953   d1 = GNUNET_TESTING_daemon_get (pg, 0);
954   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
955               "Peer looking: %s\n",
956               GNUNET_i2s (&d1->id));
957
958   GNUNET_SCHEDULER_add_now (&connect_mesh_service, NULL);
959   disconnect_task =
960       GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_mesh_peers, NULL);
961
962 }
963
964
965 /**
966  * Function that will be called whenever two daemons are connected by
967  * the testing library.
968  *
969  * @param cls closure
970  * @param first peer id for first daemon
971  * @param second peer id for the second daemon
972  * @param distance distance between the connected peers
973  * @param first_cfg config for the first daemon
974  * @param second_cfg config for the second daemon
975  * @param first_daemon handle for the first daemon
976  * @param second_daemon handle for the second daemon
977  * @param emsg error message (NULL on success)
978  */
979 static void
980 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
981             const struct GNUNET_PeerIdentity *second, uint32_t distance,
982             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
983             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
984             struct GNUNET_TESTING_Daemon *first_daemon,
985             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
986 {
987   if (emsg == NULL)
988   {
989     total_connections++;
990   }
991   else
992   {
993     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
994                 "Problem with new connection (%s)\n",
995                 emsg);
996     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (first));
997     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (second));
998   }
999
1000 }
1001
1002
1003 /**
1004  * run: load configuration options and schedule test to run (start peergroup)
1005  * 
1006  * @param cls closure
1007  * @param args argv
1008  * @param cfgfile configuration file name (can be NULL)
1009  * @param cfg configuration handle
1010  */
1011 static void
1012 run (void *cls, char *const *args, const char *cfgfile,
1013      const struct GNUNET_CONFIGURATION_Handle *cfg)
1014 {
1015   char *temp_str;
1016   struct GNUNET_TESTING_Host *hosts;
1017   char *data_filename;
1018
1019   ok = 0;
1020   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
1021
1022   GNUNET_log_setup ("test_mesh_small",
1023 #if VERBOSE
1024                     "DEBUG",
1025 #else
1026                     "WARNING",
1027 #endif
1028                     NULL);
1029
1030 #if VERBOSE
1031   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1032               "Starting daemons.\n");
1033   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing_old",
1034                                          "use_progressbars", "YES");
1035 #endif
1036
1037   if (GNUNET_OK !=
1038       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
1039                                              "num_peers", &num_peers))
1040   {
1041     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1042                 "Option TESTING:NUM_PEERS is required!\n");
1043     return;
1044   }
1045
1046   if (GNUNET_OK !=
1047       GNUNET_CONFIGURATION_get_value_time (testing_cfg, "test_mesh_small",
1048                                            "WAIT_TIME", &wait_time))
1049   {
1050     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1051                 "Option test_mesh_small:wait_time is required!\n");
1052     return;
1053   }
1054
1055   if (GNUNET_OK !=
1056       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing_old",
1057                                              "topology_output_file",
1058                                              &topology_file))
1059   {
1060     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1061                 "Option test_mesh_small:topology_output_file is required!\n");
1062     return;
1063   }
1064
1065   if (GNUNET_OK ==
1066       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_mesh_small",
1067                                              "data_output_file",
1068                                              &data_filename))
1069   {
1070     data_file =
1071         GNUNET_DISK_file_open (data_filename,
1072                                GNUNET_DISK_OPEN_READWRITE |
1073                                GNUNET_DISK_OPEN_CREATE,
1074                                GNUNET_DISK_PERM_USER_READ |
1075                                GNUNET_DISK_PERM_USER_WRITE);
1076     if (data_file == NULL)
1077     {
1078       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
1079                   data_filename);
1080       GNUNET_free (data_filename);
1081     }
1082   }
1083
1084   if (GNUNET_YES ==
1085       GNUNET_CONFIGURATION_get_value_string (cfg, "test_mesh_small",
1086                                              "output_file", &temp_str))
1087   {
1088     output_file =
1089         GNUNET_DISK_file_open (temp_str,
1090                                GNUNET_DISK_OPEN_READWRITE |
1091                                GNUNET_DISK_OPEN_CREATE,
1092                                GNUNET_DISK_PERM_USER_READ |
1093                                GNUNET_DISK_PERM_USER_WRITE);
1094     if (output_file == NULL)
1095       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
1096                   temp_str);
1097   }
1098   GNUNET_free_non_null (temp_str);
1099
1100   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
1101
1102   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
1103                                        &connect_cb, &peergroup_ready, NULL,
1104                                        hosts);
1105   GNUNET_assert (pg != NULL);
1106   shutdown_handle =
1107     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1108                                     &shutdown_task, NULL);
1109 }
1110
1111
1112
1113 /**
1114  * test_mesh_small command line options
1115  */
1116 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1117   {'V', "verbose", NULL,
1118    gettext_noop ("be verbose (print progress information)"),
1119    0, &GNUNET_GETOPT_set_one, &verbose},
1120   GNUNET_GETOPT_OPTION_END
1121 };
1122
1123
1124 /**
1125  * Main: start test
1126  */
1127 int
1128 main (int argc, char *argv[])
1129 {
1130   char * argv2[] = {
1131     argv[0],
1132     "-c",
1133     "test_mesh_small.conf",
1134 #if VERBOSE
1135     "-L",
1136     "DEBUG",
1137 #endif
1138     NULL
1139   };
1140   int argc2 = (sizeof (argv2) / sizeof (char *)) - 1;
1141
1142   initialized = GNUNET_NO;
1143
1144   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
1145   if (strstr (argv[0], "test_mesh_small_unicast") != NULL)
1146   {
1147     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n");
1148     test = UNICAST;
1149     test_name = "unicast";
1150     ok_goal = 5;
1151   }
1152   else if (strstr (argv[0], "test_mesh_small_multicast") != NULL)
1153   {
1154     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MULTICAST\n");
1155     test = MULTICAST;
1156     test_name = "multicast";
1157     ok_goal = 10;
1158   }
1159   else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL)
1160   {
1161    /* Each peer is supposed to generate the following callbacks:
1162     * 1 incoming tunnel (@dest)
1163     * 1 connected peer (@orig)
1164     * TOTAL_PACKETS received data packet (@dest)
1165     * TOTAL_PACKETS received data packet (@orig)
1166     * 1 received tunnel destroy (@dest)
1167     * _________________________________
1168     * 5 x ok expected per peer
1169     */
1170     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
1171     test = SPEED_ACK;
1172     test_name = "speed ack";
1173     ok_goal = TOTAL_PACKETS * 2 + 3;
1174     argv2 [3] = NULL; // remove -L DEBUG
1175 #if VERBOSE
1176     argc2 -= 2;
1177 #endif
1178   }
1179   else if (strstr (argv[0], "test_mesh_small_speed") != NULL)
1180   {
1181    /* Each peer is supposed to generate the following callbacks:
1182     * 1 incoming tunnel (@dest)
1183     * 1 connected peer (@orig)
1184     * TOTAL_PACKETS received data packet (@dest)
1185     * 1 received data packet (@orig)
1186     * 1 received tunnel destroy (@dest)
1187     * _________________________________
1188     */
1189     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
1190     ok_goal = TOTAL_PACKETS + 4;
1191     if (strstr (argv[0], "_min") != NULL)
1192     {
1193       test = SPEED_MIN;
1194       test_name = "speed min";
1195     }
1196     else if (strstr (argv[0], "_nobuf") != NULL)
1197     {
1198       test = SPEED_NOBUF;
1199       test_name = "speed nobuf";
1200     }
1201     else
1202     {
1203       test = SPEED;
1204       test_name = "speed";
1205     }
1206   }
1207   else
1208   {
1209     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
1210     test = SETUP;
1211     ok_goal = 0;
1212   }
1213
1214   if (strstr (argv[0], "backwards") != NULL)
1215   {
1216     char *aux;
1217
1218     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
1219     test_backwards = GNUNET_YES;
1220     ok_goal++; // need one root->leaf packet to initialize tunnel
1221     aux = malloc (32); // "leaked"
1222     sprintf (aux, "backwards %s", test_name);
1223     test_name = aux;
1224   }
1225
1226   GNUNET_PROGRAM_run (argc2, argv2,
1227                       "test_mesh_small",
1228                       gettext_noop ("Test mesh in a small network."), options,
1229                       &run, NULL);
1230 #if REMOVE_DIR
1231   GNUNET_DISK_directory_remove ("/tmp/test_mesh_small");
1232 #endif
1233   if (ok_goal > ok)
1234   {
1235     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1236                 "FAILED! (%d/%d)\n", ok, ok_goal);
1237     return 1;
1238   }
1239   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
1240   return 0;
1241 }
1242
1243 /* end of test_mesh_small.c */