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