ported unreliability to transport testing
[oweals/gnunet.git] / src / transport / test_transport_api_unreliability.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_unreliability.c
22  * @brief test case for transports; ensures messages get
23  *        through, regardless of order
24  *
25  * This test case serves as a base for unreliable
26  * transport test cases to check that the transports
27  * achieve reliable message delivery.
28  */
29 #include "platform.h"
30 #include "gnunet_common.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_getopt_lib.h"
33 #include "gnunet_os_lib.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_scheduler_lib.h"
36 #include "gnunet_server_lib.h"
37 #include "gnunet_transport_service.h"
38 #include "gauger.h"
39 #include "transport.h"
40 #include "transport-testing.h"
41
42 #define VERBOSE GNUNET_NO
43
44 #define VERBOSE_ARM GNUNET_NO
45
46 #define START_ARM GNUNET_YES
47
48 /**
49  * How long until we give up on transmitting the message?
50  */
51 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
52
53 static int ok;
54
55 static GNUNET_SCHEDULER_TaskIdentifier die_task;
56
57 struct PeerContext *p1;
58
59 struct PeerContext *p2;
60
61 struct GNUNET_TRANSPORT_TransmitHandle *th;
62
63 char *cfg_file_p1;
64
65 char *cfg_file_p2;
66
67 /*
68  * Testcase specific declarations
69  */
70
71 /**
72  * Note that this value must not significantly exceed
73  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
74  * messages may be dropped even for a reliable transport.
75  */
76 #define TOTAL_MSGS (1024 * 3)
77
78 #define MTYPE 12345
79
80 struct TestMessage
81 {
82   struct GNUNET_MessageHeader header;
83   uint32_t num;
84 };
85
86 static char *test_name;
87
88 static int msg_scheduled;
89 static int msg_sent;
90 static int msg_recv_expected;
91 static int msg_recv;
92
93 static int test_failed;
94
95 static unsigned long long total_bytes;
96
97 static struct GNUNET_TIME_Absolute start_time;
98
99 static char bitmap[TOTAL_MSGS / 8];
100
101 /*
102  * END Testcase specific declarations
103  */
104
105 #if VERBOSE
106 #define OKPP do { ok++; fprintf (stderr, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
107 #else
108 #define OKPP do { ok++; } while (0)
109 #endif
110
111 int
112 get_bit (const char *map, unsigned int bit);
113
114 static void
115 end ()
116 {
117   unsigned long long delta;
118
119   char *value_name;
120
121   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Stopping peers\n");
122
123   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value;
124   fprintf (stderr, "\nThroughput was %llu kb/s\n",
125            total_bytes * 1000 / 1024 / delta);
126   GNUNET_asprintf(&value_name, "unreliable_%s", test_name);
127   GAUGER ("TRANSPORT", value_name, (int)(total_bytes * 1000 / 1024 /delta), "kb/s");
128   GNUNET_free(value_name);
129
130   if (die_task != GNUNET_SCHEDULER_NO_TASK)
131     GNUNET_SCHEDULER_cancel (die_task);
132
133   if (th != NULL)
134     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
135   th = NULL;
136
137   GNUNET_TRANSPORT_TESTING_stop_peer (p1);
138   GNUNET_TRANSPORT_TESTING_stop_peer (p2);
139
140   ok = 0;
141
142   int i;
143   for (i = 0; i < TOTAL_MSGS; i++)
144   {
145     if (get_bit (bitmap, i) == 0)
146     {
147       GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Did not receive message %d\n", i);
148       ok = -1;
149     }
150   }
151 }
152
153 static void
154 end_badly ()
155 {
156   die_task = GNUNET_SCHEDULER_NO_TASK;
157   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Fail! Stopping peers\n");
158
159   if (test_failed == GNUNET_NO)
160     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Testcase timeout\n");
161   else
162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
163                 "Reliability failed: Last message sent %u, Next message scheduled %u, Last message received %u, Message expected %u\n",
164                 msg_sent, msg_scheduled, msg_recv, msg_recv_expected);
165
166   if (th != NULL)
167     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
168   th = NULL;
169
170   if (p1 != NULL)
171     GNUNET_TRANSPORT_TESTING_stop_peer (p1);
172   if (p2 != NULL)
173     GNUNET_TRANSPORT_TESTING_stop_peer (p2);
174
175   ok = GNUNET_SYSERR;
176 }
177
178
179 static unsigned int
180 get_size (unsigned int iter)
181 {
182   unsigned int ret;
183
184   ret = (iter * iter * iter);
185   return sizeof (struct TestMessage) + (ret % 60000);
186 }
187
188
189 /**
190  * Sets a bit active in the bitmap.
191  *
192  * @param bitIdx which bit to set
193  */
194 static void
195 set_bit (unsigned int bitIdx)
196 {
197   size_t arraySlot;
198   unsigned int targetBit;
199
200   if (bitIdx >= sizeof (bitmap) * 8)
201   {
202     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "tried to set bit %d of %d(!?!?)\n",
203                 bitIdx, sizeof (bitmap) * 8);
204     return;
205   }
206   arraySlot = bitIdx / 8;
207   targetBit = (1L << (bitIdx % 8));
208   bitmap[arraySlot] |= targetBit;
209 }
210
211 /**
212  * Obtain a bit from bitmap.
213  * @param map the bitmap
214  * @param bit index from bitmap
215  *
216  * @return Bit \a bit from hashcode \a code
217  */
218 int
219 get_bit (const char *map, unsigned int bit)
220 {
221   if (bit >= TOTAL_MSGS)
222   {
223     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "get bit %d of %d(!?!?)\n", bit,
224                 sizeof (bitmap) * 8);
225     return 0;
226   }
227   return ((map)[bit >> 3] & (1 << (bit & 7))) > 0;
228 }
229
230
231 static void
232 notify_receive (void *cls, const struct GNUNET_PeerIdentity *peer,
233                 const struct GNUNET_MessageHeader *message,
234                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
235                 uint32_t ats_count)
236 {
237   static int n;
238
239   unsigned int s;
240   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
241   const struct TestMessage *hdr;
242
243   hdr = (const struct TestMessage *) message;
244
245   if (MTYPE != ntohs (message->type))
246     return;
247   msg_recv_expected = n;
248   msg_recv = ntohl (hdr->num);
249   s = get_size (ntohl (hdr->num));
250
251   if (ntohs (message->size) != s)
252   {
253     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
254                 "Expected message %u of size %u, got %u bytes of message %u\n",
255                 ntohl (hdr->num), s, ntohs (message->size), ntohl (hdr->num));
256     if (GNUNET_SCHEDULER_NO_TASK != die_task)
257       GNUNET_SCHEDULER_cancel (die_task);
258     test_failed = GNUNET_YES;
259     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
260     return;
261   }
262
263   memset (cbuf, ntohl (hdr->num), s - sizeof (struct TestMessage));
264   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
265   {
266     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
267                 "Expected message %u with bits %u, but body did not match\n",
268                 ntohl (hdr->num), (unsigned char) n);
269     if (GNUNET_SCHEDULER_NO_TASK != die_task)
270       GNUNET_SCHEDULER_cancel (die_task);
271     test_failed = GNUNET_YES;
272     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
273     return;
274   }
275 #if VERBOSE
276   if (ntohl (hdr->num) % 5 == 0)
277   {
278     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n",
279                 ntohl (hdr->num), ntohs (message->size));
280   }
281 #endif
282   n++;
283   set_bit (ntohl (hdr->num));
284   if (0 == (n % (TOTAL_MSGS / 100)))
285   {
286     fprintf (stderr, ".");
287     if (GNUNET_SCHEDULER_NO_TASK != die_task)
288       GNUNET_SCHEDULER_cancel (die_task);
289     test_failed = GNUNET_YES;
290     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
291   }
292   if (n == TOTAL_MSGS)
293     end ();
294 }
295
296
297 static size_t
298 notify_ready (void *cls, size_t size, void *buf)
299 {
300   static int n;
301   char *cbuf = buf;
302   struct TestMessage hdr;
303   unsigned int s;
304   unsigned int ret;
305
306   th = NULL;
307
308   if (buf == NULL)
309   {
310     GNUNET_break (0);
311     ok = 42;
312     return 0;
313   }
314   ret = 0;
315   s = get_size (n);
316   GNUNET_assert (size >= s);
317   GNUNET_assert (buf != NULL);
318   cbuf = buf;
319   do
320   {
321     hdr.header.size = htons (s);
322     hdr.header.type = htons (MTYPE);
323     hdr.num = htonl (n);
324     msg_sent = n;
325     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
326     ret += sizeof (struct TestMessage);
327     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
328     ret += s - sizeof (struct TestMessage);
329 #if VERBOSE
330     if (n % 5000 == 0)
331     {
332       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message %u of size %u\n", n,
333                   s);
334     }
335
336 #endif
337     n++;
338     s = get_size (n);
339     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
340       break;                    /* sometimes pack buffer full, sometimes not */
341   }
342   while (size - ret >= s);
343   if (n < TOTAL_MSGS)
344   {
345     th  =
346         GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, s, 0, TIMEOUT,
347                                                 &notify_ready, NULL);
348     msg_scheduled = n;
349   }
350   else
351   {
352     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353                 "All messages scheduled to be sent!!\n");
354     if (GNUNET_SCHEDULER_NO_TASK != die_task)
355       GNUNET_SCHEDULER_cancel (die_task);
356     die_task =
357         GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly,
358                                       NULL);
359   }
360   if (n % 5000 == 0)
361   {
362     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
363                 "Returning total message block of size %u\n", ret);
364   }
365   total_bytes += ret;
366   return ret;
367 }
368
369
370 static void
371 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
372                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
373                 uint32_t ats_count)
374 {
375
376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
377               GNUNET_i2s (peer), cls);
378
379   if (cls == p1)
380   {
381     GNUNET_TRANSPORT_set_quota (p1->th, &p2->id,
382                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
383                                                              1024),
384                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
385                                                              1024));
386   }
387   else if (cls == p2)
388   {
389     GNUNET_TRANSPORT_set_quota (p2->th, &p1->id,
390                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
391                                                              1024),
392                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
393                                                              1024));
394   }
395 }
396
397
398 static void
399 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
400 {
401   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
402               GNUNET_i2s (peer), cls);
403 }
404
405 static void
406 sendtask ()
407 {
408   start_time = GNUNET_TIME_absolute_get ();
409   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, get_size (0), 0,
410                                                TIMEOUT, &notify_ready, NULL);
411 }
412
413 static void
414 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
415 {
416   char *p1_c = strdup (GNUNET_i2s (&p1->id));
417
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
419               GNUNET_i2s (&p2->id));
420   GNUNET_free (p1_c);
421
422   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
423   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
424 }
425
426 static void
427 run (void *cls, char *const *args, const char *cfgfile,
428      const struct GNUNET_CONFIGURATION_Handle *cfg)
429 {
430   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
431
432   p1 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p1, &notify_receive,
433                                             &notify_connect, &notify_disconnect,
434                                             NULL);
435   p2 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p2, &notify_receive,
436                                             &notify_connect, &notify_disconnect,
437                                             NULL);
438
439   GNUNET_TRANSPORT_TESTING_connect_peers (p1, p2, &testing_connect_cb, NULL);
440 }
441
442 static int
443 check ()
444 {
445   static char *const argv[] = { "test-transport-api-unreliability",
446     "-c",
447     "test_transport_api_data.conf",
448 #if VERBOSE
449     "-L", "DEBUG",
450 #endif
451     NULL
452   };
453   static struct GNUNET_GETOPT_CommandLineOption options[] = {
454     GNUNET_GETOPT_OPTION_END
455   };
456
457 #if WRITECONFIG
458   setTransportOptions ("test_transport_api_data.conf");
459 #endif
460   ok = GNUNET_SYSERR;
461
462   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
463                       "test-transport-api-unreliability", "nohelp", options, &run, &ok);
464
465   return ok;
466 }
467
468 int
469 main (int argc, char *argv[])
470 {
471   int ret;
472   int nat_res;
473
474   GNUNET_log_setup ("test-transport-api-unreliability",
475 #if VERBOSE
476                     "DEBUG",
477 #else
478                     "WARNING",
479 #endif
480                     NULL);
481
482   char *pch = strdup (argv[0]);
483   char *backup = pch;
484   char *filename = NULL;
485   char *dotexe;
486   char *src_name  = strdup (__FILE__);
487   char *split = NULL;
488
489   /* get executable filename */
490   pch = strtok (pch, "/");
491   while (pch != NULL)
492   {
493     pch = strtok (NULL, "/");
494     if (pch != NULL)
495       filename = pch;
496   }
497   /* remove "lt-" */
498   filename = strstr (filename, "tes");
499   if (NULL != (dotexe = strstr (filename, ".exe")))
500     dotexe[0] = '\0';
501
502   /* create cfg filename */
503   GNUNET_asprintf (&cfg_file_p1, "%s_peer1.conf", filename);
504   GNUNET_asprintf (&cfg_file_p2, "%s_peer2.conf", filename);
505
506   split = strstr (src_name, ".");
507   if (split != NULL)
508   {
509     split[0] = '\0';
510     test_name = strdup(&filename[strlen(src_name)+1]);
511   }
512   else
513     test_name = NULL;
514
515   GNUNET_free (src_name);
516   GNUNET_free (backup);
517
518   if ((strstr (argv[0], "tcp_nat") != NULL) || (strstr (argv[0], "udp_nat") != NULL))
519   {
520     nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server");
521     if (GNUNET_NO == nat_res)
522     {
523       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
524           "Cannot run NAT test: `%s' %s \n",
525           "gnunet-nat-server",
526            "SUID not set");
527       return 0;
528     }
529     if (GNUNET_SYSERR ==  nat_res)
530     {
531       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
532           "Cannot run NAT test: `%s' %s \n",
533           "gnunet-nat-server",
534           "file not found");
535       return 0;
536     }
537   }
538   ret = check ();
539
540   GNUNET_free (cfg_file_p1);
541   GNUNET_free (cfg_file_p2);
542   GNUNET_free_non_null (test_name);
543
544   return ret;
545 }
546
547 /* end of test_transport_api_unreliability.c */