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