glitch in the license text detected by hyazinthe, thank you!
[oweals/gnunet.git] / src / core / test_core_api_reliability.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009, 2010, 2015, 2016 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14 */
15 /**
16  * @file core/test_core_api_reliability.c
17  * @brief testcase for core_api.c focusing on reliable transmission (with TCP)
18  * @author Christian Grothoff
19  */
20 #include "platform.h"
21 #include "gnunet_arm_service.h"
22 #include "gnunet_core_service.h"
23 #include "gnunet_util_lib.h"
24 #include "gnunet_ats_service.h"
25 #include "gnunet_transport_service.h"
26 #include "gnunet_transport_hello_service.h"
27 #include <gauger.h>
28
29 /**
30  * Note that this value must not significantly exceed
31  * 'MAX_PENDING' in 'gnunet-service-transport.c', otherwise
32  * messages may be dropped even for a reliable transport.
33  */
34 #define TOTAL_MSGS (600 * 10)
35
36 /**
37  * How long until we give up on transmitting the message?
38  */
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 600)
40
41 #define MTYPE 12345
42
43
44 static unsigned long long total_bytes;
45
46 static struct GNUNET_TIME_Absolute start_time;
47
48 static struct GNUNET_SCHEDULER_Task *err_task;
49
50
51 struct PeerContext
52 {
53   struct GNUNET_CONFIGURATION_Handle *cfg;
54   struct GNUNET_CORE_Handle *ch;
55   struct GNUNET_MQ_Handle *mq;
56   struct GNUNET_PeerIdentity id;
57   struct GNUNET_TRANSPORT_OfferHelloHandle *oh;
58   struct GNUNET_MessageHeader *hello;
59   struct GNUNET_TRANSPORT_HelloGetHandle *ghh;
60   struct GNUNET_ATS_ConnectivityHandle *ats;
61   struct GNUNET_ATS_ConnectivitySuggestHandle *ats_sh;
62   int connect_status;
63   struct GNUNET_OS_Process *arm_proc;
64 };
65
66 static struct PeerContext p1;
67
68 static struct PeerContext p2;
69
70 static int ok;
71
72 static int32_t tr_n;
73
74
75 #define OKPP do { ok++; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Now at stage %u at %s:%u\n", ok, __FILE__, __LINE__); } while (0)
76
77 struct TestMessage
78 {
79   struct GNUNET_MessageHeader header;
80   uint32_t num GNUNET_PACKED;
81 };
82
83
84 static unsigned int
85 get_size (unsigned int iter)
86 {
87   unsigned int ret;
88
89   if (iter < 60000)
90     return iter + sizeof (struct TestMessage);
91   ret = (iter * iter * iter);
92   return sizeof (struct TestMessage) + (ret % 60000);
93 }
94
95
96 static void
97 terminate_peer (struct PeerContext *p)
98 {
99   if (NULL != p->ch)
100   {
101     GNUNET_CORE_disconnect (p->ch);
102     p->ch = NULL;
103   }
104   if (NULL != p->ghh)
105   {
106     GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
107     p->ghh = NULL;
108   }
109   if (NULL != p->oh)
110   {
111     GNUNET_TRANSPORT_offer_hello_cancel (p->oh);
112     p->oh = NULL;
113   }
114   if (NULL != p->ats_sh)
115   {
116     GNUNET_ATS_connectivity_suggest_cancel (p->ats_sh);
117     p->ats_sh = NULL;
118   }
119   if (NULL != p->ats)
120   {
121     GNUNET_ATS_connectivity_done (p->ats);
122     p->ats = NULL;
123   }
124 }
125
126
127 static void
128 terminate_task_error (void *cls)
129 {
130   err_task = NULL;
131   GNUNET_break (0);
132   GNUNET_SCHEDULER_shutdown ();
133   ok = 42;
134 }
135
136
137 static void
138 do_shutdown (void *cls)
139 {
140   unsigned long long delta;
141
142   delta = GNUNET_TIME_absolute_get_duration (start_time).rel_value_us;
143   FPRINTF (stderr,
144            "\nThroughput was %llu kb/s\n",
145            total_bytes * 1000000LL / 1024 / delta);
146   GAUGER ("CORE",
147           "Core throughput/s",
148           total_bytes * 1000000LL / 1024 / delta,
149           "kb/s");
150   if (NULL != err_task)
151   {
152     GNUNET_SCHEDULER_cancel (err_task);
153     err_task = NULL;
154   }
155   terminate_peer (&p1);
156   terminate_peer (&p2);
157
158 }
159
160
161 static void
162 send_message (struct GNUNET_MQ_Handle *mq,
163               int32_t num)
164 {
165   struct GNUNET_MQ_Envelope *env;
166   struct TestMessage *hdr;
167   unsigned int s;
168
169   GNUNET_assert (NULL != mq);
170   GNUNET_assert (tr_n < TOTAL_MSGS);
171   s = get_size (tr_n);
172   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
173               "Sending message %u of size %u\n",
174               tr_n,
175               s);
176   env = GNUNET_MQ_msg_extra (hdr,
177                              s - sizeof (struct TestMessage),
178                              MTYPE);
179   hdr->num = htonl (tr_n);
180   memset (&hdr[1],
181           tr_n,
182           s - sizeof (struct TestMessage));
183   tr_n++;
184   GNUNET_SCHEDULER_cancel (err_task);
185   err_task =
186       GNUNET_SCHEDULER_add_delayed (TIMEOUT,
187                                     &terminate_task_error,
188                                     NULL);
189   total_bytes += s;
190   GNUNET_MQ_send (mq,
191                   env);
192 }
193
194
195 static void *
196 connect_notify (void *cls,
197                 const struct GNUNET_PeerIdentity *peer,
198                 struct GNUNET_MQ_Handle *mq)
199 {
200   struct PeerContext *pc = cls;
201
202   if (0 == memcmp (&pc->id,
203                    peer,
204                    sizeof (struct GNUNET_PeerIdentity)))
205     return (void *) peer;
206   pc->mq = mq;
207   GNUNET_assert (0 == pc->connect_status);
208   pc->connect_status = 1;
209   if (pc == &p1)
210   {
211     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
212                 "Encrypted connection established to peer `%s'\n",
213                 GNUNET_i2s (peer));
214     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
215                 "Asking core (1) for transmission to peer `%s'\n",
216                 GNUNET_i2s (&p2.id));
217     GNUNET_SCHEDULER_cancel (err_task);
218     err_task =
219         GNUNET_SCHEDULER_add_delayed (TIMEOUT,
220                                       &terminate_task_error,
221                                       NULL);
222     start_time = GNUNET_TIME_absolute_get ();
223     send_message (mq,
224                   0);
225   }
226   return (void *) peer;
227 }
228
229
230 static void
231 disconnect_notify (void *cls,
232                    const struct GNUNET_PeerIdentity *peer,
233                    void *internal_cls)
234 {
235   struct PeerContext *pc = cls;
236
237   if (0 == memcmp (&pc->id,
238                    peer,
239                    sizeof (struct GNUNET_PeerIdentity)))
240     return;
241   pc->mq = NULL;
242   pc->connect_status = 0;
243   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
244               "Encrypted connection to `%s' cut\n",
245               GNUNET_i2s (peer));
246 }
247
248
249 static int
250 check_test (void *cls,
251             const struct TestMessage *hdr)
252 {
253   return GNUNET_OK; /* accept all */
254 }
255
256
257 static void
258 handle_test (void *cls,
259              const struct TestMessage *hdr)
260 {
261   static int n;
262   unsigned int s;
263
264   s = get_size (n);
265   if (ntohs (hdr->header.size) != s)
266   {
267     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268                 "Expected message %u of size %u, got %u bytes of message %u\n",
269                 n,
270                 s,
271                 ntohs (hdr->header.size),
272                 ntohl (hdr->num));
273     GNUNET_SCHEDULER_cancel (err_task);
274     err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error,
275                                          NULL);
276     return;
277   }
278   if (ntohl (hdr->num) != n)
279   {
280     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
281                 "Expected message %u of size %u, got %u bytes of message %u\n",
282                 n,
283                 s,
284                 (unsigned int) ntohs (hdr->header.size),
285                 (unsigned int) ntohl (hdr->num));
286     GNUNET_SCHEDULER_cancel (err_task);
287     err_task = GNUNET_SCHEDULER_add_now (&terminate_task_error,
288                                          NULL);
289     return;
290   }
291   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
292               "Got message %u of size %u\n",
293               (unsigned int) ntohl (hdr->num),
294               (unsigned int) ntohs (hdr->header.size));
295   n++;
296   if (0 == (n % (TOTAL_MSGS / 100)))
297     FPRINTF (stderr,
298              "%s",
299              ".");
300   if (n == TOTAL_MSGS)
301   {
302     ok = 0;
303     GNUNET_SCHEDULER_shutdown ();
304   }
305   else
306   {
307     if (n == tr_n)
308     {
309       send_message (p1.mq,
310                     tr_n);
311     }
312   }
313 }
314
315
316 static void
317 init_notify (void *cls,
318              const struct GNUNET_PeerIdentity *my_identity)
319 {
320   struct PeerContext *p = cls;
321   struct GNUNET_MQ_MessageHandler handlers[] = {
322     GNUNET_MQ_hd_var_size (test,
323                            MTYPE,
324                            struct TestMessage,
325                            NULL),
326     GNUNET_MQ_handler_end ()
327   };
328
329   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
330               "Connection to CORE service of `%s' established\n",
331               GNUNET_i2s (my_identity));
332   p->id = *my_identity;
333   if (cls == &p1)
334   {
335     GNUNET_assert (ok == 2);
336     OKPP;
337     /* connect p2 */
338     GNUNET_assert (NULL !=
339                    (p2.ch = GNUNET_CORE_connect (p2.cfg,
340                                                  &p2,
341                                                  &init_notify,
342                                                  &connect_notify,
343                                                  &disconnect_notify,
344                                                  handlers)));
345   }
346   else
347   {
348     GNUNET_assert (ok == 3);
349     OKPP;
350     GNUNET_assert (cls == &p2);
351     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
352                 "Asking transport (1) to connect to peer `%s'\n",
353                 GNUNET_i2s (&p2.id));
354     p1.ats_sh = GNUNET_ATS_connectivity_suggest (p1.ats,
355                                                  &p2.id,
356                                                  1);
357   }
358 }
359
360
361 static void
362 offer_hello_done (void *cls)
363 {
364   struct PeerContext *p = cls;
365
366   p->oh = NULL;
367 }
368
369
370 static void
371 process_hello (void *cls,
372                const struct GNUNET_MessageHeader *message)
373 {
374   struct PeerContext *p = cls;
375
376   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
377               "Received (my) `%s' from transport service\n", "HELLO");
378   GNUNET_assert (message != NULL);
379   GNUNET_free_non_null (p->hello);
380   p->hello = GNUNET_copy_message (message);
381   if ((p == &p1) && (NULL == p2.oh))
382     p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
383                                           message,
384                                           &offer_hello_done,
385                                           &p2);
386   if ((p == &p2) && (NULL == p1.oh))
387     p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg,
388                                           message,
389                                           &offer_hello_done,
390                                           &p1);
391
392   if ((p == &p1) && (p2.hello != NULL) && (NULL == p1.oh) )
393     p1.oh = GNUNET_TRANSPORT_offer_hello (p1.cfg,
394                                           p2.hello,
395                                           &offer_hello_done,
396                                           &p1);
397   if ((p == &p2) && (p1.hello != NULL) && (NULL == p2.oh) )
398     p2.oh = GNUNET_TRANSPORT_offer_hello (p2.cfg,
399                                           p1.hello,
400                                           &offer_hello_done,
401                                           &p2);
402 }
403
404
405 static void
406 setup_peer (struct PeerContext *p,
407             const char *cfgname)
408 {
409   char *binary;
410
411   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-service-arm");
412   p->cfg = GNUNET_CONFIGURATION_create ();
413   p->arm_proc
414     = GNUNET_OS_start_process (GNUNET_YES,
415                                GNUNET_OS_INHERIT_STD_OUT_AND_ERR,
416                                NULL, NULL, NULL,
417                                binary,
418                                "gnunet-service-arm",
419                                "-c",
420                                cfgname,
421                                NULL);
422   GNUNET_assert (GNUNET_OK ==
423                  GNUNET_CONFIGURATION_load (p->cfg,
424                                             cfgname));
425   p->ats = GNUNET_ATS_connectivity_init (p->cfg);
426   GNUNET_assert (NULL != p->ats);
427   p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
428                                        GNUNET_TRANSPORT_AC_ANY,
429                                        &process_hello,
430                                        p);
431   GNUNET_free (binary);
432 }
433
434
435 static void
436 run (void *cls,
437      char *const *args,
438      const char *cfgfile,
439      const struct GNUNET_CONFIGURATION_Handle *cfg)
440 {
441   struct GNUNET_MQ_MessageHandler handlers[] = {
442     GNUNET_MQ_hd_fixed_size (test,
443                              MTYPE,
444                              struct TestMessage,
445                              NULL),
446     GNUNET_MQ_handler_end ()
447   };
448
449   GNUNET_assert (ok == 1);
450   OKPP;
451   setup_peer (&p1,
452               "test_core_api_peer1.conf");
453   setup_peer (&p2,
454               "test_core_api_peer2.conf");
455   err_task =
456       GNUNET_SCHEDULER_add_delayed (TIMEOUT,
457                                     &terminate_task_error,
458                                     NULL);
459   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
460                                  NULL);
461
462   GNUNET_assert (NULL !=
463                  (p1.ch = GNUNET_CORE_connect (p1.cfg,
464                                                &p1,
465                                                &init_notify,
466                                                &connect_notify,
467                                                &disconnect_notify,
468                                                handlers)));
469 }
470
471
472 static void
473 stop_arm (struct PeerContext *p)
474 {
475   if (0 != GNUNET_OS_process_kill (p->arm_proc,
476                                    GNUNET_TERM_SIG))
477     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
478                          "kill");
479   if (GNUNET_OK != GNUNET_OS_process_wait (p->arm_proc))
480     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
481                          "waitpid");
482   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483               "ARM process %u stopped\n",
484               GNUNET_OS_process_get_pid (p->arm_proc));
485   GNUNET_OS_process_destroy (p->arm_proc);
486   p->arm_proc = NULL;
487   GNUNET_CONFIGURATION_destroy (p->cfg);
488 }
489
490
491 int
492 main (int argc,
493       char *argv1[])
494 {
495   char *const argv[] = {
496     "test-core-api-reliability",
497     "-c",
498     "test_core_api_data.conf",
499     NULL
500   };
501   struct GNUNET_GETOPT_CommandLineOption options[] = {
502     GNUNET_GETOPT_OPTION_END
503   };
504   ok = 1;
505   GNUNET_log_setup ("test-core-api-reliability",
506                     "WARNING",
507                     NULL);
508   GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
509                       argv,
510                       "test-core-api-reliability",
511                       "nohelp",
512                       options,
513                       &run,
514                       &ok);
515   stop_arm (&p1);
516   stop_arm (&p2);
517   GNUNET_free_non_null (p1.hello);
518   GNUNET_free_non_null (p2.hello);
519   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-1");
520   GNUNET_DISK_directory_remove ("/tmp/test-gnunet-core-peer-2");
521
522   return ok;
523 }
524
525 /* end of test_core_api_reliability.c */