error handling
[oweals/gnunet.git] / src / transport / test_communicator_basic.c
1 /*
2     This file is part of GNUnet.
3     Copyright (C) 2019 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 /**
22 * @file transport/test_communicator_basic.c
23 * @brief test the communicators
24 * @author Julius Bünger
25 * @author Martin Schanzenbach
26 */
27 #include "platform.h"
28 #include "gnunet_util_lib.h"
29 #include "transport-testing2.h"
30 #include "gnunet_ats_transport_service.h"
31 #include "gnunet_signatures.h"
32 #include "gnunet_testing_lib.h"
33 #include "transport.h"
34
35 #include <inttypes.h>
36
37
38 #define LOG(kind, ...) GNUNET_log_from (kind, \
39                                         "test_transport_communicator", \
40                                         __VA_ARGS__)
41
42 #define NUM_PEERS 2
43
44 static struct GNUNET_SCHEDULER_Task *to_task;
45
46 static int queue_est = GNUNET_NO;
47
48 static struct GNUNET_PeerIdentity peer_id[NUM_PEERS];
49
50 static char *communicator_binary;
51
52 static struct
53 GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_hs[NUM_PEERS];
54
55 static struct GNUNET_CONFIGURATION_Handle *cfg_peers[NUM_PEERS];
56
57 static char *cfg_peers_name[NUM_PEERS];
58
59 static int ret;
60
61 static struct GNUNET_TIME_Absolute start_short;
62
63 static struct GNUNET_TIME_Absolute start_long;
64
65 static struct GNUNET_TIME_Absolute timeout;
66
67 static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *my_tc;
68
69 #define SHORT_MESSAGE_SIZE 128
70
71 #define LONG_MESSAGE_SIZE 32000
72
73 #define BURST_PACKETS 5000
74
75 #define TOTAL_ITERATIONS 5
76
77 #define PEER_A 0
78
79 #define PEER_B 1
80
81 static unsigned int iterations_left = TOTAL_ITERATIONS;
82
83 #define SHORT_BURST_WINDOW \
84   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2)
85
86 #define LONG_BURST_WINDOW \
87   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,2)
88
89 enum TestPhase
90 {
91   TP_BURST_SHORT,
92   TP_BURST_LONG,
93   TP_SIZE_CHECK
94 };
95
96
97 static size_t num_sent = 0;
98
99 static uint32_t ack = 0;
100
101 static enum TestPhase phase;
102
103 static size_t num_received = 0;
104
105 static uint64_t avg_latency = 0;
106
107 static struct GNUNET_TIME_Relative duration;
108
109
110 static void
111 communicator_available_cb (void *cls,
112                            struct
113                            GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle
114                            *tc_h,
115                            enum GNUNET_TRANSPORT_CommunicatorCharacteristics cc,
116                            char *address_prefix)
117 {
118   LOG (GNUNET_ERROR_TYPE_INFO,
119        "Communicator available. (cc: %u, prefix: %s)\n",
120        cc,
121        address_prefix);
122 }
123
124
125 static void
126 add_address_cb (void *cls,
127                 struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *
128                 tc_h,
129                 const char *address,
130                 struct GNUNET_TIME_Relative expiration,
131                 uint32_t aid,
132                 enum GNUNET_NetworkType nt)
133 {
134   LOG (GNUNET_ERROR_TYPE_DEBUG,
135        "New address. (addr: %s, expir: %" PRIu32 ", ID: %" PRIu32 ", nt: %u\n",
136        address,
137        expiration.rel_value_us,
138        aid,
139        nt);
140   // addresses[1] = GNUNET_strdup (address);
141   if ((0 == strcmp ((char*) cls, cfg_peers_name[PEER_B])) &&
142       (GNUNET_NO == queue_est))
143   {
144     queue_est = GNUNET_YES;
145     GNUNET_TRANSPORT_TESTING_transport_communicator_open_queue (tc_hs[PEER_A],
146                                                                 &peer_id[PEER_B],
147                                                                 address);
148   }
149 }
150
151
152 /**
153  * @brief Callback that informs whether the requested queue will be
154  * established
155  *
156  * Implements #GNUNET_TRANSPORT_TESTING_QueueCreateReplyCallback.
157  *
158  * @param cls Closure - unused
159  * @param tc_h Communicator handle - unused
160  * @param will_try #GNUNET_YES if queue will be established
161  *                #GNUNET_NO if queue will not be established (bogous address)
162  */
163 static void
164 queue_create_reply_cb (void *cls,
165                        struct
166                        GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *
167                        tc_h,
168                        int will_try)
169 {
170   if (GNUNET_YES == will_try)
171     LOG (GNUNET_ERROR_TYPE_DEBUG,
172          "Queue will be established!\n");
173   else
174     LOG (GNUNET_ERROR_TYPE_WARNING,
175          "Queue won't be established (bougus address?)!\n");
176 }
177
178
179 static struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *
180 handle_backchannel_cb (void *cls,
181                        struct GNUNET_MessageHeader *msg,
182                        struct GNUNET_PeerIdentity *pid)
183 {
184   struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h = cls;
185   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Handling BC message...\n");
186   if (0 == memcmp (&peer_id[PEER_A], pid, sizeof (*pid)))
187     return tc_hs[PEER_A];
188   else
189     return tc_hs[PEER_B];
190 }
191
192
193 static char*
194 make_payload (size_t payload_size)
195 {
196   struct GNUNET_TIME_Absolute ts;
197   struct GNUNET_TIME_AbsoluteNBO ts_n;
198   char *payload = GNUNET_malloc (payload_size);
199
200   GNUNET_assert (payload_size >= 8); // So that out timestamp fits
201   ts = GNUNET_TIME_absolute_get ();
202   ts_n = GNUNET_TIME_absolute_hton (ts);
203   memset (payload, 0, payload_size);
204   memcpy (payload, &ts_n, sizeof (struct GNUNET_TIME_AbsoluteNBO));
205   return payload;
206 }
207
208
209 static void
210 latency_timeout (void *cls)
211 {
212   to_task = NULL;
213   if (GNUNET_TIME_absolute_get_remaining (timeout).rel_value_us > 0)
214   {
215     to_task = GNUNET_SCHEDULER_add_at (timeout,
216                                        &latency_timeout,
217                                        NULL);
218     return;
219   }
220
221   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
222               "Latency too high. Test failed. (Phase: %d. Sent: %lu, Received: %lu)\n",
223               phase, num_sent, num_received);
224   ret = 2;
225   GNUNET_SCHEDULER_shutdown ();
226 }
227
228
229 static void
230 size_test (void *cls)
231 {
232   char *payload;
233
234   GNUNET_assert (TP_SIZE_CHECK == phase);
235   if (ack >= 64000)
236     return; /* Leave some room for our protocol, so not 2^16 exactly */
237   payload = make_payload (ack);
238   ack += 5;
239   num_sent++;
240   GNUNET_TRANSPORT_TESTING_transport_communicator_send (my_tc,
241                                                         (ack < 64000)
242                                                         ? &size_test
243                                                         : NULL,
244                                                         NULL,
245                                                         payload,
246                                                         ack);
247   GNUNET_free (payload);
248   timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS);
249 }
250
251
252 static void
253 long_test (void *cls)
254 {
255   char *payload;
256
257   payload = make_payload (LONG_MESSAGE_SIZE);
258   num_sent++;
259   GNUNET_TRANSPORT_TESTING_transport_communicator_send (my_tc,
260                                                         (BURST_PACKETS ==
261                                                          num_sent)
262                                                         ? NULL
263                                                         : &long_test,
264                                                         NULL,
265                                                         payload,
266                                                         LONG_MESSAGE_SIZE);
267   GNUNET_free (payload);
268   timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS);
269 }
270
271
272 static void
273 short_test (void *cls)
274 {
275   char *payload;
276
277   payload = make_payload (SHORT_MESSAGE_SIZE);
278   num_sent++;
279   GNUNET_TRANSPORT_TESTING_transport_communicator_send (my_tc,
280                                                         (BURST_PACKETS ==
281                                                          num_sent)
282                                                         ? NULL
283                                                         : &short_test,
284                                                         NULL,
285                                                         payload,
286                                                         SHORT_MESSAGE_SIZE);
287   GNUNET_free (payload);
288   timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS);
289 }
290
291
292 /**
293  * @brief Handle opening of queue
294  *
295  * Issues sending of test data
296  *
297  * Implements #GNUNET_TRANSPORT_TESTING_AddQueueCallback
298  *
299  * @param cls Closure
300  * @param tc_h Communicator handle
301  * @param tc_queue Handle to newly opened queue
302  */
303 static void
304 add_queue_cb (void *cls,
305               struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle *tc_h,
306               struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorQueue *
307               tc_queue)
308 {
309   if (0 != strcmp ((char*) cls, cfg_peers_name[0]))
310     return; // TODO?
311   LOG (GNUNET_ERROR_TYPE_DEBUG,
312        "Queue established, starting test...\n");
313   start_short = GNUNET_TIME_absolute_get ();
314   my_tc = tc_queue;
315   phase = TP_BURST_SHORT;
316   timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS);
317   GNUNET_assert (NULL == to_task);
318   to_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
319                                           &latency_timeout,
320                                           NULL);
321   short_test (NULL);
322 }
323
324
325 static void
326 update_avg_latency (const char*payload)
327 {
328   struct GNUNET_TIME_AbsoluteNBO *ts_n;
329   struct GNUNET_TIME_Absolute ts;
330   struct GNUNET_TIME_Relative latency;
331
332   ts_n = (struct GNUNET_TIME_AbsoluteNBO *) payload;
333   ts = GNUNET_TIME_absolute_ntoh (*ts_n);
334   latency = GNUNET_TIME_absolute_get_duration (ts);
335   if (1 >= num_received)
336     avg_latency = latency.rel_value_us;
337   else
338     avg_latency = ((avg_latency * (num_received - 1)) + latency.rel_value_us)
339                   / num_received;
340
341 }
342
343
344 /**
345  * @brief Handle an incoming message
346  *
347  * Implements #GNUNET_TRANSPORT_TESTING_IncomingMessageCallback
348
349  * @param cls Closure
350  * @param tc_h Handle to the receiving communicator
351  * @param msg Received message
352  */
353 static void
354 incoming_message_cb (void *cls,
355                      struct GNUNET_TRANSPORT_TESTING_TransportCommunicatorHandle
356                      *tc_h,
357                      const char*payload,
358                      size_t payload_len)
359 {
360   if (0 != strcmp ((char*) cls, cfg_peers_name[NUM_PEERS - 1]))
361   {
362     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
363                 "unexpected receiver...\n");
364     return;
365   }
366   /* Reset timeout */
367   timeout = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_SECONDS);
368   switch (phase)
369   {
370   case TP_BURST_SHORT:
371     {
372       GNUNET_assert (SHORT_MESSAGE_SIZE == payload_len);
373       num_received++;
374       duration = GNUNET_TIME_absolute_get_duration (start_short);
375       update_avg_latency (payload);
376       if (num_received == BURST_PACKETS)
377       {
378         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
379                     "Short size packet test done.\n");
380         char *goodput = GNUNET_STRINGS_byte_size_fancy ((SHORT_MESSAGE_SIZE
381                                                          * num_received * 1000
382                                                          * 1000)
383                                                         / duration.rel_value_us);
384         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
385                     "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n",
386                     (unsigned long) num_received,
387                     (unsigned long) num_sent,
388                     (unsigned long long) duration.rel_value_us,
389                     goodput,
390                     (unsigned long long) avg_latency);
391         GNUNET_free (goodput);
392         start_long = GNUNET_TIME_absolute_get ();
393         phase = TP_BURST_LONG;
394         num_sent = 0;
395         avg_latency = 0;
396         num_received = 0;
397         long_test (NULL);
398       }
399       break;
400     }
401   case TP_BURST_LONG:
402     {
403       if (LONG_MESSAGE_SIZE != payload_len)
404       {
405         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
406                     "Ignoring packet with wrong length\n");
407         return; // Ignore
408       }
409       num_received++;
410       duration = GNUNET_TIME_absolute_get_duration (start_long);
411       update_avg_latency (payload);
412       if (num_received == BURST_PACKETS)
413       {
414         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
415                     "Long size packet test done.\n");
416         char *goodput = GNUNET_STRINGS_byte_size_fancy ((LONG_MESSAGE_SIZE
417                                                          * num_received * 1000
418                                                          * 1000)
419                                                         / duration.rel_value_us);
420
421         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
422                     "%lu/%lu packets in %llu us (%s/s) -- avg latency: %llu us\n",
423                     (unsigned long) num_received,
424                     (unsigned long) num_sent,
425                     (unsigned long long) duration.rel_value_us,
426                     goodput,
427                     (unsigned long long) avg_latency);
428         GNUNET_free (goodput);
429         ack = 10;
430         phase = TP_SIZE_CHECK;
431         num_received = 0;
432         num_sent = 0;
433         avg_latency = 0;
434         size_test (NULL);
435       }
436       break;
437     }
438   case TP_SIZE_CHECK:
439     {
440       num_received++;
441       update_avg_latency (payload);
442       if (num_received >= (64000 - 10) / 5)
443       {
444         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
445                     "Size packet test done.\n");
446         GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
447                     "%lu/%lu packets -- avg latency: %llu us\n",
448                     (unsigned long) num_received,
449                     (unsigned long) num_sent,
450                     (unsigned long long) avg_latency);
451         num_received = 0;
452         num_sent = 0;
453         avg_latency = 0;
454         iterations_left--;
455         if (0 != iterations_left)
456         {
457           start_short = GNUNET_TIME_absolute_get ();
458           phase = TP_BURST_SHORT;
459           short_test (NULL);
460           break;
461         }
462         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
463                     "Finished\n");
464         GNUNET_SCHEDULER_shutdown ();
465       }
466       break;
467     }
468   }
469 }
470
471
472 static void
473 do_shutdown (void *cls)
474 {
475   if (NULL != to_task)
476   {
477     GNUNET_SCHEDULER_cancel (to_task);
478     to_task = NULL;
479   }
480   for (unsigned int i = 0; i < NUM_PEERS; i++)
481   {
482     GNUNET_TRANSPORT_TESTING_transport_communicator_service_stop (tc_hs[i]);
483   }
484 }
485
486
487 /**
488  * @brief Main function called by the scheduler
489  *
490  * @param cls Closure - Handle to configuration
491  */
492 static void
493 run (void *cls)
494 {
495   ret = 0;
496   num_received = 0;
497   num_sent = 0;
498   for (unsigned int i = 0; i < NUM_PEERS; i++)
499   {
500     tc_hs[i] = GNUNET_TRANSPORT_TESTING_transport_communicator_service_start (
501       "transport",
502       communicator_binary,
503       cfg_peers_name[i],
504       &communicator_available_cb,
505       &add_address_cb,
506       &queue_create_reply_cb,
507       &add_queue_cb,
508       &incoming_message_cb,
509       &handle_backchannel_cb,
510       cfg_peers_name[i]);   /* cls */
511   }
512   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
513                                  NULL);
514 }
515
516
517 int
518 main (int argc,
519       char *const *argv)
520 {
521   struct GNUNET_CRYPTO_EddsaPrivateKey *private_key;
522   char *communicator_name;
523   char *test_mode;
524   char *test_name;
525   char *cfg_peer;
526
527   ret = 1;
528   test_name = GNUNET_TESTING_get_testname_from_underscore (argv[0]);
529   communicator_name = strchr (test_name, '-');
530   communicator_name[0] = '\0';
531   communicator_name++;
532   test_mode = test_name;
533
534   GNUNET_asprintf (&communicator_binary,
535                    "gnunet-communicator-%s",
536                    communicator_name);
537   if (GNUNET_OK !=
538       GNUNET_log_setup ("test_communicator_basic",
539                         "DEBUG",
540                         NULL))
541   {
542     fprintf (stderr, "Unable to setup log\n");
543     GNUNET_break (0);
544     return 2;
545   }
546   for (unsigned int i = 0; i < NUM_PEERS; i++)
547   {
548     GNUNET_asprintf ((&cfg_peer),
549                      "test_communicator_%s_%s_peer%u.conf",
550                      communicator_name, test_mode, i + 1);
551     cfg_peers_name[i] = cfg_peer;
552     cfg_peers[i] = GNUNET_CONFIGURATION_create ();
553     if (GNUNET_YES ==
554         GNUNET_DISK_file_test (cfg_peers_name[i]))
555     {
556       if (GNUNET_SYSERR ==
557           GNUNET_CONFIGURATION_load (cfg_peers[i],
558                                      cfg_peers_name[i]))
559       {
560         fprintf (stderr,
561                  "Malformed configuration file `%s', exiting ...\n",
562                  cfg_peers_name[i]);
563         return 1;
564       }
565     }
566     else
567     {
568       if (GNUNET_SYSERR ==
569           GNUNET_CONFIGURATION_load (cfg_peers[i],
570                                      NULL))
571       {
572         fprintf (stderr,
573                  "Configuration file %s does not exist, exiting ...\n",
574                  cfg_peers_name[i]);
575         return 1;
576       }
577     }
578     private_key =
579       GNUNET_CRYPTO_eddsa_key_create_from_configuration (cfg_peers[i]);
580     if (NULL == private_key)
581     {
582       LOG (GNUNET_ERROR_TYPE_ERROR,
583            "Unable to get peer ID\n");
584       return 1;
585     }
586     GNUNET_CRYPTO_eddsa_key_get_public (private_key,
587                                         &peer_id[i].public_key);
588     GNUNET_free (private_key);
589     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
590                 "Identity of peer %u is %s\n",
591                 i,
592                 GNUNET_i2s_full (&peer_id[i]));
593   }
594   GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, "Starting test...\n");
595   GNUNET_SCHEDULER_run (&run,
596                         NULL);
597   return ret;
598 }