273a69526050aa13cf76e7dfc6ae037760428991
[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 "gauger.h"
38 #include "transport.h"
39 #include "transport-testing.h"
40
41 #define VERBOSE GNUNET_NO
42
43 #define VERBOSE_ARM GNUNET_NO
44
45 #define START_ARM GNUNET_YES
46
47 /**
48  * Note that this value must not significantly exceed
49  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
50  * messages may be dropped even for a reliable transport.
51  */
52 #define TOTAL_MSGS (1024 * 2)
53
54 /**
55  * How long until we give up on transmitting the message?
56  */
57 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
58
59 #define MTYPE 12345
60
61 static int ok;
62
63 static  GNUNET_SCHEDULER_TaskIdentifier die_task;
64
65 struct PeerContext * p1;
66
67 struct PeerContext * p2;
68
69 struct GNUNET_TRANSPORT_TransmitHandle * th;
70
71 char * cfg_file_p1;
72
73 char * cfg_file_p2;
74
75 /*
76  * Testcase specific declarations
77  */
78
79 struct TestMessage
80 {
81   struct GNUNET_MessageHeader header;
82   uint32_t num;
83 };
84
85 static int msg_scheduled;
86 static int msg_sent;
87 static int msg_recv_expected;
88 static int msg_recv;
89
90 static int test_failed;
91
92 static unsigned long long total_bytes;
93
94 static struct GNUNET_TIME_Absolute start_time;
95
96 /*
97  * END Testcase specific declarations
98  */
99
100 #if VERBOSE
101 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
102 #else
103 #define OKPP do { ok++; } while (0)
104 #endif
105
106
107 static void
108 end ()
109 {
110   unsigned long long delta;
111   //char *value_name;
112
113   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
114
115   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
116   fprintf (stderr,
117            "\nThroughput was %llu kb/s\n",
118            total_bytes * 1000 / 1024 / delta);
119   //GNUNET_asprintf(&value_name, "reliable_%s", test_name);
120   //GAUGER ("TRANSPORT", value_name, (int)(total_bytes * 1000 / 1024 /delta), "kb/s");
121   //GNUNET_free(value_name);
122
123   if (die_task != GNUNET_SCHEDULER_NO_TASK)
124     GNUNET_SCHEDULER_cancel(die_task);
125
126   if (th != NULL)
127     GNUNET_TRANSPORT_notify_transmit_ready_cancel(th);
128   th = NULL;
129
130   GNUNET_TRANSPORT_TESTING_stop_peer(p1);
131   GNUNET_TRANSPORT_TESTING_stop_peer(p2);
132 }
133
134 static void
135 end_badly ()
136 {
137   die_task = GNUNET_SCHEDULER_NO_TASK;
138   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fail! Stopping peers\n");
139
140   if (th != NULL)
141     GNUNET_TRANSPORT_notify_transmit_ready_cancel(th);
142   th = NULL;
143
144   if (p1 != NULL)
145     GNUNET_TRANSPORT_TESTING_stop_peer(p1);
146   if (p2 != NULL)
147     GNUNET_TRANSPORT_TESTING_stop_peer(p2);
148
149   ok = GNUNET_SYSERR;
150 }
151
152
153 static unsigned int
154 get_size (unsigned int iter)
155 {
156   unsigned int ret;
157
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                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
168                 uint32_t ats_count)
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   msg_recv_expected = n;
180   msg_recv = ntohl(hdr->num);
181   if (ntohs (message->size) != (s))
182     {
183       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
184                   "Expected message %u of size %u, got %u bytes of message %u\n",
185                   n, s,
186                   ntohs (message->size),
187                   ntohl (hdr->num));
188       if (die_task != GNUNET_SCHEDULER_NO_TASK)
189         GNUNET_SCHEDULER_cancel (die_task);
190       test_failed = GNUNET_YES;
191       die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
192       return;
193     }
194   if (ntohl (hdr->num) != n)
195     {
196       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197                   "Expected message %u of size %u, got %u bytes of message %u\n",
198                   n, s,
199                   ntohs (message->size),
200                   ntohl (hdr->num));
201       if (die_task != GNUNET_SCHEDULER_NO_TASK)
202         GNUNET_SCHEDULER_cancel (die_task);
203       test_failed = GNUNET_YES;
204       die_task = GNUNET_SCHEDULER_add_now (&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       if (die_task != GNUNET_SCHEDULER_NO_TASK)
216         GNUNET_SCHEDULER_cancel (die_task);
217       test_failed = GNUNET_YES;
218       die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
219       return;
220     }
221 #if VERBOSE
222   if (ntohl(hdr->num) % 5000 == 0)
223     {
224       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225                   "Got message %u of size %u\n",
226                   ntohl (hdr->num),
227                   ntohs (message->size));
228     }
229 #endif
230   n++;
231   if (0 == (n % (TOTAL_MSGS/100)))
232     {
233       fprintf (stderr, ".");
234       if (die_task != GNUNET_SCHEDULER_NO_TASK)
235         GNUNET_SCHEDULER_cancel (die_task);
236       die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
237                                                &end_badly,
238                                                NULL);
239     }
240   if (n == TOTAL_MSGS)
241   {
242     ok = 0;
243     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"All messages received\n");
244     end ();
245   }
246 }
247
248
249 static size_t
250 notify_ready (void *cls, size_t size, void *buf)
251 {
252   static int n;
253   char *cbuf = buf;
254   struct TestMessage hdr;
255   unsigned int s;
256   unsigned int ret;
257
258   if (buf == NULL)
259     {
260       GNUNET_break (0);
261       ok = 42;
262       return 0;
263     }
264   th = NULL;
265   ret = 0;
266   s = get_size (n);
267   GNUNET_assert (size >= s);
268   GNUNET_assert (buf != NULL);
269   cbuf = buf;
270   do
271     {
272       hdr.header.size = htons (s);
273       hdr.header.type = htons (MTYPE);
274       hdr.num = htonl (n);
275       msg_sent = 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   {
297     if (th == NULL)
298       th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th,
299                                             &p1->id,
300                                             s, 0, TIMEOUT,
301                                             &notify_ready,
302                                             NULL);
303     msg_scheduled = n;
304   }
305   if (n % 5000 == 0)
306     {
307       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
308                   "Returning total message block of size %u\n",
309                   ret);
310     }
311   total_bytes += ret;
312   return ret;
313 }
314
315
316 static void
317 notify_connect (void *cls,
318                 const struct GNUNET_PeerIdentity *peer,
319                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
320                 uint32_t ats_count)
321 {
322
323   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
324               "Peer `%4s' connected to us (%p)!\n",
325               GNUNET_i2s (peer),
326               cls);
327
328   if (cls == p1)
329     {
330       GNUNET_TRANSPORT_set_quota (p1->th,
331                                   &p2->id,
332                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
333                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024));
334     }
335   else  if (cls == p2)
336     {
337       GNUNET_TRANSPORT_set_quota (p2->th,
338                                   &p1->id,
339                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
340                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024));
341     }
342 }
343
344
345 static void
346 notify_disconnect (void *cls,
347                    const struct GNUNET_PeerIdentity *peer)
348 {
349   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
350               "Peer `%4s' disconnected (%p)!\n",
351               GNUNET_i2s (peer), cls);
352 }
353
354 static void
355 sendtask ()
356 {
357   start_time = GNUNET_TIME_absolute_get ();
358   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th,
359                                           &p1->id,
360                                           get_size (0), 0, TIMEOUT,
361                                           &notify_ready,
362                                           NULL);
363 }
364
365 static void
366 testing_connect_cb (struct PeerContext * p1, struct PeerContext * p2, void *cls)
367 {
368   char * p1_c = strdup (GNUNET_i2s(&p1->id));
369   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n",
370        p1_c,
371        GNUNET_i2s (&p2->id));
372   GNUNET_free (p1_c);
373
374   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
375   GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
376 }
377
378 static void
379 run (void *cls,
380      char *const *args,
381      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
382 {
383   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
384                                            &end_badly, NULL);
385
386   p1 = GNUNET_TRANSPORT_TESTING_start_peer(cfg_file_p1,
387       &notify_receive,
388       &notify_connect,
389       &notify_disconnect,
390       NULL);
391   p2 = GNUNET_TRANSPORT_TESTING_start_peer(cfg_file_p2,
392       &notify_receive,
393       &notify_connect,
394       &notify_disconnect,
395       NULL);
396
397   GNUNET_TRANSPORT_TESTING_connect_peers(p1, p2, &testing_connect_cb, NULL);
398 }
399
400 static int
401 check ()
402 {
403   static char *const argv[] = { "test-transport-api",
404     "-c",
405     "test_transport_api_data.conf",
406 #if VERBOSE
407     "-L", "DEBUG",
408 #endif
409     NULL
410   };
411   static struct GNUNET_GETOPT_CommandLineOption options[] = {
412     GNUNET_GETOPT_OPTION_END
413   };
414
415 #if WRITECONFIG
416   setTransportOptions("test_transport_api_data.conf");
417 #endif
418   ok = 1;
419   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
420                       argv, "test-transport-api", "nohelp",
421                       options, &run, &ok);
422
423   return ok;
424 }
425
426 /**
427  * Return the actual path to a file found in the current
428  * PATH environment variable.
429  *
430  * @param binary the name of the file to find
431  */
432 static char *
433 get_path_from_PATH (char *binary)
434 {
435   char *path;
436   char *pos;
437   char *end;
438   char *buf;
439   const char *p;
440
441   p = getenv ("PATH");
442   if (p == NULL)
443     {
444       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
445                   _("PATH environment variable is unset.\n"));
446       return NULL;
447     }
448   path = GNUNET_strdup (p);     /* because we write on it */
449   buf = GNUNET_malloc (strlen (path) + 20);
450   pos = path;
451
452   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
453     {
454       *end = '\0';
455       sprintf (buf, "%s/%s", pos, binary);
456       if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
457         {
458           GNUNET_free (path);
459           return buf;
460         }
461       pos = end + 1;
462     }
463   sprintf (buf, "%s/%s", pos, binary);
464   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
465     {
466       GNUNET_free (path);
467       return buf;
468     }
469   GNUNET_free (buf);
470   GNUNET_free (path);
471   return NULL;
472 }
473
474 /**
475  * Check whether the suid bit is set on a file.
476  * Attempts to find the file using the current
477  * PATH environment variable as a search path.
478  *
479  * @param binary the name of the file to check
480  *
481  * @return GNUNET_YES if the binary is found and
482  *         can be run properly, GNUNET_NO otherwise
483  */
484 static int
485 check_gnunet_nat_binary(char *binary)
486 {
487   struct stat statbuf;
488   char *p;
489 #ifdef MINGW
490   SOCKET rawsock;
491 #endif
492
493 #ifdef MINGW
494   char *binaryexe;
495   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
496   p = get_path_from_PATH (binaryexe);
497   free (binaryexe);
498 #else
499   p = get_path_from_PATH (binary);
500 #endif
501   if (p == NULL)
502     {
503       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504                   _("Could not find binary `%s' in PATH!\n"),
505                   binary);
506       return GNUNET_NO;
507     }
508   if (0 != STAT (p, &statbuf))
509     {
510       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
511                   _("stat (%s) failed: %s\n"),
512                   p,
513                   STRERROR (errno));
514       GNUNET_free (p);
515       return GNUNET_SYSERR;
516     }
517   GNUNET_free (p);
518 #ifndef MINGW
519   if ( (0 != (statbuf.st_mode & S_ISUID)) &&
520        (statbuf.st_uid == 0) )
521     return GNUNET_YES;
522   return GNUNET_NO;
523 #else
524   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
525   if (INVALID_SOCKET == rawsock)
526     {
527       DWORD err = GetLastError ();
528       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
529                   "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n", err);
530       return GNUNET_NO; /* not running as administrator */
531     }
532   closesocket (rawsock);
533   return GNUNET_YES;
534 #endif
535 }
536
537 int
538 main (int argc, char *argv[])
539 {
540   int ret;
541
542   GNUNET_log_setup ("test-transport-api",
543 #if VERBOSE
544                     "DEBUG",
545 #else
546                     "WARNING",
547 #endif
548                     NULL);
549
550   char * pch = strdup(argv[0]);
551   char * backup = pch;
552   char * filename = NULL;
553   char *dotexe;
554
555   /* get executable filename */
556   pch = strtok (pch,"/");
557   while (pch != NULL)
558   {
559     pch = strtok (NULL, "/");
560     if (pch != NULL)
561       filename = pch;
562   }
563   /* remove "lt-" */
564   filename = strstr(filename, "tes");
565   if (NULL != (dotexe = strstr (filename, ".exe")))
566     dotexe[0] = '\0';
567
568   /* create cfg filename */
569   GNUNET_asprintf(&cfg_file_p1, "%s_peer1.conf",filename);
570   GNUNET_asprintf(&cfg_file_p2, "%s_peer2.conf", filename);
571   GNUNET_free (backup);
572
573   if (strstr(argv[0], "tcp_nat") != NULL)
574     {
575       if (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server"))
576         {
577           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
578                       "`%s' not properly installed, cannot run NAT test!\n",
579                       "gnunet-nat-server");
580           return 0;
581         }
582     }
583   else if (strstr(argv[0], "udp_nat") != NULL)
584     {
585       if (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server"))
586         {
587           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
588                       "`%s' not properly installed, cannot run NAT test!\n",
589                       "gnunet-nat-server");
590           return 0;
591         }
592     }
593
594   ret = check ();
595
596   GNUNET_free (cfg_file_p1);
597   GNUNET_free (cfg_file_p2);
598
599   return ret;
600 }
601
602
603 /* end of test_transport_api_reliability.c */