2 This file is part of GNUnet.
3 Copyright (C) 2006, 2009, 2015, 2016 GNUnet e.V.
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.
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.
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/>.
19 * @file transport-testing.c
20 * @brief testing lib for transport service
21 * @author Matthias Wachs
22 * @author Christian Grothoff
24 #include "transport-testing.h"
27 #define LOG(kind,...) GNUNET_log_from(kind, "transport-testing", __VA_ARGS__)
30 static struct GNUNET_TRANSPORT_TESTING_PeerContext *
31 find_peer_context (struct GNUNET_TRANSPORT_TESTING_Handle *tth,
32 const struct GNUNET_PeerIdentity *peer)
34 struct GNUNET_TRANSPORT_TESTING_PeerContext *t;
36 for (t = tth->p_head; NULL != t; t = t->next)
37 if (0 == memcmp (&t->id,
39 sizeof (struct GNUNET_PeerIdentity)))
46 * Find any connecting context matching the given pair of peers.
48 * @param p1 first peer
49 * @param p2 second peer
50 * @param cb function to call
51 * @param cb_cls closure for @a cb
54 GNUNET_TRANSPORT_TESTING_find_connecting_context (struct GNUNET_TRANSPORT_TESTING_PeerContext *p1,
55 struct GNUNET_TRANSPORT_TESTING_PeerContext *p2,
56 GNUNET_TRANSPORT_TESTING_ConnectContextCallback cb,
59 struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth;
60 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
61 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
63 for (cc = tth->cc_head; NULL != cc; cc = ccn)
66 if ( (cc->p1 == p1) &&
76 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
82 cx->p1_c = GNUNET_YES;
88 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
90 struct GNUNET_MQ_Handle *mq = cls;
98 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
104 cx->p2_c = GNUNET_YES;
109 clear_p1c (void *cls,
110 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
116 cx->p1_c = GNUNET_NO;
121 clear_p2c (void *cls,
122 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cx)
128 cx->p2_c = GNUNET_NO;
133 notify_connect (void *cls,
134 const struct GNUNET_PeerIdentity *peer,
135 struct GNUNET_MQ_Handle *mq)
137 struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls;
138 struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
140 struct GNUNET_TRANSPORT_TESTING_PeerContext *p2;
141 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
142 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
146 p2 = find_peer_context (p->tth,
149 ret = p->nc (p->cb_cls,
156 GNUNET_asprintf (&p2_s,
159 GNUNET_i2s (&p2->id));
161 GNUNET_asprintf (&p2_s,
164 LOG (GNUNET_ERROR_TYPE_DEBUG,
165 "Peers %s connected to peer %u (`%s')\n",
168 GNUNET_i2s (&p->id));
170 /* update flags in connecting contexts */
172 GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
176 if (GNUNET_NO == found)
178 cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
181 cc->p1_c = GNUNET_YES;
182 GNUNET_CONTAINER_DLL_insert (tth->cc_head,
187 GNUNET_TRANSPORT_TESTING_find_connecting_context (p2,
191 if (GNUNET_NO == found)
193 cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
196 cc->p1_c = GNUNET_YES;
197 GNUNET_CONTAINER_DLL_insert (tth->cc_head,
201 GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
205 /* update set connected flag for all requests */
206 for (cc = tth->cc_head; NULL != cc; cc = cc->next)
208 if (GNUNET_YES == cc->connected)
210 if ( (GNUNET_YES == cc->p1_c) &&
211 (GNUNET_YES == cc->p2_c) )
213 cc->connected = GNUNET_YES;
214 /* stop trying to connect */
217 GNUNET_SCHEDULER_cancel (cc->tct);
222 GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
225 if (NULL != cc->ats_sh)
227 GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh);
232 /* then notify application */
233 for (cc = tth->cc_head; NULL != cc; cc = ccn)
236 if ( (GNUNET_YES == cc->connected) &&
240 cc->cb = NULL; /* only notify once! */
248 * Offer the current HELLO of P2 to P1.
250 * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest`
253 offer_hello (void *cls);
257 notify_disconnect (void *cls,
258 const struct GNUNET_PeerIdentity *peer,
261 struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cls;
262 struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
264 /* Find PeerContext */
266 struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = NULL;
267 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
269 p2 = find_peer_context (p->tth,
273 GNUNET_asprintf (&p2_s,
276 GNUNET_i2s (&p2->id));
278 GNUNET_asprintf (&p2_s,
281 LOG (GNUNET_ERROR_TYPE_DEBUG,
282 "Peers %s disconnected from peer %u (`%s')\n",
285 GNUNET_i2s (&p->id));
287 /* notify about disconnect */
294 /* clear MQ, it is now invalid */
295 GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
299 /* update set connected flags for all requests */
300 GNUNET_TRANSPORT_TESTING_find_connecting_context (p,
304 GNUNET_TRANSPORT_TESTING_find_connecting_context (p2,
308 /* resume connectivity requests as necessary */
309 for (cc = tth->cc_head; NULL != cc; cc = cc->next)
311 if (GNUNET_NO == cc->connected)
313 if ( (GNUNET_YES != cc->p1_c) ||
314 (GNUNET_YES != cc->p2_c) )
316 cc->connected = GNUNET_NO;
317 /* start trying to connect */
318 if ( (NULL == cc->tct) &&
320 cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello,
322 if (NULL == cc->ats_sh)
323 cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats,
332 get_hello (void *cb_cls,
333 const struct GNUNET_MessageHeader *message)
335 struct GNUNET_TRANSPORT_TESTING_PeerContext *p = cb_cls;
336 struct GNUNET_PeerIdentity hello_id;
338 GNUNET_assert (GNUNET_OK ==
339 GNUNET_HELLO_get_id ((const struct GNUNET_HELLO_Message *) message,
341 GNUNET_assert (0 == memcmp (&hello_id,
344 GNUNET_free_non_null (p->hello);
345 p->hello = (struct GNUNET_HELLO_Message *) GNUNET_copy_message (message);
347 if (NULL != p->start_cb)
349 LOG (GNUNET_ERROR_TYPE_DEBUG,
350 "Peer %u (`%s') successfully started\n",
352 GNUNET_i2s (&p->id));
353 p->start_cb (p->start_cb_cls);
360 * Start a peer with the given configuration
361 * @param tth the testing handle
362 * @param cfgname configuration file
363 * @param peer_id a unique number to identify the peer
364 * @param handlers functions for receiving messages
365 * @param nc connect callback
366 * @param nd disconnect callback
367 * @param cb_cls closure for callback
368 * @param start_cb start callback
369 * @param start_cb_cls closure for callback
370 * @return the peer context
372 struct GNUNET_TRANSPORT_TESTING_PeerContext *
373 GNUNET_TRANSPORT_TESTING_start_peer (struct GNUNET_TRANSPORT_TESTING_Handle *tth,
376 const struct GNUNET_MQ_MessageHandler *handlers,
377 GNUNET_TRANSPORT_NotifyConnecT nc,
378 GNUNET_TRANSPORT_NotifyDisconnecT nd,
380 GNUNET_SCHEDULER_TaskCallback start_cb,
384 struct GNUNET_TRANSPORT_TESTING_PeerContext *p;
385 struct GNUNET_PeerIdentity dummy;
388 if (GNUNET_NO == GNUNET_DISK_file_test (cfgname))
390 LOG (GNUNET_ERROR_TYPE_ERROR,
391 "File not found: `%s'\n",
396 p = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_PeerContext);
400 if (NULL != handlers)
402 for (i=0;NULL != handlers[i].cb;i++) ;
403 p->handlers = GNUNET_new_array (i + 1,
404 struct GNUNET_MQ_MessageHandler);
405 GNUNET_memcpy (p->handlers,
407 i * sizeof (struct GNUNET_MQ_MessageHandler));
413 p->start_cb = start_cb;
414 if (NULL != start_cb_cls)
415 p->start_cb_cls = start_cb_cls;
418 GNUNET_CONTAINER_DLL_insert (tth->p_head,
422 /* Create configuration and call testing lib to modify it */
423 p->cfg = GNUNET_CONFIGURATION_create ();
424 GNUNET_assert (GNUNET_OK ==
425 GNUNET_CONFIGURATION_load (p->cfg, cfgname));
427 GNUNET_TESTING_configuration_create (tth->tl_system,
430 LOG (GNUNET_ERROR_TYPE_ERROR,
431 "Testing library failed to create unique configuration based on `%s'\n",
433 GNUNET_CONFIGURATION_destroy (p->cfg);
439 /* Configure peer with configuration */
440 p->peer = GNUNET_TESTING_peer_configure (tth->tl_system,
447 LOG (GNUNET_ERROR_TYPE_ERROR,
448 "Testing library failed to create unique configuration based on `%s': `%s'\n",
451 GNUNET_TRANSPORT_TESTING_stop_peer (p);
452 GNUNET_free_non_null (emsg);
455 GNUNET_free_non_null (emsg);
456 if (GNUNET_OK != GNUNET_TESTING_peer_start (p->peer))
458 LOG (GNUNET_ERROR_TYPE_ERROR,
459 "Testing library failed to create unique configuration based on `%s'\n",
461 GNUNET_TRANSPORT_TESTING_stop_peer (p);
468 GNUNET_TESTING_peer_get_identity (p->peer,
470 if (0 == memcmp (&dummy,
472 sizeof (struct GNUNET_PeerIdentity)))
474 LOG (GNUNET_ERROR_TYPE_ERROR,
475 "Testing library failed to obtain peer identity for peer %u\n",
477 GNUNET_TRANSPORT_TESTING_stop_peer (p);
480 LOG (GNUNET_ERROR_TYPE_DEBUG,
481 "Peer %u configured with identity `%s'\n",
483 GNUNET_i2s_full (&p->id));
484 p->tmh = GNUNET_TRANSPORT_manipulation_connect (p->cfg);
485 p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
492 if ( (NULL == p->th) ||
495 LOG (GNUNET_ERROR_TYPE_ERROR,
496 "Failed to connect to transport service for peer `%s': `%s'\n",
499 GNUNET_TRANSPORT_TESTING_stop_peer (p);
502 p->ats = GNUNET_ATS_connectivity_init (p->cfg);
505 LOG (GNUNET_ERROR_TYPE_ERROR,
506 "Failed to connect to ATS service for peer `%s': `%s'\n",
509 GNUNET_TRANSPORT_TESTING_stop_peer (p);
512 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
513 GNUNET_TRANSPORT_AC_ANY,
516 GNUNET_assert (NULL != p->ghh);
522 * Stops and restarts the given peer, sleeping (!) for 5s in between.
525 * @param restart_cb callback to call when restarted
526 * @param restart_cb_cls callback closure
527 * @return #GNUNET_OK in success otherwise #GNUNET_SYSERR
530 GNUNET_TRANSPORT_TESTING_restart_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p,
531 GNUNET_SCHEDULER_TaskCallback restart_cb,
532 void *restart_cb_cls)
534 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
535 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
538 LOG (GNUNET_ERROR_TYPE_DEBUG,
539 "Stopping peer %u (`%s')\n",
541 GNUNET_i2s (&p->id));
544 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
549 GNUNET_TRANSPORT_core_disconnect (p->th);
554 GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
557 for (cc = p->tth->cc_head; NULL != cc; cc = ccn)
560 if ( (cc->p1 == p) ||
562 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
566 GNUNET_ATS_connectivity_done (p->ats);
570 GNUNET_TESTING_peer_stop (p->peer))
572 LOG (GNUNET_ERROR_TYPE_ERROR,
573 "Failed to stop peer %u (`%s')\n",
575 GNUNET_i2s (&p->id));
576 return GNUNET_SYSERR;
581 LOG (GNUNET_ERROR_TYPE_DEBUG,
582 "Restarting peer %u (`%s')\n",
584 GNUNET_i2s (&p->id));
586 if (GNUNET_SYSERR == GNUNET_TESTING_peer_start (p->peer))
588 LOG (GNUNET_ERROR_TYPE_ERROR,
589 "Failed to restart peer %u (`%s')\n",
591 GNUNET_i2s (&p->id));
592 return GNUNET_SYSERR;
595 GNUNET_assert (NULL == p->start_cb);
596 p->start_cb = restart_cb;
597 p->start_cb_cls = restart_cb_cls;
599 p->th = GNUNET_TRANSPORT_core_connect (p->cfg,
606 GNUNET_assert (NULL != p->th);
607 p->ats = GNUNET_ATS_connectivity_init (p->cfg);
608 p->ghh = GNUNET_TRANSPORT_hello_get (p->cfg,
609 GNUNET_TRANSPORT_AC_ANY,
612 GNUNET_assert (NULL != p->ghh);
618 * Shutdown the given peer
623 GNUNET_TRANSPORT_TESTING_stop_peer (struct GNUNET_TRANSPORT_TESTING_PeerContext *p)
625 struct GNUNET_TRANSPORT_TESTING_Handle *tth = p->tth;
626 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
627 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
629 for (cc = tth->cc_head; NULL != cc; cc = ccn)
632 if ( (cc->p1 == p) ||
634 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
638 GNUNET_TRANSPORT_hello_get_cancel (p->ghh);
643 GNUNET_TRANSPORT_manipulation_disconnect (p->tmh);
648 GNUNET_TRANSPORT_core_disconnect (p->th);
654 GNUNET_TESTING_peer_stop (p->peer))
656 LOG (GNUNET_ERROR_TYPE_DEBUG,
657 "Testing lib failed to stop peer %u (`%s')\n",
659 GNUNET_i2s (&p->id));
661 GNUNET_TESTING_peer_destroy (p->peer);
666 GNUNET_ATS_connectivity_done (p->ats);
669 if (NULL != p->hello)
671 GNUNET_free (p->hello);
676 GNUNET_CONFIGURATION_destroy (p->cfg);
679 if (NULL != p->handlers)
681 GNUNET_free (p->handlers);
684 GNUNET_CONTAINER_DLL_remove (tth->p_head,
687 LOG (GNUNET_ERROR_TYPE_DEBUG,
688 "Peer %u (`%s') stopped\n",
690 GNUNET_i2s (&p->id));
696 * Function called after the HELLO was passed to the
700 hello_offered (void *cls)
702 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
705 cc->tct = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
712 * Offer the current HELLO of P2 to P1.
714 * @param cls our `struct GNUNET_TRANSPORT_TESTING_ConnectRequest`
717 offer_hello (void *cls)
719 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc = cls;
720 struct GNUNET_TRANSPORT_TESTING_PeerContext *p1 = cc->p1;
721 struct GNUNET_TRANSPORT_TESTING_PeerContext *p2 = cc->p2;
725 char *p2_s = GNUNET_strdup (GNUNET_i2s (&p2->id));
727 LOG (GNUNET_ERROR_TYPE_DEBUG,
728 "Asking peer %u (`%s') to connect peer %u (`%s'), providing HELLO with %u bytes\n",
730 GNUNET_i2s (&p1->id),
733 GNUNET_HELLO_size (cc->p2->hello));
738 GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
740 GNUNET_TRANSPORT_offer_hello (cc->p1->cfg,
741 (const struct GNUNET_MessageHeader *) cc->p2->hello,
748 * Initiate a connection from p1 to p2 by offering p1 p2's HELLO message
750 * Remarks: start_peer's notify_connect callback can be called before.
752 * @param tth transport testing handle
755 * @param cb the callback to call when both peers notified that they are connected
756 * @param cls callback cls
757 * @return a connect request handle
759 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *
760 GNUNET_TRANSPORT_TESTING_connect_peers (struct GNUNET_TRANSPORT_TESTING_PeerContext *p1,
761 struct GNUNET_TRANSPORT_TESTING_PeerContext *p2,
762 GNUNET_SCHEDULER_TaskCallback cb,
765 struct GNUNET_TRANSPORT_TESTING_Handle *tth = p1->tth;
766 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
767 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ccn;
770 for (cc = tth->cc_head; NULL != cc; cc = cc->next)
772 if ( (cc->p1 == p1) &&
780 cc = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_ConnectRequest);
790 cc->p1_c = ccn->p1_c;
791 cc->p2_c = ccn->p2_c;
792 cc->connected = ccn->connected;
794 GNUNET_CONTAINER_DLL_insert (tth->cc_head,
797 cc->tct = GNUNET_SCHEDULER_add_now (&offer_hello,
799 cc->ats_sh = GNUNET_ATS_connectivity_suggest (cc->p1->ats,
802 LOG (GNUNET_ERROR_TYPE_DEBUG,
803 "New connect request %p\n",
810 * Cancel the request to connect two peers
811 * Tou MUST cancel the request if you stop the peers before the peers connected succesfully
813 * @param tth transport testing handle
814 * @param cc a connect request handle
817 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc)
819 struct GNUNET_TRANSPORT_TESTING_Handle *tth = cc->p1->tth;
821 LOG (GNUNET_ERROR_TYPE_DEBUG,
822 "Canceling connect request!\n");
825 GNUNET_SCHEDULER_cancel (cc->tct);
830 GNUNET_TRANSPORT_offer_hello_cancel (cc->oh);
833 if (NULL != cc->ats_sh)
835 GNUNET_ATS_connectivity_suggest_cancel (cc->ats_sh);
838 GNUNET_CONTAINER_DLL_remove (tth->cc_head,
846 * Clean up the transport testing
848 * @param tth transport testing handle
851 GNUNET_TRANSPORT_TESTING_done (struct GNUNET_TRANSPORT_TESTING_Handle *tth)
853 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *cc;
854 struct GNUNET_TRANSPORT_TESTING_ConnectRequest *ct;
855 struct GNUNET_TRANSPORT_TESTING_PeerContext *p;
856 struct GNUNET_TRANSPORT_TESTING_PeerContext *t;
864 LOG (GNUNET_ERROR_TYPE_ERROR,
865 "Developer forgot to cancel connect request!\n");
866 GNUNET_TRANSPORT_TESTING_connect_peers_cancel (cc);
873 LOG (GNUNET_ERROR_TYPE_ERROR,
874 "Developer forgot to stop peer!\n");
875 GNUNET_TRANSPORT_TESTING_stop_peer (p);
878 GNUNET_TESTING_system_destroy (tth->tl_system,
886 * Initialize the transport testing
888 * @return transport testing handle
890 struct GNUNET_TRANSPORT_TESTING_Handle *
891 GNUNET_TRANSPORT_TESTING_init ()
893 struct GNUNET_TRANSPORT_TESTING_Handle *tth;
895 tth = GNUNET_new (struct GNUNET_TRANSPORT_TESTING_Handle);
896 tth->tl_system = GNUNET_TESTING_system_create ("transport-testing",
900 if (NULL == tth->tl_system)
902 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
903 "Failed to initialize testing library!\n");
910 /* end of transport-testing.c */