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