- use strings
[oweals/gnunet.git] / src / mesh / test_mesh_local_traffic.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 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 /**
22  * @file mesh/test_mesh_local_traffic.c
23  * @brief test mesh local traffic: test of tunnels with just one peer
24  * @author Bartlomiej Polot
25  */
26
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "gnunet_mesh_service.h"
30 #include "gnunet_testing_lib-new.h"
31 #include <gauger.h>
32
33 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
34
35 #define TARGET 1000
36
37 /**
38  * DIFFERENT TESTS TO RUN
39  */
40 #define FWD 0
41 #define BCK 1
42 #define BOTH 2
43
44
45 GNUNET_NETWORK_STRUCT_BEGIN
46
47 struct test_traffic_message
48 {
49   struct GNUNET_MessageHeader header;
50   uint32_t data GNUNET_PACKED;
51 };
52
53 GNUNET_NETWORK_STRUCT_END
54
55
56 /** Which test to run, based on executable name */
57 static int test;
58
59 static int started;
60
61 /** How many packets to send from root to leaf */
62 static unsigned int to_send_fwd;
63
64 /** How many packets to send from leaf to root */
65 static unsigned int to_send_bck;
66
67 static unsigned int sent_fwd = 0;
68
69 static unsigned int got_fwd = 0;
70
71 static unsigned int sent_bck = 0;
72
73 static unsigned int got_bck = 0;
74
75 static struct GNUNET_MESH_Handle *mesh_peer_1;
76
77 static struct GNUNET_MESH_Handle *mesh_peer_2;
78
79 static struct GNUNET_MESH_Tunnel *t;
80
81 static unsigned int one = 1;
82
83 static unsigned int two = 2;
84
85 static int result = GNUNET_SYSERR;
86
87 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
88
89 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
90
91 static struct GNUNET_TIME_Absolute start_time;
92
93 static struct GNUNET_TIME_Absolute end_time;
94
95 static struct GNUNET_PeerIdentity peer_id;
96
97
98 /**
99  * Shutdown nicely
100  */
101 static void
102 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
103 {
104   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutdown\n");
105   if (0 != abort_task)
106   {
107     GNUNET_SCHEDULER_cancel (abort_task);
108   }
109   if (NULL != t)
110   {
111     GNUNET_MESH_tunnel_destroy(t);
112   }
113   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D1\n");
114   if (NULL != mesh_peer_1)
115   {
116     GNUNET_MESH_disconnect (mesh_peer_1);
117   }
118   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D2\n");
119   if (NULL != mesh_peer_2)
120   {
121     GNUNET_MESH_disconnect (mesh_peer_2);
122   }
123 }
124
125
126 /**
127  * Something went wrong and timed out. Kill everything and set error flag
128  */
129 static void
130 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
131 {
132   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ABORT\n");
133   result = GNUNET_SYSERR;
134   abort_task = 0;
135   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
136   {
137     GNUNET_SCHEDULER_cancel (shutdown_task);
138     shutdown_task = GNUNET_SCHEDULER_NO_TASK;
139   }
140   do_shutdown (cls, tc);
141 }
142
143 static void
144 finish(void)
145 {
146   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
147     GNUNET_SCHEDULER_cancel(shutdown_task);
148   shutdown_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
149                                                  &do_shutdown, NULL);
150 }
151
152
153 /**
154  * Transmit ready callback.
155  * 
156  * @param cls Closure (peer number of peer sending the data).
157  * @param size Buffer size.
158  * @param buf Buffer.
159  */
160 static size_t
161 tmt_rdy (void *cls, size_t size, void *buf)
162 {
163   unsigned int peer_number = *(unsigned int *) cls;
164   struct GNUNET_MessageHeader *m = buf;
165   struct test_traffic_message *msg = buf;
166   size_t msize = sizeof (struct test_traffic_message);
167   unsigned int *sent;
168   unsigned int target;
169
170   if (0 == size || NULL == buf)
171     return 0;
172
173   if (1 == peer_number)
174   {
175     sent = &sent_fwd;
176     target = to_send_fwd;
177   }
178   else if (2 == peer_number)
179   {
180     sent = &sent_bck;
181     target = to_send_bck;
182   }
183   else
184     GNUNET_abort();
185
186   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending data packet # %u\n",
187               *sent);
188   GNUNET_assert (size >= msize);
189   (*sent)++;
190   if (target > *sent)
191     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
192                                        GNUNET_TIME_UNIT_FOREVER_REL,
193                                        &peer_id, msize, &tmt_rdy, cls);
194   m->size = htons (msize);
195   m->type = htons (1);
196   msg->data = htonl (*sent - 1);
197   return msize;
198 }
199
200
201 /**
202  * Function is called whenever a message is received.
203  *
204  * @param cls closure (set from GNUNET_MESH_connect)
205  * @param tunnel connection to the other end
206  * @param tunnel_ctx place to store local state associated with the tunnel
207  * @param sender who sent the message
208  * @param message the actual message
209  * @param atsi performance data for the connection
210  * @return GNUNET_OK to keep the connection open,
211  *         GNUNET_SYSERR to close it (signal serious error)
212  */
213 static int
214 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
215                const struct GNUNET_PeerIdentity *sender,
216                const struct GNUNET_MessageHeader *message,
217                const struct GNUNET_ATS_Information *atsi)
218 {
219   struct test_traffic_message *msg;
220   unsigned int *peer_number = cls;
221   unsigned int *got;
222   unsigned int target;
223
224   if (GNUNET_NO == started)
225   {
226     GNUNET_break (2 == *peer_number);
227     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got initial data packet\n");
228     started = GNUNET_YES;
229     return GNUNET_OK;
230   }
231
232   if (*peer_number == 1)
233   {
234     got = &got_bck;
235     target = to_send_bck;
236   }
237   else if (*peer_number == 2)
238   {
239     got = &got_fwd;
240     target = to_send_fwd;
241   }
242   else
243   {
244     GNUNET_abort();
245   }
246   msg = (struct test_traffic_message *) message;
247   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got data packet # %u [%u]\n",
248               ntohl (msg->data), got + 1);
249   (*got)++;
250   if (target == *got)
251   {
252     if (to_send_bck == sent_bck && to_send_fwd == sent_fwd) {
253       end_time = GNUNET_TIME_absolute_get();
254       result = GNUNET_OK;
255       finish();
256     }
257     return GNUNET_OK;
258   }
259   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
260     GNUNET_SCHEDULER_cancel (shutdown_task);
261   shutdown_task =
262     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &do_shutdown,
263                                   NULL);
264   return GNUNET_OK;
265 }
266
267
268 /**
269  * Method called whenever another peer has added us to a tunnel
270  * the other peer initiated.
271  *
272  * @param cls closure
273  * @param tunnel new handle to the tunnel
274  * @param initiator peer that started the tunnel
275  * @param atsi performance information for the tunnel
276  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
277  */
278 static void *
279 inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
280                 const struct GNUNET_PeerIdentity *initiator,
281                 const struct GNUNET_ATS_Information *atsi)
282 {
283   unsigned int id = *(unsigned int *) cls;
284
285   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "received incoming tunnel\n");
286   if (id != 2)
287   {
288     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
289                 "received incoming tunnel on peer 1\n");
290     result = GNUNET_SYSERR;
291   }
292   start_time = GNUNET_TIME_absolute_get();
293   if (FWD != test) // Send leaf -> root
294     GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
295                                        GNUNET_TIME_UNIT_FOREVER_REL,
296                                        NULL,
297                                        sizeof (struct test_traffic_message),
298                                        &tmt_rdy, &two);
299   if (BCK != test) // Send root -> leaf
300     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
301                                        GNUNET_TIME_UNIT_FOREVER_REL,
302                                        &peer_id,
303                                        sizeof (struct test_traffic_message),
304                                        &tmt_rdy, &one);
305   return NULL;
306 }
307
308
309 /**
310  * Function called whenever an inbound tunnel is destroyed.  Should clean up
311  * any associated state.
312  *
313  * @param cls closure (set from GNUNET_MESH_connect)
314  * @param tunnel connection to the other end (henceforth invalid)
315  * @param tunnel_ctx place where local state associated
316  *                   with the tunnel is stored
317  */
318 static void
319 inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
320              void *tunnel_ctx)
321 {
322   unsigned int id = *(unsigned int *) cls;
323
324   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "incoming tunnel closed\n");
325   if (id != 1)
326   {
327     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
328                 "received closing tunnel on peer 2\n");
329     result = GNUNET_SYSERR;
330   }
331 }
332
333
334 /**
335  * Method called whenever a peer has connected to the tunnel.
336  *
337  * @param cls Closure.
338  * @param peer Peer identity of connected peer.
339  */
340 static void
341 peer_connected (void *cls, const struct GNUNET_PeerIdentity *peer,
342                const struct GNUNET_ATS_Information *atsi)
343 {
344   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer connected\n");
345   peer_id = *peer;
346   /* Force an inbound tunnel notification on peer 2 */
347   GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL,
348                                      peer, sizeof (struct test_traffic_message),
349                                      &tmt_rdy, &one);
350 }
351
352
353 /**
354  * Method called whenever a peer has connected to the tunnel.
355  *
356  * @param cls closure
357  * @param peer peer identity the tunnel was created to, NULL on timeout
358  * @param atsi performance data for the connection
359  */
360 static void
361 peer_disconnected (void *cls, const struct GNUNET_PeerIdentity *peer)
362 {
363   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer disconnected\n");
364 }
365
366
367 /**
368  * Handler array for traffic received on peer1
369  */
370 static struct GNUNET_MESH_MessageHandler handlers[] = {
371   {&data_callback, 1, sizeof (struct test_traffic_message)},
372   {NULL, 0, 0}
373 };
374
375
376 /**
377  * Handler array for traffic received on peer2 (none expected)
378  */
379 static struct GNUNET_MESH_MessageHandler handlers_null[] = { {NULL, 0, 0} };
380
381
382 /**
383  * Initialize framework and start test
384  */
385 static void
386 run (void *cls, 
387      const struct GNUNET_CONFIGURATION_Handle *cfg,
388      struct GNUNET_TESTING_Peer *peer)
389 {
390   static const GNUNET_MESH_ApplicationType app1[] = { 0 };
391   static const GNUNET_MESH_ApplicationType app2[] = { 1, 0 };
392
393   abort_task =
394       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
395                                     (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
396                                     NULL);
397   mesh_peer_1 = GNUNET_MESH_connect (cfg,       /* configuration */
398                                      (void *) &one,     /* cls */
399                                      NULL,      /* inbound new hndlr */
400                                      NULL,      /* inbound end hndlr */
401                                      /* traffic handlers */
402                                      test == FWD ? handlers_null : handlers,
403                                      app1);     /* apps offered */
404
405   mesh_peer_2 = GNUNET_MESH_connect (cfg,       /* configuration */
406                                      (void *) &two,     /* cls */
407                                      &inbound_tunnel,   /* inbound new hndlr */
408                                      &inbound_end,      /* inbound end hndlr */
409                                      handlers,          /* traffic handlers */
410                                      app2);     /* apps offered */
411   if (NULL == mesh_peer_1 || NULL == mesh_peer_2)
412   {
413     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n");
414     result = GNUNET_SYSERR;
415     return;
416   }
417   else
418   {
419     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to mesh\n");
420   }
421   t = GNUNET_MESH_tunnel_create (mesh_peer_1, NULL, &peer_connected,
422                                  &peer_disconnected, (void *) &two);
423   GNUNET_MESH_peer_request_connect_by_type (t, 1);
424 }
425
426
427 /**
428  * Main
429  */
430 int
431 main (int argc, char *argv[])
432 {
433   if (strstr (argv[0], "test_mesh_local_traffic_fwd") != NULL)
434   {
435     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FWD\n");
436     test = FWD;
437     to_send_fwd = TARGET;
438   }
439   else if (strstr (argv[0], "test_mesh_local_traffic_bck") != NULL)
440   {
441     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BCK\n");
442     test = BCK;
443     to_send_bck = TARGET;
444   }
445   else if (strstr (argv[0], "test_mesh_local_traffic_both") != NULL)
446   {
447     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BOTH\n");
448     test = BOTH;
449     to_send_bck = to_send_fwd = TARGET;
450   }
451   else
452   {
453     return 1;
454   }
455
456   if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-traffic",
457                                     "test_mesh.conf",
458                                     &run, NULL))
459     return 1;
460   if (result != GNUNET_OK)
461   {
462     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
463                 "Failed.\nFWD expected: %u, Sent: %u, Got: %u\n",
464                 to_send_fwd, sent_fwd, got_fwd);
465     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
466                 "BCK expected: %u, Sent: %u, Got: %u\n",
467                 to_send_bck, sent_bck, got_bck);
468     return 1;
469   }
470   else
471   {
472     struct GNUNET_TIME_Relative total_time;
473     unsigned int total_traffic;
474     char *name;
475
476     total_traffic = BOTH == test ? 2 * TARGET : TARGET;
477     switch (test)
478     {
479       case FWD:
480         name = "Local traffic Root to Leaf";
481         break;
482       case BCK:
483         name = "Local traffic Leaf to Root";
484         break;
485       case BOTH:
486         name = "Local traffic bidirectional";
487         break;
488       default:
489         GNUNET_assert (0);
490     }
491
492     total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
493     FPRINTF (stderr, "\nTest time %llu ms\n",
494              (unsigned long long) total_time.rel_value);
495     FPRINTF (stderr, "Test payload bandwidth: %f kb/s\n",
496              4 * 1000.0 / total_time.rel_value); // 4bytes * ms
497     FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
498              total_traffic * 1000.0 / total_time.rel_value); // 1000 packets * ms
499     GAUGER ("MESH",
500             name,
501             total_traffic * 1000.0 / total_time.rel_value,
502             "packets/s");
503   }
504   return 0;
505 }
506
507 /* end of test_mesh_local_traffic.c */