7766ff875fb0edb5d4f5a79eac0a594b8e84265e
[oweals/gnunet.git] / src / multicast / test_multicast_multipeer.c
1 /*
2  * This file is part of GNUnet
3  * Copyright (C) 2013 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
19 /**
20  * @file multicast/test_multicast_multipeers.c
21  * @brief Tests for the Multicast API with multiple peers.
22  * @author xrs
23  */
24
25 #include <inttypes.h>
26
27 #include "platform.h"
28 #include "gnunet_crypto_lib.h"
29 #include "gnunet_common.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_testbed_service.h"
32 #include "gnunet_multicast_service.h"
33
34 #define PEERS_REQUESTED 12
35
36 struct MulticastPeerContext
37 {
38   int peer; /* peer number */
39   struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
40   const struct GNUNET_PeerIdentity *id;
41   struct GNUNET_TESTBED_Operation *op; /* not yet in use */
42   struct GNUNET_TESTBED_Operation *pi_op; /* not yet in use */
43   int test_ok;
44 };
45
46 enum pingpong
47 {
48   PING = 1,
49   PONG = 2
50 };
51
52 struct pingpong_msg
53 {
54   int peer;
55   enum pingpong msg;
56 };
57
58 static void service_connect (void *cls,
59                              struct GNUNET_TESTBED_Operation *op,
60                              void *ca_result,
61                              const char *emsg);
62
63 static struct MulticastPeerContext **multicast_peers;
64 static struct GNUNET_TESTBED_Peer **peers;
65
66 static struct GNUNET_TESTBED_Operation *op[PEERS_REQUESTED];
67 static struct GNUNET_TESTBED_Operation *pi_op[PEERS_REQUESTED];
68
69 static struct GNUNET_MULTICAST_Origin *origin;
70 static struct GNUNET_MULTICAST_Member *members[PEERS_REQUESTED]; /* first element always empty */
71
72 static struct GNUNET_SCHEDULER_Task *timeout_tid;
73
74 static struct GNUNET_CRYPTO_EddsaPrivateKey *group_key;
75 static struct GNUNET_CRYPTO_EddsaPublicKey group_pub_key;
76 static struct GNUNET_HashCode group_pub_key_hash;
77
78 /**
79  * Global result for testcase.
80  */
81 static int result;
82
83 /**
84  * Function run on CTRL-C or shutdown (i.e. success/timeout/etc.).
85  * Cleans up.
86  */
87 static void
88 shutdown_task (void *cls)
89 {
90   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
91               "shutdown_task!\n");
92   for (int i=0;i<PEERS_REQUESTED;i++)
93   {
94     if (NULL != op[i])
95     {
96       GNUNET_TESTBED_operation_done(op[i]);
97       op[i] = NULL;
98     }
99     if (NULL != pi_op[i])
100     {
101       GNUNET_TESTBED_operation_done (pi_op[i]);
102       pi_op[i] = NULL;
103     }
104   }
105
106   if (NULL != multicast_peers)
107   {
108     for (int i=0; i < PEERS_REQUESTED; i++)
109     {
110       GNUNET_free_non_null (multicast_peers[i]->key);
111       GNUNET_free (multicast_peers[i]);
112       multicast_peers[i] = NULL;
113     }
114     GNUNET_free (multicast_peers);
115     multicast_peers = NULL;
116   }
117
118   if (NULL != timeout_tid)
119   {
120     GNUNET_SCHEDULER_cancel (timeout_tid);
121     timeout_tid = NULL;
122   }
123 }
124
125
126 static void
127 timeout_task (void *cls)
128 {
129   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
130               "Timeout!\n");
131   result = GNUNET_SYSERR;
132   GNUNET_SCHEDULER_shutdown ();
133 }
134
135
136 static void
137 member_join_request (void *cls,
138                      const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
139                      const struct GNUNET_MessageHeader *join_msg,
140                      struct GNUNET_MULTICAST_JoinHandle *jh)
141 {
142   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
143   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
144               "Peer #%u (%s) sent a join request.\n",
145               mc_peer->peer,
146               GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
147 }
148
149
150 static int
151 notify (void *cls,
152         size_t *data_size,
153         void *data)
154 {
155   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
156
157   struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
158   pp_msg->peer = mc_peer->peer;
159   pp_msg->msg = PING;
160
161   *data_size = sizeof (struct pingpong_msg);
162   GNUNET_memcpy(data, pp_msg, *data_size);
163   GNUNET_free (pp_msg);
164
165   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
166               "Peer #%u sents ping to origin\n", mc_peer->peer);
167
168   return GNUNET_YES;
169 }
170
171
172 static void
173 member_join_decision (void *cls,
174                       int is_admitted,
175                       const struct GNUNET_PeerIdentity *peer,
176                       uint16_t relay_count,
177                       const struct GNUNET_PeerIdentity *relays,
178                       const struct GNUNET_MessageHeader *join_msg)
179 {
180   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
181
182   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
183               "Peer #%u (%s) received a decision from origin: %s\n",
184               mc_peer->peer,
185               GNUNET_i2s (multicast_peers[mc_peer->peer]->id),
186               (GNUNET_YES == is_admitted)?"accepted":"rejected");
187
188   if (GNUNET_YES == is_admitted)
189   {
190     GNUNET_MULTICAST_member_to_origin (members[mc_peer->peer],
191                                        0,
192                                        notify,
193                                        cls);
194
195   }
196 }
197
198
199 static void
200 member_replay_frag ()
201 {
202   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
203               "member replay frag...\n");
204 }
205
206
207 static void
208 member_replay_msg ()
209 {
210   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
211               "member replay msg...\n");
212 }
213
214
215 static void
216 origin_disconnected_cb (void *cls)
217 {
218   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
219               "Origin disconnected. Shutting down.\n");
220   result = GNUNET_YES;
221   GNUNET_SCHEDULER_shutdown ();
222 }
223
224
225 static void
226 member_disconnected_cb (void *cls)
227 {
228   for (int i = 1; i < PEERS_REQUESTED; ++i)
229     if (GNUNET_NO == multicast_peers[i]->test_ok)
230       return;
231   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
232               "All member disconnected. Stopping origin.\n");
233   GNUNET_MULTICAST_origin_stop (origin, origin_disconnected_cb, cls);
234 }
235
236
237 static void
238 member_message (void *cls,
239                 const struct GNUNET_MULTICAST_MessageHeader *msg)
240 {
241   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
242   struct pingpong_msg *pp_msg = (struct pingpong_msg*) &(msg[1]);
243
244   if (PONG == pp_msg->msg && mc_peer->peer == pp_msg->peer)
245   {
246     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
247                 "peer #%i (%s) receives a pong\n",
248                 mc_peer->peer,
249                 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
250     mc_peer->test_ok = GNUNET_OK;
251     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
252                 "peer #%u (%s) parting from multicast group\n",
253                 mc_peer->peer,
254                 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
255
256     GNUNET_MULTICAST_member_part (members[mc_peer->peer], member_disconnected_cb, cls);
257   }
258 }
259
260
261 static void
262 origin_join_request (void *cls,
263                  const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
264                  const struct GNUNET_MessageHeader *join_msg,
265                  struct GNUNET_MULTICAST_JoinHandle *jh)
266 {
267   struct GNUNET_MessageHeader *join_resp;
268
269   uint8_t data_size = ntohs (join_msg->size);
270
271   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
272               "origin got a join request...\n");
273   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
274               "origin receives: '%s'\n", (char *)&join_msg[1]);
275
276   char data[] = "Come in!";
277   data_size = strlen (data) + 1;
278   join_resp = GNUNET_malloc (sizeof (join_resp) + data_size);
279   join_resp->size = htons (sizeof (join_resp) + data_size);
280   join_resp->type = htons (123);
281   GNUNET_memcpy (&join_resp[1], data, data_size);
282
283   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
284               "origin sends: '%s'\n", data);
285
286   GNUNET_MULTICAST_join_decision (jh,
287                                   GNUNET_YES,
288                                   0,
289                                   NULL,
290                                   join_resp);
291
292   result = GNUNET_OK;
293 }
294
295
296 static void
297 origin_replay_frag (void *cls,
298                     const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
299                     uint64_t fragment_id,
300                     uint64_t flags,
301                     struct GNUNET_MULTICAST_ReplayHandle *rh)
302 {
303   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay fraq msg\n");
304 }
305
306
307 static void
308 origin_replay_msg (void *cls,
309                    const struct GNUNET_CRYPTO_EcdsaPublicKey *member_pub_key,
310                    uint64_t message_id,
311                    uint64_t fragment_offset,
312                    uint64_t flags,
313                    struct GNUNET_MULTICAST_ReplayHandle *rh)
314 {
315
316   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin replay msg\n");
317 }
318
319
320 static int
321 origin_notify (void *cls,
322                size_t *data_size,
323                void *data)
324 {
325   struct pingpong_msg *rcv_pp_msg = (struct pingpong_msg*)cls;
326   struct pingpong_msg *pp_msg = GNUNET_new (struct pingpong_msg);
327
328   pp_msg->peer = rcv_pp_msg->peer;
329   pp_msg->msg = PONG;
330   *data_size = sizeof (struct pingpong_msg);
331   GNUNET_memcpy(data, pp_msg, *data_size);
332   GNUNET_free (pp_msg);
333
334   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin sends pong\n");
335
336   return GNUNET_YES;
337 }
338
339
340 static void
341 origin_request (void *cls,
342                 const struct GNUNET_MULTICAST_RequestHeader *req)
343 {
344   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin receives a msg\n");
345
346   req++;
347   struct pingpong_msg *pp_msg = (struct pingpong_msg *) req;
348
349   if (1 != pp_msg->msg) {
350     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "origin didn't reveice a correct request");
351   }
352
353   GNUNET_MULTICAST_origin_to_all (origin,
354                                   0,
355                                   0,
356                                   origin_notify,
357                                   pp_msg);
358 }
359
360
361 static void
362 origin_message (void *cls,
363                 const struct GNUNET_MULTICAST_MessageHeader *msg)
364 {
365   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "origin message msg\n");
366 }
367
368
369 static void
370 multicast_disconnect (void *cls,
371                       void *op_result)
372 {
373
374 }
375
376
377 static void *
378 multicast_connect (void *cls,
379                    const struct GNUNET_CONFIGURATION_Handle *cfg)
380 {
381   struct MulticastPeerContext *multicast_peer = cls;
382   struct GNUNET_MessageHeader *join_msg;
383   char data[64];
384
385   if (0 == multicast_peer->peer)
386   {
387     group_key = GNUNET_CRYPTO_eddsa_key_create ();
388     GNUNET_CRYPTO_eddsa_key_get_public (group_key, &group_pub_key);
389
390     GNUNET_CRYPTO_hash (&group_pub_key, sizeof (group_pub_key), &group_pub_key_hash);
391     origin = GNUNET_MULTICAST_origin_start (cfg,
392                                             group_key,
393                                             0,
394                                             origin_join_request,
395                                             origin_replay_frag,
396                                             origin_replay_msg,
397                                             origin_request,
398                                             origin_message,
399                                             cls);
400     if (NULL == origin)
401     {
402       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
403                   "Peer #%u could not create a multicast group",
404                   multicast_peer->peer);
405       return NULL;
406     }
407     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
408                 "Peer #%u connected as origin to group %s\n",
409                 multicast_peer->peer,
410                 GNUNET_h2s (&group_pub_key_hash));
411     return origin;
412   }
413   else
414   {
415     multicast_peer->key = GNUNET_CRYPTO_ecdsa_key_create ();
416
417     sprintf(data, "Hi, I am peer #%u (%s). Can I enter?",
418             multicast_peer->peer,
419             GNUNET_i2s (multicast_peers[multicast_peer->peer]->id));
420     uint8_t data_size = strlen (data) + 1;
421     join_msg = GNUNET_malloc (sizeof (join_msg) + data_size);
422     join_msg->size = htons (sizeof (join_msg) + data_size);
423     join_msg->type = htons (123);
424     GNUNET_memcpy (&join_msg[1], data, data_size);
425
426     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
427                 "Peer #%u (%s) tries to join multicast group %s\n",
428                 multicast_peer->peer,
429                 GNUNET_i2s (multicast_peers[multicast_peer->peer]->id),
430                 GNUNET_h2s (&group_pub_key_hash));
431
432     members[multicast_peer->peer] =
433       GNUNET_MULTICAST_member_join (cfg,
434                                     &group_pub_key,
435                                     multicast_peer->key,
436                                     multicast_peers[0]->id,
437                                     0,
438                                     NULL,
439                                     join_msg, /* join message */
440                                     member_join_request,
441                                     member_join_decision,
442                                     member_replay_frag,
443                                     member_replay_msg,
444                                     member_message,
445                                     cls);
446     return members[multicast_peer->peer];
447   }
448 }
449
450
451 static void
452 peer_information_cb (void *cls,
453                      struct GNUNET_TESTBED_Operation *operation,
454                      const struct GNUNET_TESTBED_PeerInformation *pinfo,
455                      const char *emsg)
456 {
457   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
458
459   if (NULL == pinfo) {
460     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "got no peer information\n");
461     result = GNUNET_SYSERR;
462     GNUNET_SCHEDULER_shutdown ();
463   }
464
465   multicast_peers[mc_peer->peer]->id = pinfo->result.id;
466
467   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
468               "Got peer information of %s (%s)\n",
469               (0 == mc_peer->peer)? "origin" : "member",
470               GNUNET_i2s (pinfo->result.id));
471
472   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
473               "Create peer #%u (%s)\n",
474               mc_peer->peer,
475               GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
476
477   if (0 != mc_peer->peer)
478   {
479     /* connect to multicast service of members */
480     op[mc_peer->peer] =
481       GNUNET_TESTBED_service_connect (/* Closure for operation */
482                                       NULL,
483                                       /* The peer whose service to connect to */
484                                       peers[mc_peer->peer],
485                                       /* The name of the service */
486                                       "multicast",
487                                       /* called after a handle to service is opened */
488                                       service_connect,
489                                       /* closure for the above callback */
490                                       cls,
491                                       /* called when opening the service connection */
492                                       multicast_connect,
493                                       /* called when closing the service connection */
494                                       multicast_disconnect,
495                                       /* closure for the above two callbacks */
496                                       cls);
497   }
498 }
499
500
501 static void
502 service_connect (void *cls,
503                  struct GNUNET_TESTBED_Operation *op,
504                  void *ca_result,
505                  const char *emsg)
506 {
507   struct MulticastPeerContext *mc_peer = (struct MulticastPeerContext*)cls;
508
509   if (NULL == ca_result)
510   {
511     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
512                 "Connection adapter not created for peer #%u (%s)\n",
513                 mc_peer->peer,
514                 GNUNET_i2s (multicast_peers[mc_peer->peer]->id));
515
516     result = GNUNET_SYSERR;
517     GNUNET_SCHEDULER_shutdown();
518   }
519
520   if (0 == mc_peer->peer)
521   {
522     // Get GNUnet identity of members
523     for (int i = 0; i<PEERS_REQUESTED; i++)
524     {
525       pi_op[i] = GNUNET_TESTBED_peer_get_information (peers[i],
526                                                       GNUNET_TESTBED_PIT_IDENTITY,
527                                                       peer_information_cb,
528                                                       multicast_peers[i]);
529     }
530   }
531 }
532
533
534
535 /**
536  * Main function inovked from TESTBED once all of the
537  * peers are up and running.  This one then connects
538  * just to the multicast service of peer 0 and 1.
539  * Peer 0 is going to be origin.
540  * Peer 1 is going to be one member.
541  * Origin will start a multicast group and the member will try to join it.
542  * After that we execute some multicast test.
543  *
544  * @param cls closure
545  * @param h the run handle
546  * @param peers started peers for the test
547  * @param PEERS_REQUESTED size of the 'peers' array
548  * @param links_succeeded number of links between peers that were created
549  * @param links_failed number of links testbed was unable to establish
550  */
551 static void
552 testbed_master (void *cls,
553                 struct GNUNET_TESTBED_RunHandle *h,
554                 unsigned int num_peers,
555                 struct GNUNET_TESTBED_Peer **p,
556                 unsigned int links_succeeded,
557                 unsigned int links_failed)
558 {
559   /* Testbed is ready with peers running and connected in a pre-defined overlay
560      topology (FIXME)  */
561   peers = p;
562   multicast_peers = GNUNET_new_array (PEERS_REQUESTED, struct MulticastPeerContext*);
563
564   // Create test contexts for members
565   for (int i = 0; i<PEERS_REQUESTED; i++)
566   {
567     multicast_peers[i] = GNUNET_new (struct MulticastPeerContext);
568     multicast_peers[i]->peer = i;
569     multicast_peers[i]->test_ok = GNUNET_NO;
570   }
571   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
572               "Create origin peer\n");
573   op[0] =
574     GNUNET_TESTBED_service_connect (/* Closure for operation */
575                                     NULL,
576                                     /* The peer whose service to connect to */
577                                     peers[0],
578                                     /* The name of the service */
579                                     "multicast",
580                                     /* called after a handle to service is opened */
581                                     service_connect,
582                                     /* closure for the above callback */
583                                     multicast_peers[0],
584                                     /* called when opening the service connection */
585                                     multicast_connect,
586                                     /* called when closing the service connection */
587                                     multicast_disconnect,
588                                     /* closure for the above two callbacks */
589                                     multicast_peers[0]);
590   /* Schedule a new task on shutdown */
591   GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
592   /* Schedule the shutdown task with a delay of a few Seconds */
593   timeout_tid =
594     GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
595                                   (GNUNET_TIME_UNIT_SECONDS, 400),
596                                   &timeout_task,
597                                   NULL);
598 }
599
600
601 int
602 main (int argc, char *argv[])
603 {
604   int ret;
605   char const *config_file;
606
607   if (strstr (argv[0], "_line") != NULL)
608   {
609     config_file = "test_multicast_line.conf";
610   }
611   else if (strstr(argv[0], "_star") != NULL)
612   {
613     config_file = "test_multicast_star.conf";
614   }
615   else
616   {
617     config_file = "test_multicast_star.conf";
618   }
619
620   result = GNUNET_SYSERR;
621   ret =
622     GNUNET_TESTBED_test_run ("test-multicast-multipeer",
623                              config_file,
624                              /* number of peers to start */
625                              PEERS_REQUESTED,
626                              /* Event mask - set to 0 for no event notifications */
627                              0LL,
628                              /* Controller event callback */
629                              NULL,
630                              /* Closure for controller event callback */
631                              NULL,
632                              /* called when testbed setup is complete */
633                              testbed_master,
634                              /* Closure for the test_master callback */
635                              NULL);
636   if ( (GNUNET_OK != ret) || (GNUNET_OK != result) )
637     return 1;
638   return 0;
639 }
640
641 /* end of test_multicast_multipeer.c */