617092676a1221d0b9356b850830ba8a6f4de73a
[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, "\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, const struct GNUNET_PeerIdentity *peer,
165                 const struct GNUNET_MessageHeader *message,
166                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
167                 uint32_t ats_count)
168 {
169   static int n;
170   unsigned int s;
171   char cbuf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
172   const struct TestMessage *hdr;
173
174   hdr = (const struct TestMessage *) message;
175   s = get_size (n);
176   if (MTYPE != ntohs (message->type))
177     return;
178   msg_recv_expected = n;
179   msg_recv = ntohl (hdr->num);
180   if (ntohs (message->size) != (s))
181   {
182     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183                 "Expected message %u of size %u, got %u bytes of message %u\n",
184                 n, s, ntohs (message->size), ntohl (hdr->num));
185     if (die_task != GNUNET_SCHEDULER_NO_TASK)
186       GNUNET_SCHEDULER_cancel (die_task);
187     test_failed = GNUNET_YES;
188     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
189     return;
190   }
191   if (ntohl (hdr->num) != n)
192   {
193     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194                 "Expected message %u of size %u, got %u bytes of message %u\n",
195                 n, s, ntohs (message->size), ntohl (hdr->num));
196     if (die_task != GNUNET_SCHEDULER_NO_TASK)
197       GNUNET_SCHEDULER_cancel (die_task);
198     test_failed = GNUNET_YES;
199     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
200     return;
201   }
202   memset (cbuf, n, s - sizeof (struct TestMessage));
203   if (0 != memcmp (cbuf, &hdr[1], s - sizeof (struct TestMessage)))
204   {
205     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206                 "Expected message %u with bits %u, but body did not match\n", n,
207                 (unsigned char) n);
208     if (die_task != GNUNET_SCHEDULER_NO_TASK)
209       GNUNET_SCHEDULER_cancel (die_task);
210     test_failed = GNUNET_YES;
211     die_task = GNUNET_SCHEDULER_add_now (&end_badly, NULL);
212     return;
213   }
214 #if VERBOSE
215   if (ntohl (hdr->num) % 5000 == 0)
216   {
217     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got message %u of size %u\n",
218                 ntohl (hdr->num), ntohs (message->size));
219   }
220 #endif
221   n++;
222   if (0 == (n % (TOTAL_MSGS / 100)))
223   {
224     fprintf (stderr, ".");
225     if (die_task != GNUNET_SCHEDULER_NO_TASK)
226       GNUNET_SCHEDULER_cancel (die_task);
227     die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
228   }
229   if (n == TOTAL_MSGS)
230   {
231     ok = 0;
232     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages received\n");
233     end ();
234   }
235 }
236
237
238 static size_t
239 notify_ready (void *cls, size_t size, void *buf)
240 {
241   static int n;
242   char *cbuf = buf;
243   struct TestMessage hdr;
244   unsigned int s;
245   unsigned int ret;
246
247   if (buf == NULL)
248   {
249     GNUNET_break (0);
250     ok = 42;
251     return 0;
252   }
253   th = NULL;
254   ret = 0;
255   s = get_size (n);
256   GNUNET_assert (size >= s);
257   GNUNET_assert (buf != NULL);
258   cbuf = buf;
259   do
260   {
261     hdr.header.size = htons (s);
262     hdr.header.type = htons (MTYPE);
263     hdr.num = htonl (n);
264     msg_sent = n;
265     memcpy (&cbuf[ret], &hdr, sizeof (struct TestMessage));
266     ret += sizeof (struct TestMessage);
267     memset (&cbuf[ret], n, s - sizeof (struct TestMessage));
268     ret += s - sizeof (struct TestMessage);
269 #if VERBOSE
270     if (n % 5000 == 0)
271     {
272       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending message %u of size %u\n", n,
273                   s);
274     }
275 #endif
276     n++;
277     s = get_size (n);
278     if (0 == GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, 16))
279       break;                    /* sometimes pack buffer full, sometimes not */
280   }
281   while (size - ret >= s);
282   if (n < TOTAL_MSGS)
283   {
284     if (th == NULL)
285       th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, s, 0,
286                                                    TIMEOUT, &notify_ready,
287                                                    NULL);
288     msg_scheduled = n;
289   }
290   if (n % 5000 == 0)
291   {
292     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
293                 "Returning total message block of size %u\n", ret);
294   }
295   total_bytes += ret;
296   if (n == TOTAL_MSGS)
297   {
298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "All messages sent\n");
299   }
300   return ret;
301 }
302
303
304 static void
305 notify_connect (void *cls, const struct GNUNET_PeerIdentity *peer,
306                 const struct GNUNET_TRANSPORT_ATS_Information *ats,
307                 uint32_t ats_count)
308 {
309
310   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' connected to us (%p)!\n",
311               GNUNET_i2s (peer), cls);
312
313   if (cls == p1)
314   {
315     GNUNET_TRANSPORT_set_quota (p1->th, &p2->id,
316                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
317                                                              1024),
318                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
319                                                              1024));
320   }
321   else if (cls == p2)
322   {
323     GNUNET_TRANSPORT_set_quota (p2->th, &p1->id,
324                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
325                                                              1024),
326                                 GNUNET_BANDWIDTH_value_init (1024 * 1024 *
327                                                              1024));
328   }
329 }
330
331
332 static void
333 notify_disconnect (void *cls, const struct GNUNET_PeerIdentity *peer)
334 {
335   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peer `%4s' disconnected (%p)!\n",
336               GNUNET_i2s (peer), cls);
337 }
338
339 static void
340 sendtask ()
341 {
342   start_time = GNUNET_TIME_absolute_get ();
343   th = GNUNET_TRANSPORT_notify_transmit_ready (p2->th, &p1->id, get_size (0), 0,
344                                                TIMEOUT, &notify_ready, NULL);
345 }
346
347 static void
348 testing_connect_cb (struct PeerContext *p1, struct PeerContext *p2, void *cls)
349 {
350   char *p1_c = strdup (GNUNET_i2s (&p1->id));
351
352   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Peers connected: %s <-> %s\n", p1_c,
353               GNUNET_i2s (&p2->id));
354   GNUNET_free (p1_c);
355
356   // FIXME: THIS IS REQUIRED! SEEMS TO BE A BUG!
357   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &sendtask, NULL);
358 }
359
360 static void
361 run (void *cls, char *const *args, const char *cfgfile,
362      const struct GNUNET_CONFIGURATION_Handle *cfg)
363 {
364   die_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, &end_badly, NULL);
365
366   p1 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p1, &notify_receive,
367                                             &notify_connect, &notify_disconnect,
368                                             NULL);
369   p2 = GNUNET_TRANSPORT_TESTING_start_peer (cfg_file_p2, &notify_receive,
370                                             &notify_connect, &notify_disconnect,
371                                             NULL);
372
373   GNUNET_TRANSPORT_TESTING_connect_peers (p1, p2, &testing_connect_cb, NULL);
374 }
375
376 static int
377 check ()
378 {
379   static char *const argv[] = { "test-transport-api",
380     "-c",
381     "test_transport_api_data.conf",
382 #if VERBOSE
383     "-L", "DEBUG",
384 #endif
385     NULL
386   };
387   static struct GNUNET_GETOPT_CommandLineOption options[] = {
388     GNUNET_GETOPT_OPTION_END
389   };
390
391 #if WRITECONFIG
392   setTransportOptions ("test_transport_api_data.conf");
393 #endif
394   ok = 1;
395   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, argv,
396                       "test-transport-api", "nohelp", options, &run, &ok);
397
398   return ok;
399 }
400
401 int
402 main (int argc, char *argv[])
403 {
404   int ret;
405   int nat_res;
406
407   GNUNET_log_setup ("test-transport-api",
408 #if VERBOSE
409                     "DEBUG",
410 #else
411                     "WARNING",
412 #endif
413                     NULL);
414
415   char *pch = strdup (argv[0]);
416   char *backup = pch;
417   char *filename = NULL;
418   char *dotexe;
419
420   /* get executable filename */
421   pch = strtok (pch, "/");
422   while (pch != NULL)
423   {
424     pch = strtok (NULL, "/");
425     if (pch != NULL)
426       filename = pch;
427   }
428   /* remove "lt-" */
429   filename = strstr (filename, "tes");
430   if (NULL != (dotexe = strstr (filename, ".exe")))
431     dotexe[0] = '\0';
432
433   /* create cfg filename */
434   GNUNET_asprintf (&cfg_file_p1, "%s_peer1.conf", filename);
435   GNUNET_asprintf (&cfg_file_p2, "%s_peer2.conf", filename);
436   GNUNET_free (backup);
437
438   if ((strstr (argv[0], "tcp_nat") != NULL) || (strstr (argv[0], "udp_nat") != NULL))
439   {
440     nat_res = GNUNET_OS_check_helper_binary ("gnunet-nat-server");
441     if (GNUNET_NO == nat_res)
442     {
443       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
444           "Cannot run NAT test: `%s' %s \n",
445           "gnunet-nat-server",
446            "SUID not set");
447       return 0;
448     }
449     if (GNUNET_SYSERR ==  nat_res)
450     {
451       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
452           "Cannot run NAT test: `%s' %s \n",
453           "gnunet-nat-server",
454           "file not found");
455       return 0;
456     }
457   }
458   ret = check ();
459
460   GNUNET_free (cfg_file_p1);
461   GNUNET_free (cfg_file_p2);
462
463   return ret;
464 }
465
466
467 /* end of test_transport_api_reliability.c */