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