60191394d1e3902f8f8e0226510fb7bb721ef7ea
[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
112   //char *value_name;
113
114   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
115
116   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
117   fprintf (stderr,
118            "\nThroughput was %llu kb/s\n", 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, ntohs (message->size), ntohl (hdr->num));
186     if (die_task != GNUNET_SCHEDULER_NO_TASK)
187       GNUNET_SCHEDULER_cancel (die_task);
188     test_failed = GNUNET_YES;
189     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
190     return;
191   }
192   if (ntohl (hdr->num) != n)
193   {
194     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
195                 "Expected message %u of size %u, got %u bytes of message %u\n",
196                 n, s, ntohs (message->size), ntohl (hdr->num));
197     if (die_task != GNUNET_SCHEDULER_NO_TASK)
198       GNUNET_SCHEDULER_cancel (die_task);
199     test_failed = GNUNET_YES;
200     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
201     return;
202   }
203   memset (cbuf, n, s - sizeof (struct TestMessage));
204   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
205   {
206     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
207                 "Expected message %u with bits %u, but body did not match\n",
208                 n, (unsigned char) n);
209     if (die_task != GNUNET_SCHEDULER_NO_TASK)
210       GNUNET_SCHEDULER_cancel (die_task);
211     test_failed = GNUNET_YES;
212     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
213     return;
214   }
215 #if VERBOSE
216   if (ntohl (hdr->num) % 5000 == 0)
217   {
218     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
219                 "Got message %u of size %u\n",
220                 ntohl (hdr->num), ntohs (message->size));
221   }
222 #endif
223   n++;
224   if (0 == (n % (TOTAL_MSGS / 100)))
225   {
226     fprintf (stderr, ".");
227     if (die_task != GNUNET_SCHEDULER_NO_TASK)
228       GNUNET_SCHEDULER_cancel (die_task);
229     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
230   }
231   if (n == TOTAL_MSGS)
232   {
233     ok = 0;
234     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages received\n");
235     end ();
236   }
237 }
238
239
240 static size_t
241 notify_ready (void *cls, size_t size, void *buf)
242 {
243   static int n;
244   char *cbuf = buf;
245   struct TestMessage hdr;
246   unsigned int s;
247   unsigned int ret;
248
249   if (buf == NULL)
250   {
251     GNUNET_break (0);
252     ok = 42;
253     return 0;
254   }
255   th = NULL;
256   ret = 0;
257   s = get_size (n);
258   GNUNET_assert (size >= s);
259   GNUNET_assert (buf != NULL);
260   cbuf = buf;
261   do
262   {
263     hdr.header.size = htons (s);
264     hdr.header.type = htons (MTYPE);
265     hdr.num = htonl (n);
266     msg_sent = n;
267     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
268     ret += sizeof (struct TestMessage);
269     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
270     ret += s - sizeof (struct TestMessage);
271 #if VERBOSE
272     if (n % 5000 == 0)
273     {
274       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
275                   "Sending message %u of size %u\n", n, s);
276     }
277 #endif
278     n++;
279     s = get_size (n);
280     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
281       break;                    /* sometimes pack buffer full, sometimes not */
282   }
283   while (size - ret >= s);
284   if (n < TOTAL_MSGS)
285   {
286     if (th == NULL)
287       th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th,
288                                                    &p1->id,
289                                                    s, 0, TIMEOUT,
290                                                    &notify_ready, NULL);
291     msg_scheduled = n;
292   }
293   if (n % 5000 == 0)
294   {
295     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
296                 "Returning total message block of size %u\n", ret);
297   }
298   total_bytes += ret;
299   if (n == TOTAL_MSGS)
300   {
301     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages sent\n");
302   }
303   return ret;
304 }
305
306
307 static void
308 notify_connect (void *cls,
309                 const struct GNUNET_PeerIdentity *peer,
310                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
311                 uint32_t ats_count)
312 {
313
314   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315               "Peer `%4s' connected to us (%p)!\n", GNUNET_i2s (peer), cls);
316
317   if (cls == p1)
318   {
319     GNUNET_TRANSPORT_set_quota (p1->th,
320                                 &p2->id,
321                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
322                                                              1024),
323                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
324                                                              1024));
325   }
326   else if (cls == p2)
327   {
328     GNUNET_TRANSPORT_set_quota (p2->th,
329                                 &p1->id,
330                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
331                                                              1024),
332                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
333                                                              1024));
334   }
335 }
336
337
338 static void
339 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
340 {
341   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
342               "Peer `%4s' disconnected (%p)!\n", GNUNET_i2s (peer), cls);
343 }
344
345 static void
346 sendtask ()
347 {
348   start_time = GNUNET_TIME_absolute_get ();
349   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th,
350                                                &p1->id,
351                                                get_size (0), 0, TIMEOUT,
352                                                &notify_ready, NULL);
353 }
354
355 static void
356 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
357 {
358   char *p1_c = strdup (GNUNET_i2s (&p1->id));
359
360   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n",
361               p1_c, GNUNET_i2s (&p2->id));
362   GNUNET_free (p1_c);
363
364   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
365   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
366 }
367
368 static void
369 run (void *cls,
370      char *const *args,
371      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *cfg)
372 {
373   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
374
375   p1 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p1,
376                                             &notify_receive,
377                                             &notify_connect,
378                                             &notify_disconnect, NULL);
379   p2 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p2,
380                                             &notify_receive,
381                                             &notify_connect,
382                                             &notify_disconnect, NULL);
383
384   GNUNET_TRANSPORT_TESTING_connect_peers (p1, p2, &testing_connect_cb, NULL);
385 }
386
387 static int
388 check ()
389 {
390   static char *const argv[] = { "test-transport-api",
391     "-c",
392     "test_transport_api_data.conf",
393 #if VERBOSE
394     "-L", "DEBUG",
395 #endif
396     NULL
397   };
398   static struct GNUNET_GETOPT_CommandLineOption options[] = {
399     GNUNET_GETOPT_OPTION_END
400   };
401
402 #if WRITECONFIG
403   setTransportOptions ("test_transport_api_data.conf");
404 #endif
405   ok = 1;
406   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
407                       argv, "test-transport-api", "nohelp", options, &run, &ok);
408
409   return ok;
410 }
411
412 /**
413  * Return the actual path to a file found in the current
414  * PATH environment variable.
415  *
416  * @param binary the name of the file to find
417  */
418 static char *
419 get_path_from_PATH (char *binary)
420 {
421   char *path;
422   char *pos;
423   char *end;
424   char *buf;
425   const char *p;
426
427   p = getenv ("PATH");
428   if (p == NULL)
429   {
430     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
431                 _("PATH environment variable is unset.\n"));
432     return NULL;
433   }
434   path = GNUNET_strdup (p);     /* because we write on it */
435   buf = GNUNET_malloc (strlen (path) + 20);
436   pos = path;
437
438   while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
439   {
440     *end = '\0';
441     sprintf (buf, "%s/%s", pos, binary);
442     if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
443     {
444       GNUNET_free (path);
445       return buf;
446     }
447     pos = end + 1;
448   }
449   sprintf (buf, "%s/%s", pos, binary);
450   if (GNUNET_DISK_file_test (buf) == GNUNET_YES)
451   {
452     GNUNET_free (path);
453     return buf;
454   }
455   GNUNET_free (buf);
456   GNUNET_free (path);
457   return NULL;
458 }
459
460 /**
461  * Check whether the suid bit is set on a file.
462  * Attempts to find the file using the current
463  * PATH environment variable as a search path.
464  *
465  * @param binary the name of the file to check
466  *
467  * @return GNUNET_YES if the binary is found and
468  *         can be run properly, GNUNET_NO otherwise
469  */
470 static int
471 check_gnunet_nat_binary (char *binary)
472 {
473   struct stat statbuf;
474   char *p;
475
476 #ifdef MINGW
477   SOCKET rawsock;
478 #endif
479
480 #ifdef MINGW
481   char *binaryexe;
482
483   GNUNET_asprintf (&binaryexe, "%s.exe", binary);
484   p = get_path_from_PATH (binaryexe);
485   free (binaryexe);
486 #else
487   p = get_path_from_PATH (binary);
488 #endif
489   if (p == NULL)
490   {
491     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
492                 _("Could not find binary `%s' in PATH!\n"), binary);
493     return GNUNET_NO;
494   }
495   if (0 != STAT (p, &statbuf))
496   {
497     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
498                 _("stat (%s) failed: %s\n"), p, STRERROR (errno));
499     GNUNET_free (p);
500     return GNUNET_SYSERR;
501   }
502   GNUNET_free (p);
503 #ifndef MINGW
504   if ((0 != (statbuf.st_mode & S_ISUID)) && (statbuf.st_uid == 0))
505     return GNUNET_YES;
506   return GNUNET_NO;
507 #else
508   rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
509   if (INVALID_SOCKET == rawsock)
510   {
511     DWORD err = GetLastError ();
512
513     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
514                 "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) have failed! GLE = %d\n",
515                 err);
516     return GNUNET_NO;           /* not running as administrator */
517   }
518   closesocket (rawsock);
519   return GNUNET_YES;
520 #endif
521 }
522
523 int
524 main (int argc, char *argv[])
525 {
526   int ret;
527
528   GNUNET_log_setup ("test-transport-api",
529 #if VERBOSE
530                     "DEBUG",
531 #else
532                     "WARNING",
533 #endif
534                     NULL);
535
536   char *pch = strdup (argv[0]);
537   char *backup = pch;
538   char *filename = NULL;
539   char *dotexe;
540
541   /* get executable filename */
542   pch = strtok (pch, "/");
543   while (pch != NULL)
544   {
545     pch = strtok (NULL, "/");
546     if (pch != NULL)
547       filename = pch;
548   }
549   /* remove "lt-" */
550   filename = strstr (filename, "tes");
551   if (NULL != (dotexe = strstr (filename, ".exe")))
552     dotexe[0] = '\0';
553
554   /* create cfg filename */
555   GNUNET_asprintf (&cfg_file_p1, "%s_peer1.conf", filename);
556   GNUNET_asprintf (&cfg_file_p2, "%s_peer2.conf", filename);
557   GNUNET_free (backup);
558
559   if (strstr (argv[0], "tcp_nat") != NULL)
560   {
561     if (GNUNET_YES != check_gnunet_nat_binary ("gnunet-nat-server"))
562     {
563       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
564                   "`%s' not properly installed, cannot run NAT test!\n",
565                   "gnunet-nat-server");
566       return 0;
567     }
568   }
569   else if (strstr (argv[0], "udp_nat") != NULL)
570   {
571     if (GNUNET_YES != check_gnunet_nat_binary ("gnunet-nat-server"))
572     {
573       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
574                   "`%s' not properly installed, cannot run NAT test!\n",
575                   "gnunet-nat-server");
576       return 0;
577     }
578   }
579
580   ret = check ();
581
582   GNUNET_free (cfg_file_p1);
583   GNUNET_free (cfg_file_p2);
584
585   return ret;
586 }
587
588
589 /* end of test_transport_api_reliability.c */