4f00fa0d962a13be20aae1164ebc24b9a7ab0158
[oweals/gnunet.git] / src / transport / test_transport_api_reliability.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009, 2010 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 2, 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 transport/test_transport_api_reliability.c
22  * @brief base test case for transport implementations
23  *
24  * This test case serves as a base for tcp and http
25  * transport test cases to check that the transports
26  * achieve reliable message delivery.
27  */
28 #include "platform.h"
29 #include "gnunet_common.h"
30 #include "gnunet_hello_lib.h"
31 #include "gnunet_getopt_lib.h"
32 #include "gnunet_os_lib.h"
33 #include "gnunet_program_lib.h"
34 #include "gnunet_scheduler_lib.h"
35 #include "gnunet_transport_service.h"
36 #include "transport.h"
37
38 #define VERBOSE GNUNET_NO
39
40 #define VERBOSE_ARM GNUNET_NO
41
42 #define START_ARM GNUNET_YES
43
44 /**
45  * Note that this value must not significantly exceed
46  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
47  * messages may be dropped even for a reliable transport.
48  */
49 #define TOTAL_MSGS (60000 * 20)
50
51 /**
52  * How long until we give up on transmitting the message?
53  */
54 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 150)
55
56 #define MTYPE 12345
57
58 struct PeerContext
59 {
60   struct GNUNET_CONFIGURATION_Handle *cfg;
61   struct GNUNET_TRANSPORT_Handle *th;
62   struct GNUNET_PeerIdentity id;
63 #if START_ARM
64   pid_t arm_pid;
65 #endif
66 };
67
68 static struct PeerContext p1;
69
70 static struct PeerContext p2;
71
72 static struct GNUNET_SCHEDULER_Handle *sched;
73
74 static int ok;
75
76 static int is_tcp;
77
78 static int is_tcp_nat;
79
80 static int is_http;
81
82 static int connected;
83
84 static unsigned long long total_bytes;
85
86 static struct GNUNET_TIME_Absolute start_time;
87
88 static GNUNET_SCHEDULER_TaskIdentifier die_task;
89
90 #if VERBOSE
91 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
92 #else
93 #define OKPP do { ok++; } while (0)
94 #endif
95
96
97 static void
98 end ()
99 {
100   unsigned long long delta;
101
102   GNUNET_SCHEDULER_cancel (sched, die_task);
103   die_task = GNUNET_SCHEDULER_NO_TASK;
104 #if VERBOSE
105   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from transports!\n");
106 #endif
107   GNUNET_TRANSPORT_disconnect (p1.th);
108   GNUNET_TRANSPORT_disconnect (p2.th);
109 #if VERBOSE
110   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 
111               "Transports disconnected, returning success!\n");
112 #endif
113   delta = GNUNET_TIME_absolute_get_duration (start_time).value;
114   fprintf (stderr,
115            "\nThroughput was %llu kb/s\n",
116            total_bytes * 1000 / 1024 / delta);
117   ok = 0;
118 }
119
120
121 static void
122 stop_arm (struct PeerContext *p)
123 {
124 #if START_ARM
125   if (0 != PLIBC_KILL (p->arm_pid, SIGTERM))
126     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
127   GNUNET_OS_process_wait (p->arm_pid);
128 #endif
129   GNUNET_CONFIGURATION_destroy (p->cfg);
130 }
131
132
133 static void
134 end_badly (void *cls,
135            const struct GNUNET_SCHEDULER_TaskContext *tc)
136 {
137   GNUNET_break (0);
138   GNUNET_TRANSPORT_disconnect (p1.th);
139   GNUNET_TRANSPORT_disconnect (p2.th);
140   ok = 1;
141 }
142
143
144 struct TestMessage 
145 {
146   struct GNUNET_MessageHeader header;
147   uint32_t num;
148 };
149
150
151 static unsigned int
152 get_size (unsigned int iter)
153 {
154   unsigned int ret;
155
156   if (iter < 60000)
157     return iter + sizeof (struct TestMessage);
158   ret = (iter * iter * iter);
159   return sizeof (struct TestMessage) + (ret % 60000);
160 }
161
162
163 static void
164 notify_receive (void *cls,
165                 const struct GNUNET_PeerIdentity *peer,
166                 const struct GNUNET_MessageHeader *message,
167                 struct GNUNET_TIME_Relative latency,
168                 uint32_t distance)
169 {
170   static int n;
171   unsigned int s;
172   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
173   const struct TestMessage *hdr;
174
175   hdr = (const struct TestMessage*) message;
176   s = get_size (n);
177   if (MTYPE != ntohs (message->type))
178     return;
179   if (ntohs (message->size) != s)
180     {
181       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
182                   "Expected message %u of size %u, got %u bytes of message %u\n",
183                   n, s,
184                   ntohs (message->size),
185                   ntohl (hdr->num));
186       GNUNET_SCHEDULER_cancel (sched, die_task);
187       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
188       return;
189     }
190   if (ntohl (hdr->num) != n)
191     {
192       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
193                   "Expected message %u of size %u, got %u bytes of message %u\n",
194                   n, s,
195                   ntohs (message->size),
196                   ntohl (hdr->num));
197       GNUNET_SCHEDULER_cancel (sched, die_task);
198       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
199       return;
200     }
201   memset (cbuf, n, s - sizeof (struct TestMessage));
202   if (0 != memcmp (cbuf,
203                    &hdr[1],
204                    s - sizeof (struct TestMessage)))
205     {
206       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
207                   "Expected message %u with bits %u, but body did not match\n",
208                   n, (unsigned char) n);
209       GNUNET_SCHEDULER_cancel (sched, die_task);
210       die_task = GNUNET_SCHEDULER_add_now (sched, &end_badly, NULL);
211       return;
212     }
213 #if VERBOSE
214   if (ntohl(hdr->num) % 5000 == 0)
215     {
216       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217                   "Got message %u of size %u\n",
218                   ntohl (hdr->num),
219                   ntohs (message->size));
220     }
221 #endif
222   n++;
223   if (0 == (n % (TOTAL_MSGS/100)))
224     {
225       fprintf (stderr, ".");
226       GNUNET_SCHEDULER_cancel (sched, die_task);
227       die_task = GNUNET_SCHEDULER_add_delayed (sched,
228                                                TIMEOUT,
229                                                &end_badly,
230                                                NULL);    
231     }
232   if (n == TOTAL_MSGS)
233     end ();
234 }
235
236
237 static size_t
238 notify_ready (void *cls, size_t size, void *buf)
239 {
240   static int n;
241   char *cbuf = buf;
242   struct TestMessage hdr;
243   unsigned int s;
244   unsigned int ret;
245
246   if (buf == NULL)
247     {
248       GNUNET_break (0);
249       ok = 42;
250       return 0;
251     }
252   ret = 0;
253   s = get_size (n);
254   GNUNET_assert (size >= s);
255   GNUNET_assert (buf != NULL);
256   cbuf = buf;
257   do
258     {
259       hdr.header.size = htons (s);
260       hdr.header.type = htons (MTYPE);
261       hdr.num = htonl (n);
262       memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
263       ret += sizeof (struct TestMessage);
264       memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
265       ret += s - sizeof (struct TestMessage);
266 #if VERBOSE
267       if (n % 5000 == 0)
268         {
269           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
270                       "Sending message %u of size %u\n",
271                       n,
272                       s);
273         }
274 #endif
275       n++;
276       s = get_size (n);
277       if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
278         break; /* sometimes pack buffer full, sometimes not */
279     }
280   while (size - ret >= s);
281   if (n < TOTAL_MSGS)
282     GNUNET_TRANSPORT_notify_transmit_ready (p1.th,
283                                             &p2.id,
284                                             s, 0, TIMEOUT, 
285                                             &notify_ready,
286                                             NULL);
287   if (n % 5000 == 0)
288     {
289       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
290                   "Returning total message block of size %u\n",
291                   ret);
292     }
293   total_bytes += ret;
294   return ret;
295 }
296
297
298 static void
299 notify_connect (void *cls,
300                 const struct GNUNET_PeerIdentity *peer,
301                 struct GNUNET_TIME_Relative latency,
302                 uint32_t distance)
303 {
304   if (cls == &p1)
305     {
306       GNUNET_TRANSPORT_set_quota (p1.th,
307                                   &p2.id,
308                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
309                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
310                                   GNUNET_TIME_UNIT_FOREVER_REL,
311                                   NULL, NULL);
312       start_time = GNUNET_TIME_absolute_get ();
313       connected++;
314     }
315   else
316     {
317       GNUNET_TRANSPORT_set_quota (p2.th,
318                                   &p1.id,
319                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
320                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
321                                   GNUNET_TIME_UNIT_FOREVER_REL,
322                                   NULL, NULL);
323       connected++;
324     }
325
326   if (connected == 2)
327     {
328       GNUNET_TRANSPORT_notify_transmit_ready (p1.th,
329                                               &p2.id,
330                                               get_size (0), 0, TIMEOUT,
331                                               &notify_ready,
332                                               NULL);
333     }
334 #if VERBOSE
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
336               "Peer `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
337 #endif
338 }
339
340
341 static void
342 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
343 {
344 #if VERBOSE
345   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
346               "Peer `%4s' disconnected (%p)!\n",
347               GNUNET_i2s (peer), cls);
348 #endif
349 }
350
351
352 static void
353 setup_peer (struct PeerContext *p, const char *cfgname)
354 {
355   p->cfg = GNUNET_CONFIGURATION_create ();
356 #if START_ARM
357   p->arm_pid = GNUNET_OS_start_process (NULL, NULL, 
358                                         "gnunet-service-arm",
359                                         "gnunet-service-arm",
360 #if VERBOSE_ARM
361                                         "-L", "DEBUG",
362 #endif
363                                         "-c", cfgname, NULL);
364 #endif
365   GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (p->cfg, cfgname));
366   p->th = GNUNET_TRANSPORT_connect (sched, p->cfg,
367                                     p,
368                                     &notify_receive,
369                                     &notify_connect, 
370                                     &notify_disconnect);
371   GNUNET_assert (p->th != NULL);
372 }
373
374
375 static void
376 exchange_hello_last (void *cls,
377                      const struct GNUNET_MessageHeader *message)
378 {
379   struct PeerContext *me = cls;
380
381   GNUNET_TRANSPORT_get_hello_cancel (p2.th, &exchange_hello_last, me);
382 #if VERBOSE
383   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
384               "Exchanging HELLO with peer (%p)!\n", cls);
385 #endif
386   GNUNET_assert (ok >= 3);
387   OKPP;
388   GNUNET_assert (message != NULL);
389   GNUNET_assert (GNUNET_OK ==
390                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
391                                       message, &me->id));
392   /* both HELLOs exchanged, get ready to test transmission! */
393   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394               "Finished exchanging HELLOs, now waiting for transmission!\n");
395 }
396
397
398 static void
399 exchange_hello (void *cls,
400                 const struct GNUNET_MessageHeader *message)
401 {
402   struct PeerContext *me = cls;
403
404   GNUNET_TRANSPORT_get_hello_cancel (p1.th, &exchange_hello, me);
405 #if VERBOSE
406   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407               "Exchanging HELLO with peer (%p)!\n", cls);
408 #endif
409   GNUNET_assert (ok >= 2);
410   OKPP;
411   GNUNET_assert (message != NULL);
412   GNUNET_assert (GNUNET_OK ==
413                  GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *)
414                                       message, &me->id));
415
416 #if VERBOSE
417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418               "Received HELLO size %d\n", 
419               GNUNET_HELLO_size((const struct GNUNET_HELLO_Message *)message));
420 #endif
421   GNUNET_TRANSPORT_offer_hello (p2.th, message);
422   GNUNET_TRANSPORT_get_hello (p2.th, &exchange_hello_last, &p2);
423 }
424
425
426 static void
427 run (void *cls,
428      struct GNUNET_SCHEDULER_Handle *s,
429      char *const *args,
430      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
431 {
432   GNUNET_assert (ok == 1);
433   OKPP;
434   sched = s;
435   die_task = GNUNET_SCHEDULER_add_delayed (sched,
436                                            TIMEOUT,
437                                            &end_badly,
438                                            NULL);
439   if (is_tcp)
440     {
441       setup_peer (&p1, "test_transport_api_tcp_peer1.conf");
442       setup_peer (&p2, "test_transport_api_tcp_peer2.conf");
443     }
444   else if (is_http)
445     {
446       setup_peer (&p1, "test_transport_api_http_peer1.conf");
447       setup_peer (&p2, "test_transport_api_http_peer2.conf");
448     }
449   else if (is_tcp_nat)
450     {
451       setup_peer (&p1, "test_transport_api_tcp_nat_peer1.conf");
452       setup_peer (&p2, "test_transport_api_tcp_nat_peer2.conf");
453     }
454   else
455     GNUNET_assert (0);
456   GNUNET_assert(p1.th != NULL);
457   GNUNET_assert(p2.th != NULL);
458   GNUNET_TRANSPORT_get_hello (p1.th, &exchange_hello, &p1);
459 }
460
461
462 static int
463 check ()
464 {
465   char *const argv[] = { "test-transport-api-reliability",
466     "-c",
467     "test_transport_api_data.conf",
468 #if VERBOSE
469     "-L", "DEBUG",
470 #endif
471     NULL
472   };
473   struct GNUNET_GETOPT_CommandLineOption options[] = {
474     GNUNET_GETOPT_OPTION_END
475   };
476
477 #if WRITECONFIG
478   setTransportOptions("test_transport_api_data.conf");
479 #endif
480   ok = 1;
481   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
482                       argv, "test-transport-api-reliability", "nohelp",
483                       options, &run, &ok);
484   stop_arm (&p1);
485   stop_arm (&p2);
486   return ok;
487 }
488
489
490 int
491 main (int argc, char *argv[])
492 {
493   int ret;
494 #ifdef MINGW
495   return GNUNET_SYSERR;
496 #endif
497   if (strstr(argv[0], "tcp_nat") != NULL)
498     {
499       is_tcp_nat = GNUNET_YES;
500     }
501   else if (strstr(argv[0], "tcp") != NULL)
502     {
503       is_tcp = GNUNET_YES;
504     }
505   else if (strstr(argv[0], "http") != NULL)
506     {
507       is_http = GNUNET_YES;
508     }
509   GNUNET_log_setup ("test-transport-api-reliability",
510 #if VERBOSE
511                     "DEBUG",
512 #else
513                     "WARNING",
514 #endif
515                     NULL);
516   ret = check ();
517   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-1");
518   GNUNET_DISK_directory_remove ("/tmp/test-gnunetd-transport-peer-2");
519   return ret;
520 }
521
522 /* end of test_transport_api_reliability.c */