37395ad0f03045bbf93b74218749f706f249332c
[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  * How long until we give up on transmitting the message?
49  */
50 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1500)
51
52 static int ok;
53
54 static  GNUNET_SCHEDULER_TaskIdentifier die_task;
55
56 struct PeerContext * p1;
57
58 struct PeerContext * p2;
59
60 struct GNUNET_TRANSPORT_TransmitHandle * th;
61
62 char * cfg_file_p1;
63
64 char * cfg_file_p2;
65
66 /*
67  * Testcase specific declarations
68  */
69
70 /**
71  * Note that this value must not significantly exceed
72  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
73  * messages may be dropped even for a reliable transport.
74  */
75 #define TOTAL_MSGS (1024 * 2)
76
77 #define MTYPE 12345
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   if (n == TOTAL_MSGS)
313   {
314     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,"All messages sent\n");
315   }
316   return ret;
317 }
318
319
320 static void
321 notify_connect (void *cls,
322                 const struct GNUNET_PeerIdentity *peer,
323                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
324                 uint32_t ats_count)
325 {
326
327   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
328               "Peer `%4s' connected to us (%p)!\n",
329               GNUNET_i2s (peer),
330               cls);
331
332   if (cls == p1)
333     {
334       GNUNET_TRANSPORT_set_quota (p1->th,
335                                   &p2->id,
336                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
337                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024));
338     }
339   else  if (cls == p2)
340     {
341       GNUNET_TRANSPORT_set_quota (p2->th,
342                                   &p1->id,
343                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024),
344                                   GNUNET_BANDWIDTH_value_init (1024 * 1024 * 1024));
345     }
346 }
347
348
349 static void
350 notify_disconnect (void *cls,
351                    const struct GNUNET_PeerIdentity *peer)
352 {
353   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354               "Peer `%4s' disconnected (%p)!\n",
355               GNUNET_i2s (peer), cls);
356 }
357
358 static void
359 sendtask ()
360 {
361   start_time = GNUNET_TIME_absolute_get ();
362   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th,
363                                           &p1->id,
364                                           get_size (0), 0, TIMEOUT,
365                                           &notify_ready,
366                                           NULL);
367 }
368
369 static void
370 testing_connect_cb (struct PeerContext * p1, struct PeerContext * p2, void *cls)
371 {
372   char * p1_c = strdup (GNUNET_i2s(&p1->id));
373   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n",
374        p1_c,
375        GNUNET_i2s (&p2->id));
376   GNUNET_free (p1_c);
377
378   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
379   GNUNET_SCHEDULER_add_delayed(GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
380 }
381
382 static void
383 run (void *cls,
384      char *const *args,
385      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
386 {
387   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
388                                            &end_badly, NULL);
389
390   p1 = GNUNET_TRANSPORT_TESTING_start_peer(cfg_file_p1,
391       &notify_receive,
392       &notify_connect,
393       &notify_disconnect,
394       NULL);
395   p2 = GNUNET_TRANSPORT_TESTING_start_peer(cfg_file_p2,
396       &notify_receive,
397       &notify_connect,
398       &notify_disconnect,
399       NULL);
400
401   GNUNET_TRANSPORT_TESTING_connect_peers(p1, p2, &testing_connect_cb, NULL);
402 }
403
404 static int
405 check ()
406 {
407   static char *const argv[] = { "test-transport-api",
408     "-c",
409     "test_transport_api_data.conf",
410 #if VERBOSE
411     "-L", "DEBUG",
412 #endif
413     NULL
414   };
415   static struct GNUNET_GETOPT_CommandLineOption options[] = {
416     GNUNET_GETOPT_OPTION_END
417   };
418
419 #if WRITECONFIG
420   setTransportOptions("test_transport_api_data.conf");
421 #endif
422   ok = 1;
423   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
424                       argv, "test-transport-api", "nohelp",
425                       options, &run, &ok);
426
427   return ok;
428 }
429
430 /**
431  * Return the actual path to a file found in the current
432  * PATH environment variable.
433  *
434  * @param binary the name of the file to find
435  */
436 static char *
437 get_path_from_PATH (char *binary)
438 {
439   char *path;
440   char *pos;
441   char *end;
442   char *buf;
443   const char *p;
444
445   p = getenv ("PATH");
446   if (p == NULL)
447     {
448       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449                   _("PATH environment variable is unset.\n"));
450       return NULL;
451     }
452   path = GNUNET_strdup (p);     /* because we write on it */
453   buf = GNUNET_malloc (strlen (path) + 20);
454   pos = path;
455
456   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
457     {
458       *end = '\0';
459       sprintf (buf, "%s/%s", pos, binary);
460       if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
461         {
462           GNUNET_free (path);
463           return buf;
464         }
465       pos = end + 1;
466     }
467   sprintf (buf, "%s/%s", pos, binary);
468   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
469     {
470       GNUNET_free (path);
471       return buf;
472     }
473   GNUNET_free (buf);
474   GNUNET_free (path);
475   return NULL;
476 }
477
478 /**
479  * Check whether the suid bit is set on a file.
480  * Attempts to find the file using the current
481  * PATH environment variable as a search path.
482  *
483  * @param binary the name of the file to check
484  *
485  * @return GNUNET_YES if the binary is found and
486  *         can be run properly, GNUNET_NO otherwise
487  */
488 static int
489 check_gnunet_nat_binary(char *binary)
490 {
491   struct stat statbuf;
492   char *p;
493 #ifdef MINGW
494   SOCKET rawsock;
495 #endif
496
497 #ifdef MINGW
498   char *binaryexe;
499   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
500   p = get_path_from_PATH (binaryexe);
501   free (binaryexe);
502 #else
503   p = get_path_from_PATH (binary);
504 #endif
505   if (p == NULL)
506     {
507       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
508                   _("Could not find binary `%s' in PATH!\n"),
509                   binary);
510       return GNUNET_NO;
511     }
512   if (0 != STAT (p, &statbuf))
513     {
514       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
515                   _("stat (%s) failed: %s\n"),
516                   p,
517                   STRERROR (errno));
518       GNUNET_free (p);
519       return GNUNET_SYSERR;
520     }
521   GNUNET_free (p);
522 #ifndef MINGW
523   if ( (0 != (statbuf.st_mode & S_ISUID)) &&
524        (statbuf.st_uid == 0) )
525     return GNUNET_YES;
526   return GNUNET_NO;
527 #else
528   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
529   if (INVALID_SOCKET == rawsock)
530     {
531       DWORD err = GetLastError ();
532       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
533                   "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n", err);
534       return GNUNET_NO; /* not running as administrator */
535     }
536   closesocket (rawsock);
537   return GNUNET_YES;
538 #endif
539 }
540
541 int
542 main (int argc, char *argv[])
543 {
544   int ret;
545
546   GNUNET_log_setup ("test-transport-api",
547 #if VERBOSE
548                     "DEBUG",
549 #else
550                     "WARNING",
551 #endif
552                     NULL);
553
554   char * pch = strdup(argv[0]);
555   char * backup = pch;
556   char * filename = NULL;
557   char *dotexe;
558
559   /* get executable filename */
560   pch = strtok (pch,"/");
561   while (pch != NULL)
562   {
563     pch = strtok (NULL, "/");
564     if (pch != NULL)
565       filename = pch;
566   }
567   /* remove "lt-" */
568   filename = strstr(filename, "tes");
569   if (NULL != (dotexe = strstr (filename, ".exe")))
570     dotexe[0] = '\0';
571
572   /* create cfg filename */
573   GNUNET_asprintf(&cfg_file_p1, "%s_peer1.conf",filename);
574   GNUNET_asprintf(&cfg_file_p2, "%s_peer2.conf", filename);
575   GNUNET_free (backup);
576
577   if (strstr(argv[0], "tcp_nat") != NULL)
578     {
579       if (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server"))
580         {
581           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
582                       "`%s' not properly installed, cannot run NAT test!\n",
583                       "gnunet-nat-server");
584           return 0;
585         }
586     }
587   else if (strstr(argv[0], "udp_nat") != NULL)
588     {
589       if (GNUNET_YES != check_gnunet_nat_binary("gnunet-nat-server"))
590         {
591           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
592                       "`%s' not properly installed, cannot run NAT test!\n",
593                       "gnunet-nat-server");
594           return 0;
595         }
596     }
597
598   ret = check ();
599
600   GNUNET_free (cfg_file_p1);
601   GNUNET_free (cfg_file_p2);
602
603   return ret;
604 }
605
606
607 /* end of test_transport_api_reliability.c */