- fix error check
[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   if (GNUNET_YES == started)
190   {
191     (*sent)++;
192     if (target > *sent)
193       GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
194                                          GNUNET_TIME_UNIT_FOREVER_REL,
195                                          &peer_id, msize, &tmt_rdy, cls);
196   }
197   m->size = htons (msize);
198   m->type = htons (1);
199   msg->data = htonl (*sent - 1);
200   return msize;
201 }
202
203
204 /**
205  * Function is called whenever a message is received.
206  *
207  * @param cls closure (set from GNUNET_MESH_connect)
208  * @param tunnel connection to the other end
209  * @param tunnel_ctx place to store local state associated with the tunnel
210  * @param sender who sent the message
211  * @param message the actual message
212  * @param atsi performance data for the connection
213  * @return GNUNET_OK to keep the connection open,
214  *         GNUNET_SYSERR to close it (signal serious error)
215  */
216 static int
217 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
218                const struct GNUNET_PeerIdentity *sender,
219                const struct GNUNET_MessageHeader *message,
220                const struct GNUNET_ATS_Information *atsi)
221 {
222   struct test_traffic_message *msg;
223   unsigned int *peer_number = cls;
224   unsigned int *got;
225   unsigned int target;
226
227   if (GNUNET_NO == started)
228   {
229     GNUNET_break (2 == *peer_number);
230     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got initial data packet\n");
231     started = GNUNET_YES;
232     start_time = GNUNET_TIME_absolute_get();
233     if (FWD != test) // Send leaf -> root
234       GNUNET_MESH_notify_transmit_ready (tunnel, GNUNET_NO,
235                                         GNUNET_TIME_UNIT_FOREVER_REL,
236                                         NULL,
237                                         sizeof (struct test_traffic_message),
238                                         &tmt_rdy, &two);
239     if (BCK != test) // Send root -> leaf
240       GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
241                                         GNUNET_TIME_UNIT_FOREVER_REL,
242                                         &peer_id,
243                                         sizeof (struct test_traffic_message),
244                                         &tmt_rdy, &one);
245     return GNUNET_OK;
246   }
247
248   if (*peer_number == 1)
249   {
250     got = &got_bck;
251     target = to_send_bck;
252   }
253   else if (*peer_number == 2)
254   {
255     got = &got_fwd;
256     target = to_send_fwd;
257   }
258   else
259   {
260     GNUNET_abort();
261   }
262   msg = (struct test_traffic_message *) message;
263   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got data packet # %u [%u]\n",
264               ntohl (msg->data), got + 1);
265   (*got)++;
266   if (target == *got)
267   {
268     if (to_send_bck == sent_bck && to_send_fwd == sent_fwd) {
269       end_time = GNUNET_TIME_absolute_get();
270       result = GNUNET_OK;
271       finish();
272     }
273     return GNUNET_OK;
274   }
275   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
276     GNUNET_SCHEDULER_cancel (shutdown_task);
277   shutdown_task =
278     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &do_shutdown,
279                                   NULL);
280   return GNUNET_OK;
281 }
282
283
284 /**
285  * Method called whenever another peer has added us to a tunnel
286  * the other peer initiated.
287  *
288  * @param cls closure
289  * @param tunnel new handle to the tunnel
290  * @param initiator peer that started the tunnel
291  * @param atsi performance information for the tunnel
292  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
293  */
294 static void *
295 inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
296                 const struct GNUNET_PeerIdentity *initiator,
297                 const struct GNUNET_ATS_Information *atsi)
298 {
299   unsigned int id = *(unsigned int *) cls;
300
301   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "received incoming tunnel %p\n", tunnel);
302   if (id != 2)
303   {
304     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
305                 "received incoming tunnel on peer 1\n");
306     result = GNUNET_SYSERR;
307   }
308   return NULL;
309 }
310
311
312 /**
313  * Function called whenever an inbound tunnel is destroyed.  Should clean up
314  * any associated state.
315  *
316  * @param cls closure (set from GNUNET_MESH_connect)
317  * @param tunnel connection to the other end (henceforth invalid)
318  * @param tunnel_ctx place where local state associated
319  *                   with the tunnel is stored
320  */
321 static void
322 inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
323              void *tunnel_ctx)
324 {
325   unsigned int id = *(unsigned int *) cls;
326
327   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "incoming tunnel closed\n");
328   if (id != 2)
329   {
330     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
331                 "received closing tunnel on peer 1\n");
332     result = GNUNET_SYSERR;
333   }
334 }
335
336
337 /**
338  * Method called whenever a peer has connected to the tunnel.
339  *
340  * @param cls Closure.
341  * @param peer Peer identity of connected peer.
342  */
343 static void
344 peer_connected (void *cls, const struct GNUNET_PeerIdentity *peer,
345                const struct GNUNET_ATS_Information *atsi)
346 {
347   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer connected\n");
348   peer_id = *peer;
349   /* Force an inbound tunnel notification on peer 2 */
350   GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL,
351                                      peer, sizeof (struct test_traffic_message),
352                                      &tmt_rdy, &one);
353 }
354
355
356 /**
357  * Method called whenever a peer has connected to the tunnel.
358  *
359  * @param cls closure
360  * @param peer peer identity the tunnel was created to, NULL on timeout
361  * @param atsi performance data for the connection
362  */
363 static void
364 peer_disconnected (void *cls, const struct GNUNET_PeerIdentity *peer)
365 {
366   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer disconnected\n");
367 }
368
369
370 /**
371  * Handler array for traffic received on peer1
372  */
373 static struct GNUNET_MESH_MessageHandler handlers[] = {
374   {&data_callback, 1, sizeof (struct test_traffic_message)},
375   {NULL, 0, 0}
376 };
377
378
379 /**
380  * Handler array for traffic received on peer2 (none expected)
381  */
382 static struct GNUNET_MESH_MessageHandler handlers_null[] = { {NULL, 0, 0} };
383
384
385 /**
386  * Initialize framework and start test
387  */
388 static void
389 run (void *cls, 
390      const struct GNUNET_CONFIGURATION_Handle *cfg,
391      struct GNUNET_TESTING_Peer *peer)
392 {
393   static const GNUNET_MESH_ApplicationType app1[] = { 0 };
394   static const GNUNET_MESH_ApplicationType app2[] = { 1, 0 };
395
396   abort_task =
397       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
398                                     (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
399                                     NULL);
400   mesh_peer_1 = GNUNET_MESH_connect (cfg,       /* configuration */
401                                      (void *) &one,     /* cls */
402                                      NULL,      /* inbound new hndlr */
403                                      NULL,      /* inbound end hndlr */
404                                      /* traffic handlers */
405                                      test == FWD ? handlers_null : handlers,
406                                      app1);     /* apps offered */
407
408   mesh_peer_2 = GNUNET_MESH_connect (cfg,       /* configuration */
409                                      (void *) &two,     /* cls */
410                                      &inbound_tunnel,   /* inbound new hndlr */
411                                      &inbound_end,      /* inbound end hndlr */
412                                      handlers,          /* traffic handlers */
413                                      app2);     /* apps offered */
414   if (NULL == mesh_peer_1 || NULL == mesh_peer_2)
415   {
416     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n");
417     result = GNUNET_SYSERR;
418     return;
419   }
420   else
421   {
422     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to mesh\n");
423   }
424   t = GNUNET_MESH_tunnel_create (mesh_peer_1, NULL, &peer_connected,
425                                  &peer_disconnected, (void *) &two);
426   GNUNET_MESH_peer_request_connect_by_type (t, 1);
427 }
428
429
430 /**
431  * Main
432  */
433 int
434 main (int argc, char *argv[])
435 {
436   if (strstr (argv[0], "test_mesh_local_traffic_fwd") != NULL)
437   {
438     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "FWD\n");
439     test = FWD;
440     to_send_fwd = TARGET;
441   }
442   else if (strstr (argv[0], "test_mesh_local_traffic_bck") != NULL)
443   {
444     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BCK\n");
445     test = BCK;
446     to_send_bck = TARGET;
447   }
448   else if (strstr (argv[0], "test_mesh_local_traffic_both") != NULL)
449   {
450     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "BOTH\n");
451     test = BOTH;
452     to_send_bck = to_send_fwd = TARGET;
453   }
454   else
455   {
456     return 1;
457   }
458
459   if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-traffic",
460                                     "test_mesh.conf",
461                                     &run, NULL))
462     return 1;
463   if (result != GNUNET_OK)
464   {
465     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
466                 "Failed.\nFWD expected: %u, Sent: %u, Got: %u\n",
467                 to_send_fwd, sent_fwd, got_fwd);
468     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
469                 "BCK expected: %u, Sent: %u, Got: %u\n",
470                 to_send_bck, sent_bck, got_bck);
471     return 1;
472   }
473   else
474   {
475     struct GNUNET_TIME_Relative total_time;
476     unsigned int total_traffic;
477     char *name;
478
479     total_traffic = BOTH == test ? 2 * TARGET : TARGET;
480     switch (test)
481     {
482       case FWD:
483         name = "Local traffic Root to Leaf";
484         break;
485       case BCK:
486         name = "Local traffic Leaf to Root";
487         break;
488       case BOTH:
489         name = "Local traffic bidirectional";
490         break;
491       default:
492         GNUNET_assert (0);
493     }
494
495     total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
496     FPRINTF (stderr, "\nTest time %llu ms\n",
497              (unsigned long long) total_time.rel_value);
498     FPRINTF (stderr, "Test payload bandwidth: %f kb/s\n",
499              4 * 1000.0 / total_time.rel_value); // 4bytes * ms
500     FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
501              total_traffic * 1000.0 / total_time.rel_value); // 1000 packets * ms
502     GAUGER ("MESH",
503             name,
504             total_traffic * 1000.0 / total_time.rel_value,
505             "packets/s");
506   }
507   return 0;
508 }
509
510 /* end of test_mesh_local_traffic.c */