-enable peer API to return pointer to interned peer hash code
[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, 60)
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 (peer #).
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.
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   switch (client)
513   {
514   case 1L:
515     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Root client got a message!\n");
516     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
517     peers_responded++;
518     if (test == MULTICAST && peers_responded < 2)
519       return GNUNET_OK;
520     break;
521   case 2L:
522   case 3L:
523     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
524                 "Leaf client %li got a message.\n",
525                 client);
526     GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
527     client = 2L;
528     break;
529   default:
530     GNUNET_assert (0);
531     break;
532   }
533
534   if (SPEED == test && GNUNET_YES == test_backwards)
535   {
536     expected_target_client = 1L;
537   }
538   else
539   {
540     expected_target_client = 2L;
541   }
542
543   if (GNUNET_NO == initialized)
544   {
545     initialized = GNUNET_YES;
546     start_time = GNUNET_TIME_absolute_get ();
547     if (SPEED == test)
548     {
549       GNUNET_assert (2L == client);
550       GNUNET_SCHEDULER_add_now (&data_task, NULL);
551       return GNUNET_OK;
552     }
553   }
554
555   if (client == expected_target_client) // Normally 2 or 3
556   {
557     data_received++;
558     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
559                 " received data %u\n", data_received);
560     if (SPEED != test || (ok_goal - 2) == ok)
561     {
562       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
563                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
564                                         sizeof (struct GNUNET_MessageHeader),
565                                         &tmt_rdy, (void *) 1L);
566     }
567     else
568     {
569       if (data_received < TOTAL_PACKETS)
570         return GNUNET_OK;
571     }
572   }
573   else // Normally 1
574   {
575     if (test == SPEED_ACK || test == SPEED)
576     {
577       data_ack++;
578       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579               " received ack %u\n", data_ack);
580       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
581                                         GNUNET_TIME_UNIT_FOREVER_REL, sender,
582                                         sizeof (struct GNUNET_MessageHeader),
583                                         &tmt_rdy, (void *) 1L);
584       if (data_ack < TOTAL_PACKETS && SPEED != test)
585         return GNUNET_OK;
586       if (ok == 2 && SPEED == test)
587         return GNUNET_OK;
588       show_end_data();
589     }
590     GNUNET_MESH_tunnel_destroy (t);
591     t = NULL;
592   }
593
594   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
595   {
596     GNUNET_SCHEDULER_cancel (disconnect_task);
597     disconnect_task =
598         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers,
599                                       NULL);
600   }
601
602   return GNUNET_OK;
603 }
604
605
606 /**
607  * Handlers, for diverse services
608  */
609 static struct GNUNET_MESH_MessageHandler handlers[] = {
610   {&data_callback, 1, sizeof (struct GNUNET_MessageHeader)},
611   {NULL, 0, 0}
612 };
613
614
615 /**
616  * Method called whenever another peer has added us to a tunnel
617  * the other peer initiated.
618  *
619  * @param cls closure
620  * @param tunnel new handle to the tunnel
621  * @param initiator peer that started the tunnel
622  * @param atsi performance information for the tunnel
623  * @return initial tunnel context for the tunnel
624  *         (can be NULL -- that's not an error)
625  */
626 static void *
627 incoming_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
628                  const struct GNUNET_PeerIdentity *initiator,
629                  const struct GNUNET_ATS_Information *atsi)
630 {
631   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
632               "Incoming tunnel from %s to peer %d\n",
633               GNUNET_i2s (initiator), (long) cls);
634   ok++;
635   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
636   if ((long) cls == 2L)
637     incoming_t = tunnel;
638   else if ((long) cls == 3L)
639     incoming_t2 = tunnel;
640   else
641   {
642     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
643                 "Incoming tunnel for unknown client %lu\n", (long) cls);
644     GNUNET_break(0);
645   }
646   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
647   {
648     GNUNET_SCHEDULER_cancel (disconnect_task);
649     disconnect_task =
650         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
651   }
652
653   return NULL;
654 }
655
656 /**
657  * Function called whenever an inbound tunnel is destroyed.  Should clean up
658  * any associated state.
659  *
660  * @param cls closure (set from GNUNET_MESH_connect)
661  * @param tunnel connection to the other end (henceforth invalid)
662  * @param tunnel_ctx place where local state associated
663  *                   with the tunnel is stored
664  */
665 static void
666 tunnel_cleaner (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
667                 void *tunnel_ctx)
668 {
669   long i = (long) cls;
670
671   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
672               "Incoming tunnel disconnected at peer %d\n",
673               i);
674   if (2L == i)
675   {
676     ok++;
677     incoming_t = NULL;
678   }
679   else if (3L == i)
680   {
681     ok++;
682     incoming_t2 = NULL;
683   }
684   else
685     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
686                 "Unknown peer! %d\n", i);
687   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
688   peers_in_tunnel--;
689   if (peers_in_tunnel > 0)
690     return;
691
692   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
693   {
694     GNUNET_SCHEDULER_cancel (disconnect_task);
695     disconnect_task = GNUNET_SCHEDULER_add_now (&disconnect_mesh_peers, NULL);
696   }
697
698   return;
699 }
700
701
702 /**
703  * Method called whenever a tunnel falls apart.
704  *
705  * @param cls closure
706  * @param peer peer identity the tunnel stopped working with
707  */
708 static void
709 dh (void *cls, const struct GNUNET_PeerIdentity *peer)
710 {
711   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
712               "peer %s disconnected\n",
713               GNUNET_i2s (peer));
714   return;
715 }
716
717
718 /**
719  * Method called whenever a peer connects to a tunnel.
720  *
721  * @param cls closure
722  * @param peer peer identity the tunnel was created to, NULL on timeout
723  * @param atsi performance data for the connection
724  */
725 static void
726 ch (void *cls, const struct GNUNET_PeerIdentity *peer,
727     const struct GNUNET_ATS_Information *atsi)
728 {
729   struct GNUNET_PeerIdentity *dest;
730
731   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
732               "peer %s connected\n", GNUNET_i2s (peer));
733
734   if (0 == memcmp (&d2->id, peer, sizeof (d2->id)) && (long) cls == 1L)
735   {
736     ok++;
737   }
738   if (test == MULTICAST && 0 == memcmp (&d3->id, peer, sizeof (d3->id)) &&
739       (long) cls == 1L)
740   {
741     ok++;
742   }
743   GNUNET_log (GNUNET_ERROR_TYPE_INFO, " ok: %d\n", ok);
744   switch (test)
745   {
746     case UNICAST:
747     case SPEED:
748     case SPEED_ACK:
749       // incoming_t is NULL unless we send a relevant data packet
750       dest = &d2->id;
751       break;
752     case MULTICAST:
753       peers_in_tunnel++;
754       if (peers_in_tunnel < 2)
755         return;
756       dest = NULL;
757       break;
758     default:
759       return;
760   }
761   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
762   {
763     GNUNET_SCHEDULER_cancel (disconnect_task);
764     disconnect_task =
765         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
766     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
767                 "Sending data initializer...\n");
768     peers_responded = 0;
769     data_ack = 0;
770     data_received = 0;
771     data_sent = 0;
772     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
773                                        GNUNET_TIME_UNIT_FOREVER_REL, dest,
774                                        sizeof (struct GNUNET_MessageHeader),
775                                        &tmt_rdy, (void *) 1L);
776   }
777   else
778   {
779     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
780                 "Disconnect already run?\n");
781     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
782                 "Aborting...\n");
783   }
784   return;
785 }
786
787
788 /**
789  * START THE TESTCASE ITSELF, AS WE ARE CONNECTED TO THE MESH SERVICES.
790  * 
791  * Testcase continues when the root receives confirmation of connected peers,
792  * on callback funtion ch.
793  * 
794  * @param cls Closure (unsued).
795  * @param tc Task Context.
796  */
797 static void
798 do_test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
799 {
800   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test_task\n");
801   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "add peer 2\n");
802   GNUNET_MESH_peer_request_connect_add (t, &d2->id);
803
804   if (test == MULTICAST)
805   {
806     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807                 "add peer 3\n");
808     GNUNET_MESH_peer_request_connect_add (t, &d3->id);
809   }
810
811   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
812               "schedule timeout in SHORT_TIME\n");
813   if (GNUNET_SCHEDULER_NO_TASK != disconnect_task)
814   {
815     GNUNET_SCHEDULER_cancel (disconnect_task);
816     disconnect_task =
817         GNUNET_SCHEDULER_add_delayed (SHORT_TIME, &disconnect_mesh_peers, NULL);
818   }
819 }
820
821
822 /**
823  * connect_mesh_service: connect to the mesh service of one of the peers
824  *
825  * @param cls Closure (unsued).
826  * @param tc Task Context.
827  */
828 static void
829 connect_mesh_service (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
830 {
831   GNUNET_MESH_ApplicationType app;
832
833   if ((GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason) != 0)
834     return;
835
836   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
837               "connect_mesh_service\n");
838
839   d2 = GNUNET_TESTING_daemon_get (pg, num_peers - 1);
840   if (test == MULTICAST)
841   {
842     d3 = GNUNET_TESTING_daemon_get (pg, num_peers - 2);
843   }
844   app = (GNUNET_MESH_ApplicationType) 0;
845
846 #if VERBOSE
847   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
848               "connecting to mesh service of peer %s\n",
849               GNUNET_i2s (&d1->id));
850   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
851               "connecting to mesh service of peer %s\n",
852               GNUNET_i2s (&d2->id));
853   if (test == MULTICAST)
854   {
855     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
856                 "connecting to mesh service of peer %s\n",
857                 GNUNET_i2s (&d3->id));
858   }
859 #endif
860   h1 = GNUNET_MESH_connect (d1->cfg, (void *) 1L, NULL, &tunnel_cleaner,
861                             handlers, &app);
862   h2 = GNUNET_MESH_connect (d2->cfg, (void *) 2L, &incoming_tunnel,
863                             &tunnel_cleaner, handlers, &app);
864   if (test == MULTICAST)
865   {
866     h3 = GNUNET_MESH_connect (d3->cfg, (void *) 3L, &incoming_tunnel,
867                               &tunnel_cleaner, handlers, &app);
868   }
869   t = GNUNET_MESH_tunnel_create (h1, NULL, &ch, &dh, (void *) 1L);
870   if (SPEED_MIN == test)
871   {
872     GNUNET_MESH_tunnel_speed_min(t);
873     test = SPEED;
874   }
875   if (SPEED_NOBUF == test)
876   {
877     GNUNET_MESH_tunnel_buffer(t, GNUNET_NO);
878     test = SPEED;
879   }
880   peers_in_tunnel = 0;
881   test_task =
882       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
883                                     (GNUNET_TIME_UNIT_SECONDS, 1), &do_test,
884                                     NULL);
885 }
886
887
888
889 /**
890  * peergroup_ready: start test when all peers are connected
891  * 
892  * @param cls closure
893  * @param emsg error message
894  */
895 static void
896 peergroup_ready (void *cls, const char *emsg)
897 {
898   char *buf;
899   int buf_len;
900   unsigned int i;
901
902   if (emsg != NULL)
903   {
904     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
905                 "Peergroup callback called with error, aborting test!\n");
906     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
907                 "Error from testing: `%s'\n", emsg);
908     ok--;
909     GNUNET_TESTING_daemons_stop (pg, TIMEOUT, &shutdown_callback, NULL);
910     return;
911   }
912 #if VERBOSE
913   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
914               "************************************************************\n");
915   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
916               "Peer Group started successfully!\n");
917   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
918               "Have %u connections\n",
919               total_connections);
920 #endif
921
922   if (data_file != NULL)
923   {
924     buf = NULL;
925     buf_len = GNUNET_asprintf (&buf, "CONNECTIONS_0: %u\n", total_connections);
926     if (buf_len > 0)
927       GNUNET_DISK_file_write (data_file, buf, buf_len);
928     GNUNET_free (buf);
929   }
930   peers_running = GNUNET_TESTING_daemons_running (pg);
931   for (i = 0; i < num_peers; i++)
932   {
933     GNUNET_PEER_Id peer_id;
934
935     d1 = GNUNET_TESTING_daemon_get (pg, i);
936     peer_id = GNUNET_PEER_intern (&d1->id);
937     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "  %u: %s\n",
938                 peer_id, GNUNET_i2s (&d1->id));
939   }
940   d1 = GNUNET_TESTING_daemon_get (pg, 0);
941   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
942               "Peer looking: %s\n",
943               GNUNET_i2s (&d1->id));
944
945   GNUNET_SCHEDULER_add_now (&connect_mesh_service, NULL);
946   disconnect_task =
947       GNUNET_SCHEDULER_add_delayed (wait_time, &disconnect_mesh_peers, NULL);
948
949 }
950
951
952 /**
953  * Function that will be called whenever two daemons are connected by
954  * the testing library.
955  *
956  * @param cls closure
957  * @param first peer id for first daemon
958  * @param second peer id for the second daemon
959  * @param distance distance between the connected peers
960  * @param first_cfg config for the first daemon
961  * @param second_cfg config for the second daemon
962  * @param first_daemon handle for the first daemon
963  * @param second_daemon handle for the second daemon
964  * @param emsg error message (NULL on success)
965  */
966 static void
967 connect_cb (void *cls, const struct GNUNET_PeerIdentity *first,
968             const struct GNUNET_PeerIdentity *second, uint32_t distance,
969             const struct GNUNET_CONFIGURATION_Handle *first_cfg,
970             const struct GNUNET_CONFIGURATION_Handle *second_cfg,
971             struct GNUNET_TESTING_Daemon *first_daemon,
972             struct GNUNET_TESTING_Daemon *second_daemon, const char *emsg)
973 {
974   if (emsg == NULL)
975   {
976     total_connections++;
977   }
978   else
979   {
980     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
981                 "Problem with new connection (%s)\n",
982                 emsg);
983     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (first));
984     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, " (%s)\n", GNUNET_i2s (second));
985   }
986
987 }
988
989
990 /**
991  * run: load configuration options and schedule test to run (start peergroup)
992  * 
993  * @param cls closure
994  * @param args argv
995  * @param cfgfile configuration file name (can be NULL)
996  * @param cfg configuration handle
997  */
998 static void
999 run (void *cls, char *const *args, const char *cfgfile,
1000      const struct GNUNET_CONFIGURATION_Handle *cfg)
1001 {
1002   char *temp_str;
1003   struct GNUNET_TESTING_Host *hosts;
1004   char *data_filename;
1005
1006   ok = 0;
1007   testing_cfg = GNUNET_CONFIGURATION_dup (cfg);
1008
1009   GNUNET_log_setup ("test_mesh_small",
1010 #if VERBOSE
1011                     "DEBUG",
1012 #else
1013                     "WARNING",
1014 #endif
1015                     NULL);
1016
1017 #if VERBOSE
1018   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1019               "Starting daemons.\n");
1020   GNUNET_CONFIGURATION_set_value_string (testing_cfg, "testing_old",
1021                                          "use_progressbars", "YES");
1022 #endif
1023
1024   if (GNUNET_OK !=
1025       GNUNET_CONFIGURATION_get_value_number (testing_cfg, "testing_old",
1026                                              "num_peers", &num_peers))
1027   {
1028     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1029                 "Option TESTING:NUM_PEERS is required!\n");
1030     return;
1031   }
1032
1033   if (GNUNET_OK !=
1034       GNUNET_CONFIGURATION_get_value_time (testing_cfg, "test_mesh_small",
1035                                            "WAIT_TIME", &wait_time))
1036   {
1037     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1038                 "Option test_mesh_small:wait_time is required!\n");
1039     return;
1040   }
1041
1042   if (GNUNET_OK !=
1043       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "testing_old",
1044                                              "topology_output_file",
1045                                              &topology_file))
1046   {
1047     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1048                 "Option test_mesh_small:topology_output_file is required!\n");
1049     return;
1050   }
1051
1052   if (GNUNET_OK ==
1053       GNUNET_CONFIGURATION_get_value_string (testing_cfg, "test_mesh_small",
1054                                              "data_output_file",
1055                                              &data_filename))
1056   {
1057     data_file =
1058         GNUNET_DISK_file_open (data_filename,
1059                                GNUNET_DISK_OPEN_READWRITE |
1060                                GNUNET_DISK_OPEN_CREATE,
1061                                GNUNET_DISK_PERM_USER_READ |
1062                                GNUNET_DISK_PERM_USER_WRITE);
1063     if (data_file == NULL)
1064     {
1065       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
1066                   data_filename);
1067       GNUNET_free (data_filename);
1068     }
1069   }
1070
1071   if (GNUNET_YES ==
1072       GNUNET_CONFIGURATION_get_value_string (cfg, "test_mesh_small",
1073                                              "output_file", &temp_str))
1074   {
1075     output_file =
1076         GNUNET_DISK_file_open (temp_str,
1077                                GNUNET_DISK_OPEN_READWRITE |
1078                                GNUNET_DISK_OPEN_CREATE,
1079                                GNUNET_DISK_PERM_USER_READ |
1080                                GNUNET_DISK_PERM_USER_WRITE);
1081     if (output_file == NULL)
1082       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to open %s for output!\n",
1083                   temp_str);
1084   }
1085   GNUNET_free_non_null (temp_str);
1086
1087   hosts = GNUNET_TESTING_hosts_load (testing_cfg);
1088
1089   pg = GNUNET_TESTING_peergroup_start (testing_cfg, num_peers, TIMEOUT,
1090                                        &connect_cb, &peergroup_ready, NULL,
1091                                        hosts);
1092   GNUNET_assert (pg != NULL);
1093   shutdown_handle =
1094     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1095                                     &shutdown_task, NULL);
1096 }
1097
1098
1099
1100 /**
1101  * test_mesh_small command line options
1102  */
1103 static struct GNUNET_GETOPT_CommandLineOption options[] = {
1104   {'V', "verbose", NULL,
1105    gettext_noop ("be verbose (print progress information)"),
1106    0, &GNUNET_GETOPT_set_one, &verbose},
1107   GNUNET_GETOPT_OPTION_END
1108 };
1109
1110
1111 /**
1112  * Main: start test
1113  */
1114 int
1115 main (int argc, char *argv[])
1116 {
1117   char * argv2[] = {
1118     argv[0],
1119     "-c",
1120     "test_mesh_small.conf",
1121 #if VERBOSE
1122     "-L",
1123     "DEBUG",
1124 #endif
1125     NULL
1126   };
1127   int argc2 = (sizeof (argv2) / sizeof (char *)) - 1;
1128
1129   initialized = GNUNET_NO;
1130
1131   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start\n");
1132   if (strstr (argv[0], "test_mesh_small_unicast") != NULL)
1133   {
1134     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNICAST\n");
1135     test = UNICAST;
1136     test_name = "unicast";
1137     ok_goal = 5;
1138   }
1139   else if (strstr (argv[0], "test_mesh_small_multicast") != NULL)
1140   {
1141     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "MULTICAST\n");
1142     test = MULTICAST;
1143     test_name = "multicast";
1144     ok_goal = 10;
1145   }
1146   else if (strstr (argv[0], "test_mesh_small_speed_ack") != NULL)
1147   {
1148    /* Each peer is supposed to generate the following callbacks:
1149     * 1 incoming tunnel (@dest)
1150     * 1 connected peer (@orig)
1151     * TOTAL_PACKETS received data packet (@dest)
1152     * TOTAL_PACKETS received data packet (@orig)
1153     * 1 received tunnel destroy (@dest)
1154     * _________________________________
1155     * 5 x ok expected per peer
1156     */
1157     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED_ACK\n");
1158     test = SPEED_ACK;
1159     test_name = "speed ack";
1160     ok_goal = TOTAL_PACKETS * 2 + 3;
1161     argv2 [3] = NULL; // remove -L DEBUG
1162 #if VERBOSE
1163     argc2 -= 2;
1164 #endif
1165   }
1166   else if (strstr (argv[0], "test_mesh_small_speed") != NULL)
1167   {
1168    /* Each peer is supposed to generate the following callbacks:
1169     * 1 incoming tunnel (@dest)
1170     * 1 connected peer (@orig)
1171     * TOTAL_PACKETS received data packet (@dest)
1172     * 1 received data packet (@orig)
1173     * 1 received tunnel destroy (@dest)
1174     * _________________________________
1175     */
1176     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "SPEED\n");
1177     ok_goal = TOTAL_PACKETS + 4;
1178     if (strstr (argv[0], "_min") != NULL)
1179     {
1180       test = SPEED_MIN;
1181       test_name = "speed min";
1182     }
1183     else if (strstr (argv[0], "_nobuf") != NULL)
1184     {
1185       test = SPEED_NOBUF;
1186       test_name = "speed nobuf";
1187     }
1188     else
1189     {
1190       test = SPEED;
1191       test_name = "speed";
1192     }
1193   }
1194   else
1195   {
1196     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "UNKNOWN\n");
1197     test = SETUP;
1198     ok_goal = 0;
1199   }
1200
1201   if (strstr (argv[0], "backwards") != NULL)
1202   {
1203     char *aux;
1204
1205     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BACKWARDS (LEAF TO ROOT)\n");
1206     test_backwards = GNUNET_YES;
1207     ok_goal++; // need one root->leaf packet to initialize tunnel
1208     aux = malloc (32); // "leaked"
1209     sprintf (aux, "backwards %s", test_name);
1210     test_name = aux;
1211   }
1212
1213   GNUNET_PROGRAM_run (argc2, argv2,
1214                       "test_mesh_small",
1215                       gettext_noop ("Test mesh in a small network."), options,
1216                       &run, NULL);
1217 #if REMOVE_DIR
1218   GNUNET_DISK_directory_remove ("/tmp/test_mesh_small");
1219 #endif
1220   if (ok_goal > ok)
1221   {
1222     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1223                 "FAILED! (%d/%d)\n", ok, ok_goal);
1224     return 1;
1225   }
1226   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "success\n");
1227   return 0;
1228 }
1229
1230 /* end of test_mesh_small.c */