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