Merge branch 'master' of gnunet.org:gnunet
[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 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 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 /**
52  * Testcase timeout (set aggressively as we know this test doesn't work right now)
53  */
54 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30)
55
56
57 static struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext *ccc;
58
59 static struct GNUNET_SCHEDULER_Task *measure_task;
60
61
62 /**
63  * Statistics we track per peer.
64  */
65 struct PeerStats
66 {
67   struct GNUNET_STATISTICS_Handle *stat;
68
69   unsigned int addresses_avail;
70
71   unsigned int switch_attempts;
72
73   unsigned int switch_success;
74
75   unsigned int switch_fail;
76 };
77
78 static struct PeerStats stats[2];
79
80 /* Amount of data transfered since last switch attempt */
81 static unsigned long long bytes_sent_after_switch;
82
83 static unsigned long long bytes_recv_after_switch;
84
85
86 static int
87 stat_start_attempt_cb (void *cls,
88                        const char *subsystem,
89                        const char *name,
90                        uint64_t value,
91                        int is_persistent)
92 {
93   struct PeerStats *stat = cls;
94
95   stat->switch_attempts++;
96   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch attempted (%p)", stat);
97   bytes_recv_after_switch = 0;
98   bytes_sent_after_switch = 0;
99
100   return GNUNET_OK;
101 }
102
103
104 static int
105 stat_success_attempt_cb (void *cls,
106                          const char *subsystem,
107                          const char *name,
108                          uint64_t value,
109                          int is_persistent)
110 {
111   struct PeerStats *stat = cls;
112
113   stat->switch_success++;
114   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Switch succeeded (%p)", stat);
115   return GNUNET_OK;
116 }
117
118
119 static int
120 stat_fail_attempt_cb (void *cls,
121                       const char *subsystem,
122                       const char *name,
123                       uint64_t value,
124                       int is_persistent)
125 {
126   struct PeerStats *stat = cls;
127
128   if (value == 0)
129     return GNUNET_OK;
130
131   stat->switch_fail++;
132   GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Switch failed (%p)", stat);
133   return GNUNET_OK;
134 }
135
136
137 static int
138 stat_addresses_available (void *cls,
139                           const char *subsystem,
140                           const char *name,
141                           uint64_t value,
142                           int is_persistent)
143 {
144   struct PeerStats *stat = cls;
145
146   stat->addresses_avail++;
147   return GNUNET_OK;
148 }
149
150
151 /**
152  * List of statistics entries we care about.
153  */
154 static struct WatchEntry
155 {
156   /**
157    * Name of the statistic we watch.
158    */
159   const char *stat_name;
160
161   /**
162    * Handler to register;
163    */
164   GNUNET_STATISTICS_Iterator stat_handler;
165 } watches[] =
166 { { "# Attempts to switch addresses", &stat_start_attempt_cb },
167   { "# Successful attempts to switch addresses", &stat_success_attempt_cb },
168   { "# Failed attempts to switch addresses (failed to send CONNECT CONT)",
169     &stat_fail_attempt_cb },
170   { "# Failed attempts to switch addresses (failed to send CONNECT)",
171     &stat_fail_attempt_cb },
172   { "# Failed attempts to switch addresses (no response)",
173     &stat_fail_attempt_cb },
174   { "# transport addresses", &stat_addresses_available },
175   { NULL, NULL } };
176
177
178 static void
179 custom_shutdown (void *cls)
180 {
181   int result;
182
183   if (NULL != measure_task)
184   {
185     GNUNET_SCHEDULER_cancel (measure_task);
186     measure_task = NULL;
187   }
188   if (0 == stats[0].switch_attempts + stats[1].switch_attempts)
189   {
190     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
191                 "Test did not work, as peers didn't switch (flawed testcase)!\n");
192     ccc->global_ret = 77;
193   }
194   else
195   {
196     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
197                 "Fail (timeout)! No transmission after switch! Stopping peers\n");
198     ccc->global_ret = 77;   /* GNUNET_SYSERR; */
199   }
200
201   /* stop statistics */
202   for (unsigned int i = 0; i < 2; i++)
203   {
204     if (NULL != stats[i].stat)
205     {
206       for (unsigned int j = 0; NULL != watches[j].stat_name; j++)
207         GNUNET_assert (GNUNET_OK ==
208                        GNUNET_STATISTICS_watch_cancel (stats[i].stat,
209                                                        "transport",
210                                                        watches[j].stat_name,
211                                                        watches[j].stat_handler,
212                                                        &stats[i]));
213       GNUNET_STATISTICS_destroy (stats[i].stat, GNUNET_NO);
214       stats[i].stat = NULL;
215     }
216   }
217
218   result = 0;
219   fprintf (stderr, "\n");
220   if (stats[0].switch_attempts > 0)
221   {
222     fprintf (
223       stderr,
224       "Peer 1 tried %u times to switch and succeeded %u times, failed %u times\n",
225       stats[0].switch_attempts,
226       stats[0].switch_success,
227       stats[0].switch_fail);
228     if (stats[0].switch_success != stats[0].switch_attempts)
229     {
230       GNUNET_break (0);
231       result++;
232     }
233   }
234   else if (stats[0].addresses_avail > 1)
235   {
236     fprintf (stderr,
237              "Peer 1 had %u addresses available, but did not try to switch\n",
238              stats[0].addresses_avail);
239   }
240   if (stats[1].switch_attempts > 0)
241   {
242     fprintf (
243       stderr,
244       "Peer 2 tried %u times to switch and succeeded %u times, failed %u times\n",
245       stats[1].switch_attempts,
246       stats[1].switch_success,
247       stats[1].switch_fail);
248     if (stats[1].switch_success != stats[1].switch_attempts)
249     {
250       GNUNET_break (0);
251       result++;
252     }
253   }
254   else if (stats[1].addresses_avail > 1)
255   {
256     fprintf (stderr,
257              "Peer 2 had %u addresses available, but did not try to switch\n",
258              stats[1].addresses_avail);
259   }
260
261   if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
262       (bytes_sent_after_switch == 0))
263   {
264     fprintf (stderr, "No data sent after switching!\n");
265     GNUNET_break (0);
266     result++;
267   }
268   if (((stats[0].switch_attempts > 0) || (stats[1].switch_attempts > 0)) &&
269       (bytes_recv_after_switch == 0))
270   {
271     fprintf (stderr, "No data received after switching!\n");
272     GNUNET_break (0);
273     result++;
274   }
275 #if 0
276   /* This test is not really expected to pass right now... */
277   if (0 != result)
278     ccc->global_ret = GNUNET_SYSERR;
279 #endif
280 }
281
282
283 static void
284 notify_receive (void *cls,
285                 struct GNUNET_TRANSPORT_TESTING_PeerContext *receiver,
286                 const struct GNUNET_PeerIdentity *sender,
287                 const struct GNUNET_TRANSPORT_TESTING_TestMessage *hdr)
288 {
289   if (GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE != ntohs (hdr->header.type))
290     return;
291
292   {
293     char *ps = GNUNET_strdup (GNUNET_i2s (&receiver->id));
294
295     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
296                 "Peer %u (`%s') got message %u of size %u from peer (`%s')\n",
297                 receiver->no,
298                 ps,
299                 (uint32_t) ntohl (hdr->num),
300                 ntohs (hdr->header.size),
301                 GNUNET_i2s (sender));
302     GNUNET_free (ps);
303   }
304   if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) &&
305       (stats[0].switch_attempts ==
306        stats[0].switch_fail + stats[0].switch_success) &&
307       (stats[1].switch_attempts ==
308        stats[1].switch_fail + stats[1].switch_success))
309   {
310     bytes_recv_after_switch += ntohs (hdr->header.size);
311     if ((bytes_sent_after_switch > 0) && (bytes_recv_after_switch > 0))
312     {
313       /* A peer switched addresses and sent and received data after the
314        * switch operations */
315       GNUNET_SCHEDULER_shutdown ();
316     }
317   }
318 }
319
320
321 static void
322 notify_send (void *cls)
323 {
324   static uint32_t cnt;
325
326   GNUNET_assert (
327     GNUNET_OK ==
328     GNUNET_TRANSPORT_TESTING_send (ccc->p[1],
329                                    ccc->p[0],
330                                    GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE,
331                                    GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE,
332                                    ++cnt,
333                                    &notify_send,
334                                    NULL));
335   if (((stats[0].switch_attempts >= 1) || (stats[1].switch_attempts >= 1)) &&
336       (stats[0].switch_attempts ==
337        stats[0].switch_fail + stats[0].switch_success) &&
338       (stats[1].switch_attempts ==
339        stats[1].switch_fail + stats[1].switch_success))
340   {
341     bytes_sent_after_switch += GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE;
342   }
343 }
344
345
346 static void
347 progress_indicator (void *cls)
348 {
349   static int counter;
350
351   measure_task = NULL;
352   counter++;
353   if ((TIMEOUT.rel_value_us / 1000 / 1000LL) < counter)
354   {
355     fprintf (stderr, "%s", ".\n");
356   }
357   else
358   {
359     fprintf (stderr, "%s", ".");
360     measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
361                                                  &progress_indicator,
362                                                  NULL);
363   }
364 }
365
366
367 static void
368 connected_cb (void *cls)
369 {
370   for (unsigned int i = 0; i < 2; i++)
371   {
372     stats[i].stat = GNUNET_STATISTICS_create ("transport", ccc->p[i]->cfg);
373     if (NULL == stats[i].stat)
374     {
375       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
376                   "Fail! Could not create statistics for peers!\n");
377       ccc->global_ret = GNUNET_SYSERR;
378       GNUNET_SCHEDULER_shutdown ();
379       return;
380     }
381     for (unsigned int j = 0; NULL != watches[j].stat_name; j++)
382     {
383       GNUNET_STATISTICS_watch (stats[i].stat,
384                                "transport",
385                                watches[j].stat_name,
386                                watches[j].stat_handler,
387                                &stats[i]);
388     }
389   }
390   /* Show progress */
391   ccc->global_ret = GNUNET_OK;
392   measure_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
393                                                &progress_indicator,
394                                                NULL);
395   /* Peers are connected, start transmit test messages */
396   GNUNET_assert (
397     GNUNET_OK ==
398     GNUNET_TRANSPORT_TESTING_send (ccc->p[1],
399                                    ccc->p[0],
400                                    GNUNET_TRANSPORT_TESTING_SIMPLE_MTYPE,
401                                    GNUNET_TRANSPORT_TESTING_LARGE_MESSAGE_SIZE,
402                                    0,
403                                    &notify_send,
404                                    NULL));
405 }
406
407
408 int
409 main (int argc, char *argv[])
410 {
411   struct GNUNET_TRANSPORT_TESTING_ConnectCheckContext my_ccc =
412   { .connect_continuation = &connected_cb,
413     .config_file = "test_transport_api_data.conf",
414     .rec = &notify_receive,
415     .nc = &GNUNET_TRANSPORT_TESTING_log_connect,
416     .shutdown_task = &custom_shutdown,
417     .timeout = TIMEOUT };
418
419   ccc = &my_ccc;
420   int ret;
421
422   ret = GNUNET_TRANSPORT_TESTING_main (2,
423                                        &GNUNET_TRANSPORT_TESTING_connect_check,
424                                        ccc);
425   if (77 == ret)
426     return 77;
427   if (GNUNET_OK != ret)
428     return 1;
429   return 0;
430 }
431 /* end of test_transport_address_switch.c */