- add a test for traffic between two clients of a single peer
[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 100
36
37
38 static struct GNUNET_MESH_Handle *mesh_peer_1;
39
40 static struct GNUNET_MESH_Handle *mesh_peer_2;
41
42 static struct GNUNET_MESH_Tunnel *t;
43
44 static unsigned int one = 1;
45
46 static unsigned int two = 2;
47
48 static int result = GNUNET_SYSERR;
49
50 static unsigned int sent = 0;
51
52 static unsigned int got = 0;
53
54 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
55
56 static GNUNET_SCHEDULER_TaskIdentifier shutdown_task;
57
58 static struct GNUNET_TIME_Absolute start_time;
59
60 static struct GNUNET_TIME_Absolute end_time;
61
62 static struct GNUNET_PeerIdentity peer_id;
63
64
65 /**
66  * Shutdown nicely
67  */
68 static void
69 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
70 {
71   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "shutdown\n");
72   if (0 != abort_task)
73   {
74     GNUNET_SCHEDULER_cancel (abort_task);
75   }
76   if (NULL != t)
77   {
78     GNUNET_MESH_tunnel_destroy(t);
79   }
80   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D1\n");
81   if (NULL != mesh_peer_1)
82   {
83     GNUNET_MESH_disconnect (mesh_peer_1);
84   }
85   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "D2\n");
86   if (NULL != mesh_peer_2)
87   {
88     GNUNET_MESH_disconnect (mesh_peer_2);
89   }
90 }
91
92
93 /**
94  * Something went wrong and timed out. Kill everything and set error flag
95  */
96 static void
97 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
98 {
99   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "ABORT\n");
100   result = GNUNET_SYSERR;
101   abort_task = 0;
102   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
103   {
104     GNUNET_SCHEDULER_cancel (shutdown_task);
105     shutdown_task = GNUNET_SCHEDULER_NO_TASK;
106   }
107   do_shutdown (cls, tc);
108 }
109
110 static void
111 finish(void)
112 {
113   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
114     GNUNET_SCHEDULER_cancel(shutdown_task);
115   shutdown_task =  GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
116                                                  &do_shutdown, NULL);
117 }
118
119
120 /**
121  * Function is called whenever a message is received.
122  *
123  * @param cls closure (set from GNUNET_MESH_connect)
124  * @param tunnel connection to the other end
125  * @param tunnel_ctx place to store local state associated with the tunnel
126  * @param sender who sent the message
127  * @param message the actual message
128  * @param atsi performance data for the connection
129  * @return GNUNET_OK to keep the connection open,
130  *         GNUNET_SYSERR to close it (signal serious error)
131  */
132 static int
133 data_callback (void *cls, struct GNUNET_MESH_Tunnel *tunnel, void **tunnel_ctx,
134                const struct GNUNET_PeerIdentity *sender,
135                const struct GNUNET_MessageHeader *message,
136                const struct GNUNET_ATS_Information *atsi)
137 {
138   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Data callback\n");
139   got++;
140   if (TARGET == got)
141   {
142     end_time = GNUNET_TIME_absolute_get();
143     result = GNUNET_OK;
144     finish();
145     return GNUNET_OK;
146   }
147   if (GNUNET_SCHEDULER_NO_TASK != shutdown_task)
148     GNUNET_SCHEDULER_cancel (shutdown_task);
149   shutdown_task =
150     GNUNET_SCHEDULER_add_delayed (TIMEOUT, &do_shutdown,
151                                   NULL);
152   return GNUNET_OK;
153 }
154
155
156 /**
157  * Method called whenever another peer has added us to a tunnel
158  * the other peer initiated.
159  *
160  * @param cls closure
161  * @param tunnel new handle to the tunnel
162  * @param initiator peer that started the tunnel
163  * @param atsi performance information for the tunnel
164  * @return initial tunnel context for the tunnel (can be NULL -- that's not an error)
165  */
166 static void *
167 inbound_tunnel (void *cls, struct GNUNET_MESH_Tunnel *tunnel,
168                 const struct GNUNET_PeerIdentity *initiator,
169                 const struct GNUNET_ATS_Information *atsi)
170 {
171   unsigned int id = *(unsigned int *) cls;
172
173   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "received incoming tunnel\n");
174   if (id != 1)
175   {
176     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
177                 "received incoming tunnel on peer 2\n");
178     result = GNUNET_SYSERR;
179   }
180   return NULL;
181 }
182
183
184 /**
185  * Function called whenever an inbound tunnel is destroyed.  Should clean up
186  * any associated state.
187  *
188  * @param cls closure (set from GNUNET_MESH_connect)
189  * @param tunnel connection to the other end (henceforth invalid)
190  * @param tunnel_ctx place where local state associated
191  *                   with the tunnel is stored
192  */
193 static void
194 inbound_end (void *cls, const struct GNUNET_MESH_Tunnel *tunnel,
195              void *tunnel_ctx)
196 {
197   unsigned int id = *(unsigned int *) cls;
198
199   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "incoming tunnel closed\n");
200   if (id != 1)
201   {
202     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
203                 "received closing tunnel on peer 2\n");
204     result = GNUNET_SYSERR;
205   }
206 }
207
208
209 /**
210  * Transmit ready callback.
211  * 
212  * @param cls Closure.
213  * @param size Buffer size.
214  * @param buf Buffer.
215  */
216 static size_t
217 tmt_rdy (void *cls, size_t size, void *buf)
218 {
219   struct GNUNET_MessageHeader *m = buf;
220   size_t msize = sizeof (struct GNUNET_MessageHeader);
221
222   if (0 == size || NULL == buf)
223     return 0;
224   GNUNET_assert (size >= msize);
225   sent++;
226   if (TARGET > sent)
227     GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO,
228                                        GNUNET_TIME_UNIT_FOREVER_REL,
229                                        &peer_id, msize, &tmt_rdy, NULL);
230   m->size = htons (msize);
231   m->type = htons (1);
232   return msize;
233 }
234
235
236 /**
237  * Method called whenever a peer has connected to the tunnel.
238  *
239  * @param cls Closure.
240  * @param peer Peer identity of connected peer.
241  */
242 static void
243 peer_connected (void *cls, const struct GNUNET_PeerIdentity *peer,
244                const struct GNUNET_ATS_Information *atsi)
245 {
246   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer connected\n");
247   peer_id = *peer;
248   start_time = GNUNET_TIME_absolute_get();
249   GNUNET_MESH_notify_transmit_ready (t, GNUNET_NO, GNUNET_TIME_UNIT_FOREVER_REL,
250                                      peer, sizeof (struct GNUNET_MessageHeader),
251                                      &tmt_rdy, NULL);
252 }
253
254
255 /**
256  * Method called whenever a peer has connected to the tunnel.
257  *
258  * @param cls closure
259  * @param peer peer identity the tunnel was created to, NULL on timeout
260  * @param atsi performance data for the connection
261  */
262 static void
263 peer_disconnected (void *cls, const struct GNUNET_PeerIdentity *peer)
264 {
265   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "peer disconnected\n");
266 }
267
268
269 /**
270  * Handler array for traffic received on peer1
271  */
272 static struct GNUNET_MESH_MessageHandler handlers1[] = {
273   {&data_callback, 1, 0},
274   {NULL, 0, 0}
275 };
276
277
278 /**
279  * Handler array for traffic received on peer2 (none expected)
280  */
281 static struct GNUNET_MESH_MessageHandler handlers2[] = { {NULL, 0, 0} };
282
283
284 /**
285  * Initialize framework and start test
286  */
287 static void
288 run (void *cls, 
289      const struct GNUNET_CONFIGURATION_Handle *cfg,
290      struct GNUNET_TESTING_Peer *peer)
291 {
292   static const GNUNET_MESH_ApplicationType app1[] = { 1, 0 };
293   static const GNUNET_MESH_ApplicationType app2[] = { 0 };
294
295   abort_task =
296       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
297                                     (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
298                                     NULL);
299   mesh_peer_1 = GNUNET_MESH_connect (cfg,       /* configuration */
300                                      (void *) &one,     /* cls */
301                                      &inbound_tunnel,   /* inbound new hndlr */
302                                      &inbound_end,      /* inbound end hndlr */
303                                      handlers1, /* traffic handlers */
304                                      app1);     /* apps offered */
305
306   mesh_peer_2 = GNUNET_MESH_connect (cfg,       /* configuration */
307                                      (void *) &two,     /* cls */
308                                      NULL,   /* inbound new hndlr */
309                                      NULL,      /* inbound end hndlr */
310                                      handlers2, /* traffic handlers */
311                                      app2);     /* apps offered */
312   if (NULL == mesh_peer_1 || NULL == mesh_peer_2)
313   {
314     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Couldn't connect to mesh\n");
315     result = GNUNET_SYSERR;
316     return;
317   }
318   else
319   {
320     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected to mesh\n");
321   }
322   t = GNUNET_MESH_tunnel_create (mesh_peer_2, NULL, &peer_connected,
323                                  &peer_disconnected, (void *) &two);
324   GNUNET_MESH_peer_request_connect_by_type (t, 1);
325 }
326
327
328 /**
329  * Main
330  */
331 int
332 main (int argc, char *argv[])
333 {
334   if (0 != GNUNET_TESTING_peer_run ("test-mesh-local-traffic",
335                                     "test_mesh.conf",
336                                     &run, NULL))
337     return 1;
338   if (result != GNUNET_OK)
339   {
340     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
341                 "Failed. Sent: %u, Got: %u\n",
342                 sent, got);
343     return 1;
344   }
345   else
346   {
347     struct GNUNET_TIME_Relative total_time;
348
349     total_time = GNUNET_TIME_absolute_get_difference(start_time, end_time);
350     FPRINTF (stderr, "\nTest time %llu ms\n",
351              (unsigned long long) total_time.rel_value);
352     FPRINTF (stderr, "Test bandwidth: %f kb/s\n",
353              4 * 1000.0 / total_time.rel_value); // 4bytes * ms
354     FPRINTF (stderr, "Test throughput: %f packets/s\n\n",
355              TARGET * 1000.0 / total_time.rel_value); // 1000 packets * ms
356     GAUGER ("MESH", "Tunnel 5 peers", TARGET * 1000.0 / total_time.rel_value,
357             "packets/s");
358   }
359   return 0;
360 }
361
362 /* end of test_mesh_local_traffic.c */