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