96e11b7de9a528c71de15e72b8552e32b6dd488b
[oweals/gnunet.git] / src / transport / test_transport_address_switch.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2009, 2010, 2011, 2016 GNUnet e.V.
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  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  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  Boston, MA 02110-1301, USA.
19  */
20 /**
21  * @file transport/test_transport_address_switch.c
22  * @brief base test case for transport implementations
23  *
24  * This test case tests if peers can successfully switch addresses when
25  * connected for plugins supporting multiple addresses by monitoring transport's
26  * statistic values.
27  *
28  * This test starts 2 peers and connects them. When connected test messages
29  * are transmitted from peer 2 to peer 1. The test monitors transport's
30  * statistics values for information about address switch attempts.
31  *
32  * The test passes with success if one of the peers could successfully switch
33  * addresses in connected state and a test message was successfully transmitted
34  * after this switch.
35  *
36  * Since it is not possible to trigger an address switch from outside,
37  * the test returns "77" (skipped) when no address switching attempt
38  * takes place. It fails if an address switch attempt fails.
39  *
40  * NOTE: The test seems largely useless right now, as we simply NEVER
41  * switch addresses under the test conditions.  However, it may be a
42  * good starting point for a future test.  For now, it always times
43  * out and returns "77" (skipped), so we set the timeout suitably low.
44  */
45 #include "platform.h"
46 #include "gnunet_transport_service.h"
47 #include "gnunet_ats_service.h"
48 #include "transport-testing.h"
49
50
51 GNUNET_NETWORK_STRUCT_BEGIN
52 struct TestMessage
53 {
54   struct GNUNET_MessageHeader header;
55   uint32_t num GNUNET_PACKED;
56 };
57 GNUNET_NETWORK_STRUCT_END
58
59 /**
60  * Message type for test messages
61  */
62 #define MTYPE 12345
63
64 /**
65  * Message size for test messages
66  */
67 #define MSIZE 2048
68
69 /**
70  * Testcase timeout
71  */
72 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
73
74 /**
75  * How long until we give up on transmitting the message?
76  */
77 #define TIMEOUT_TRANSMIT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
78
79
80 static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
81
82 static struct GNUNET_SCHEDULER_Task *measure_task;
83
84 static struct GNUNET_TRANSPORT_TransmitHandle *th;
85
86 /**
87  * Statistics we track per peer.
88  */
89 struct PeerStats
90 {
91   struct GNUNET_STATISTICS_Handle *stat;
92
93   unsigned int addresses_avail;
94
95   unsigned int switch_attempts;
96
97   unsigned int switch_success;
98
99   unsigned int switch_fail;
100 };
101
102 static struct PeerStats stats[2];
103
104 /* Amount of data transfered since last switch attempt */
105 static unsigned long long bytes_sent_after_switch;
106
107 static unsigned long long bytes_recv_after_switch;
108
109
110 static int
111 stat_start_attempt_cb (void *cls,
112                        const char *subsystem,
113                        const char *name,
114                        uint64_t value,
115                        int is_persistent)
116 {
117   struct PeerStats *stat = cls;
118
119   stat->switch_attempts++;
120   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
121               "Switch attempted (%p)",
122               stat);
123   bytes_recv_after_switch = 0;
124   bytes_sent_after_switch = 0;
125
126   return GNUNET_OK;
127 }
128
129
130 static int
131 stat_success_attempt_cb (void *cls,
132                          const char *subsystem,
133                          const char *name,
134                          uint64_t value,
135                          int is_persistent)
136 {
137   struct PeerStats *stat = cls;
138
139   stat->switch_success++;
140   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
141               "Switch succeeded (%p)",
142               stat);
143   return GNUNET_OK;
144 }
145
146
147 static int
148 stat_fail_attempt_cb (void *cls,
149                       const char *subsystem,
150                       const char *name,
151                       uint64_t value,
152                       int is_persistent)
153 {
154   struct PeerStats *stat = cls;
155
156   if (value == 0)
157     return GNUNET_OK;
158
159   stat->switch_fail++;
160   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
161               "Switch failed (%p)",
162               stat);
163   return GNUNET_OK;
164 }
165
166
167 static int
168 stat_addresses_available (void *cls,
169                           const char *subsystem,
170                           const char *name,
171                           uint64_t value,
172                           int is_persistent)
173 {
174   struct PeerStats *stat = cls;
175
176   stat->addresses_avail++;
177   return GNUNET_OK;
178 }
179
180
181 /**
182  * List of statistics entries we care about.
183  */
184 static struct WatchEntry {
185
186   /**
187    * Name of the statistic we watch.
188    */
189   const char *stat_name;
190
191   /**
192    * Handler to register;
193    */
194   GNUNET_STATISTICS_Iterator stat_handler;
195 } watches[] = {
196   { "# Attempts to switch addresses", &stat_start_attempt_cb },
197   { "# Successful attempts to switch addresses", &stat_success_attempt_cb },
198   { "# Failed attempts to switch addresses (failed to send CONNECT CONT)", &stat_fail_attempt_cb },
199   { "# Failed attempts to switch addresses (failed to send CONNECT)", &stat_fail_attempt_cb },
200   { "# Failed attempts to switch addresses (no response)", &stat_fail_attempt_cb },
201   { "# transport addresses", &stat_addresses_available },
202   { NULL, NULL }
203 };
204
205
206 static void
207 custom_shutdown (void *cls)
208 {
209   int result;
210
211   if (NULL != measure_task)
212   {
213     GNUNET_SCHEDULER_cancel (measure_task);
214     measure_task = NULL;
215   }
216   if (0 == stats[0].switch_attempts + stats[1].switch_attempts)
217   {
218     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
219                 "Test did not work, as peers didn't switch (flawed testcase)!\n");
220     ccc->global_ret = 77;
221   }
222   else
223   {
224     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
225                 "Fail (timeout)! No transmission after switch! Stopping peers\n");
226     ccc->global_ret = GNUNET_SYSERR;
227   }
228
229   /* stop statistics */
230   for (unsigned int i=0;i<2;i++)
231   {
232     if (NULL != stats[i].stat)
233     {
234       for (unsigned int j=0;NULL != watches[j].stat_name; j++)
235         GNUNET_STATISTICS_watch_cancel (stats[i].stat,
236                                         "transport",
237                                         watches[j].stat_name,
238                                         watches[j].stat_handler,
239                                         &stats[i]);
240
241       GNUNET_STATISTICS_destroy (stats[i].stat,
242                                  GNUNET_NO);
243       stats[i].stat = NULL;
244     }
245   }
246   if (NULL != th)
247   {
248     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
249     th = NULL;
250   }
251
252   result = 0;
253   FPRINTF (stderr, "\n");
254   if (stats[0].switch_attempts > 0)
255   {
256     FPRINTF (stderr,
257              "Peer 1 tried %u times to switch and succeeded %u times, failed %u times\n",
258              stats[0].switch_attempts,
259              stats[0].switch_success,
260              stats[0].switch_fail);
261     if (stats[0].switch_success != stats[0].switch_attempts)
262     {
263       GNUNET_break (0);
264       result ++;
265     }
266   }
267   else if (stats[0].addresses_avail > 1)
268   {
269     FPRINTF (stderr,
270              "Peer 1 had %u addresses available, but did not try to switch\n",
271              stats[0].addresses_avail);
272   }
273   if (stats[1].switch_attempts > 0)
274   {
275     FPRINTF (stderr,
276              "Peer 2 tried %u times to switch and succeeded %u times, failed %u times\n",
277              stats[1].switch_attempts,
278              stats[1].switch_success,
279              stats[1].switch_fail);
280     if (stats[1].switch_success != stats[1].switch_attempts)
281     {
282       GNUNET_break (0);
283       result++;
284     }
285   }
286   else if (stats[1].addresses_avail > 1)
287   {
288     FPRINTF (stderr,
289              "Peer 2 had %u addresses available, but did not try to switch\n",
290              stats[1].addresses_avail);
291   }
292
293   if ( ((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
294        (bytes_sent_after_switch == 0) )
295   {
296     FPRINTF (stderr, "No data sent after switching!\n");
297     GNUNET_break (0);
298     result++;
299   }
300   if ( ((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
301        (bytes_recv_after_switch == 0) )
302   {
303     FPRINTF (stderr, "No data received after switching!\n");
304     GNUNET_break (0);
305     result++;
306   }
307   if (0 != result)
308     ccc->global_ret = GNUNET_SYSERR;
309 }
310
311
312 static void
313 notify_receive (void *cls,
314                 struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver,
315                 const struct GNUNET_PeerIdentity *sender,
316                 const struct GNUNET_MessageHeader *message)
317 {
318   const struct TestMessage *hdr;
319
320   hdr = (const struct TestMessage *) message;
321   if (MTYPE != ntohs (message->type))
322     return;
323
324   {
325     char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id));
326
327     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
328                 "Peer %u (`%s') got message %u of size %u from peer (`%s')\n",
329                 receiver->no,
330                 ps,
331                 ntohl (hdr->num),
332                 ntohs (message->size),
333                 GNUNET_i2s (sender));
334     GNUNET_free (ps);
335   }
336   if ( ((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) &&
337         (stats[0].switch_attempts == stats[0].switch_fail + stats[0].switch_success) &&
338         (stats[1].switch_attempts == stats[1].switch_fail + stats[1].switch_success) )
339   {
340     bytes_recv_after_switch += ntohs(hdr->header.size);
341     if ((bytes_sent_after_switch > 0) && (bytes_recv_after_switch > 0))
342     {
343       /* A peer switched addresses and sent and received data after the
344        * switch operations */
345       GNUNET_SCHEDULER_shutdown ();
346     }
347   }
348 }
349
350
351 static size_t
352 notify_ready (void *cls, size_t size, void *buf)
353 {
354   static uint32_t counter;
355   char *cbuf = buf;
356   struct TestMessage hdr;
357
358   th = NULL;
359   if (buf == NULL)
360   {
361     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362                "Timeout occurred while waiting for transmit_ready for message\n");
363     ccc->global_ret = GNUNET_SYSERR;
364     GNUNET_SCHEDULER_shutdown ();
365     return 0;
366   }
367
368   GNUNET_assert(size >= MSIZE);
369   GNUNET_assert(buf != NULL);
370   cbuf = buf;
371
372   hdr.header.size = htons (MSIZE);
373   hdr.header.type = htons (MTYPE);
374   hdr.num = htonl (counter++);
375   GNUNET_memcpy (&cbuf[0], &hdr, sizeof(struct TestMessage));
376   memset (&cbuf[sizeof(struct TestMessage)], '0', MSIZE - sizeof(struct TestMessage));
377
378   {
379     char *receiver_s = GNUNET_strdup (GNUNET_i2s (&ccc->p[0]->id));
380
381     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
382                 "Sending message %u of size %u from peer %u (`%4s') -> peer %u (`%s') !\n",
383                 (unsigned int) (counter - 1),
384                 MSIZE,
385                 ccc->p[1]->no,
386                 GNUNET_i2s (&ccc->p[1]->id),
387                 ccc->p[0]->no,
388                 receiver_s);
389     GNUNET_free(receiver_s);
390   }
391
392   if (th == NULL)
393     th = GNUNET_TRANSPORT_notify_transmit_ready (ccc->p[1]->th,
394                                                  &ccc->p[0]->id,
395                                                  MSIZE,
396                                                  TIMEOUT_TRANSMIT,
397                                                  &notify_ready,
398                                                  NULL);
399
400   if ( ( (stats[0].switch_attempts >= 1) ||
401          (stats[1].switch_attempts >= 1) ) &&
402        (stats[0].switch_attempts == stats[0].switch_fail + stats[0].switch_success) &&
403        (stats[1].switch_attempts == stats[1].switch_fail + stats[1].switch_success) )
404   {
405     bytes_sent_after_switch += MSIZE;
406   }
407   return MSIZE;
408 }
409
410
411 static void
412 notify_disconnect (void *cls,
413                    struct GNUNET_TRANSPORT_TESTING_PeerContext *me,
414                    const struct GNUNET_PeerIdentity *other)
415 {
416   if (NULL != th)
417   {
418     GNUNET_TRANSPORT_notify_transmit_ready_cancel (th);
419     th = NULL;
420   }
421 }
422
423
424 static void
425 progress_indicator (void *cls)
426 {
427   static int counter;
428
429   measure_task = NULL;
430   counter++;
431   if ((TIMEOUT.rel_value_us / 1000 / 1000LL) < counter)
432   {
433     FPRINTF (stderr, "%s", ".\n");
434   }
435   else
436   {
437     FPRINTF (stderr, "%s", ".");
438     measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
439                                                  &progress_indicator,
440                                                  NULL);
441   }
442 }
443
444
445 static void
446 connected_cb (void *cls)
447 {
448   for (unsigned int i=0;i<2;i++)
449   {
450     stats[i].stat = GNUNET_STATISTICS_create ("transport",
451                                               ccc->p[i]->cfg);
452     if (NULL == stats[i].stat)
453     {
454       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
455                   "Fail! Could not create statistics for peers!\n");
456       ccc->global_ret = GNUNET_SYSERR;
457       GNUNET_SCHEDULER_shutdown ();
458       return;
459     }
460     for (unsigned int j=0;NULL != watches[j].stat_name; j++)
461     {
462       GNUNET_STATISTICS_watch (stats[i].stat,
463                                "transport",
464                                watches[j].stat_name,
465                                watches[j].stat_handler,
466                                &stats[i]);
467     }
468   }
469   /* Show progress */
470   ccc->global_ret = GNUNET_OK;
471   measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
472                                                &progress_indicator,
473                                                NULL);
474   /* Peers are connected, start transmit test messages */
475   th = GNUNET_TRANSPORT_notify_transmit_ready (ccc->p[1]->th,
476                                                &ccc->p[0]->id, MSIZE,
477                                                TIMEOUT_TRANSMIT,
478                                                &notify_ready, NULL);
479
480 }
481
482
483 int
484 main (int argc,
485       char *argv[])
486 {
487   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc = {
488     .connect_continuation = &connected_cb,
489     .config_file = "test_transport_startonly.conf",
490     .rec = &notify_receive,
491     .nc = &GNUNET_TRANSPORT_TESTING_log_connect,
492     .nd = &notify_disconnect,
493     .shutdown_task = &custom_shutdown,
494     .timeout = TIMEOUT
495   };
496   ccc = &my_ccc;
497   int ret;
498
499   ret = GNUNET_TRANSPORT_TESTING_main (2,
500                                        &GNUNET_TRANSPORT_TESTING_connect_check,
501                                        ccc);
502   if (77 == ret)
503     return 77;
504   if (GNUNET_OK != ret)
505     return 1;
506   return 0;
507 }
508 /* end of test_transport_address_switch.c */