2 This file is part of GNUnet.
3 (C) 2008--2013 Christian Grothoff (and other contributing authors)
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 2, or (at your
8 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 General Public License for more details.
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., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file testbed/gnunet-service-testbed.c
23 * @brief implementation of the TESTBED service
24 * @author Sree Harsha Totakura
27 #include "gnunet-service-testbed.h"
30 * The event mask for the events we listen from sub-controllers
32 #define EVENT_MASK (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED)
35 * A list of directly linked neighbours
37 struct Slave **GST_slave_list;
40 * The size of directly linked neighbours list
42 unsigned int GST_slave_list_size;
47 static struct Route **route_list;
50 * The head for the LCF queue
52 static struct LCFContextQueue *lcfq_head;
55 * The tail for the LCF queue
57 static struct LCFContextQueue *lcfq_tail;
62 static GNUNET_SCHEDULER_TaskIdentifier lcf_proc_task_id;
65 * The size of the route list
67 static unsigned int route_list_size;
71 * Adds a slave to the slave array
73 * @param slave the slave controller to add
76 slave_list_add (struct Slave *slave)
78 if (slave->host_id >= GST_slave_list_size)
79 GST_array_grow_large_enough (GST_slave_list, GST_slave_list_size,
81 GNUNET_assert (NULL == GST_slave_list[slave->host_id]);
82 GST_slave_list[slave->host_id] = slave;
87 * Adds a route to the route list
89 * @param route the route to add
92 route_list_add (struct Route *route)
94 if (route->dest >= route_list_size)
95 GST_array_grow_large_enough (route_list, route_list_size, route->dest);
96 GNUNET_assert (NULL == route_list[route->dest]);
97 route_list[route->dest] = route;
102 * Cleans up the route list
105 GST_route_list_clear ()
109 for (id = 0; id < route_list_size; id++)
110 if (NULL != route_list[id])
111 GNUNET_free (route_list[id]);
112 GNUNET_free_non_null (route_list);
118 * Iterator for freeing hash map entries in a slave's reghost_map
120 * @param cls handle to the slave
121 * @param key current key code
122 * @param value value in the hash map
123 * @return GNUNET_YES if we should continue to
128 reghost_free_iterator (void *cls, const struct GNUNET_HashCode *key,
131 struct Slave *slave = cls;
132 struct RegisteredHostContext *rhc = value;
133 struct ForwardedOverlayConnectContext *focc;
135 GNUNET_assert (GNUNET_YES ==
136 GNUNET_CONTAINER_multihashmap_remove (slave->reghost_map, key,
138 while (NULL != (focc = rhc->focc_dll_head))
140 GNUNET_CONTAINER_DLL_remove (rhc->focc_dll_head, rhc->focc_dll_tail, focc);
141 GST_cleanup_focc (focc);
143 if (NULL != rhc->sub_op)
144 GNUNET_TESTBED_operation_done (rhc->sub_op);
145 if (NULL != rhc->client)
146 GNUNET_SERVER_client_drop (rhc->client);
153 * Cleans up the slave list
156 GST_slave_list_clear ()
159 struct HostRegistration *hr_entry;
161 for (id = 0; id < GST_slave_list_size; id++)
162 if (NULL != GST_slave_list[id])
164 while (NULL != (hr_entry = GST_slave_list[id]->hr_dll_head))
166 GNUNET_CONTAINER_DLL_remove (GST_slave_list[id]->hr_dll_head,
167 GST_slave_list[id]->hr_dll_tail, hr_entry);
168 GNUNET_free (hr_entry);
170 if (NULL != GST_slave_list[id]->rhandle)
171 GNUNET_TESTBED_cancel_registration (GST_slave_list[id]->rhandle);
173 GNUNET_CONTAINER_multihashmap_iterate (GST_slave_list
175 reghost_free_iterator,
177 GNUNET_CONTAINER_multihashmap_destroy (GST_slave_list[id]->reghost_map);
178 if (NULL != GST_slave_list[id]->controller)
179 GNUNET_TESTBED_controller_disconnect (GST_slave_list[id]->controller);
180 if (NULL != GST_slave_list[id]->controller_proc)
181 GNUNET_TESTBED_controller_stop (GST_slave_list[id]->controller_proc);
182 GNUNET_free (GST_slave_list[id]);
184 GNUNET_free_non_null (GST_slave_list);
185 GST_slave_list = NULL;
190 * Finds the route with directly connected host as destination through which
191 * the destination host can be reached
193 * @param host_id the id of the destination host
194 * @return the route with directly connected destination host; NULL if no route
198 GST_find_dest_route (uint32_t host_id)
202 if (route_list_size <= host_id)
204 while (NULL != (route = route_list[host_id]))
206 if (route->thru == GST_context->host_id)
208 host_id = route->thru;
215 * Function to send a failure reponse for controller link operation
217 * @param client the client to send the message to
218 * @param operation_id the operation ID of the controller link request
219 * @param cfg the configuration with which the delegated controller is started.
220 * Can be NULL if the delegated controller is not started but just
222 * @param emsg set to an error message explaining why the controller link
223 * failed. Setting this to NULL signifies success. !This should be
224 * NULL if cfg is set!
227 send_controller_link_response (struct GNUNET_SERVER_Client *client,
228 uint64_t operation_id,
229 const struct GNUNET_CONFIGURATION_Handle
233 struct GNUNET_TESTBED_ControllerLinkResponse *msg;
239 GNUNET_assert ((NULL == cfg) || (NULL == emsg));
243 msize = sizeof (struct GNUNET_TESTBED_ControllerLinkResponse);
246 xconfig = GNUNET_TESTBED_compress_cfg_ (cfg,
249 msize += xconfig_size;
252 msize += strlen (emsg);
253 msg = GNUNET_malloc (msize);
254 msg->header.type = htons
255 (GNUNET_MESSAGE_TYPE_TESTBED_LINK_CONTROLLERS_RESULT);
256 msg->header.size = htons (msize);
258 msg->success = htons (GNUNET_YES);
259 msg->operation_id = GNUNET_htonll (operation_id);
260 msg->config_size = htons ((uint16_t) config_size);
262 memcpy (&msg[1], xconfig, xconfig_size);
264 memcpy (&msg[1], emsg, strlen (emsg));
265 GST_queue_message (client, &msg->header);
270 * The Link Controller forwarding task
272 * @param cls the LCFContext
273 * @param tc the Task context from scheduler
276 lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
280 * Completion callback for host registrations while forwarding Link Controller messages
282 * @param cls the LCFContext
283 * @param emsg the error message; NULL if host registration is successful
286 lcf_proc_cc (void *cls, const char *emsg)
288 struct LCFContext *lcf = cls;
290 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id);
295 goto registration_error;
296 lcf->state = DELEGATED_HOST_REGISTERED;
297 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
299 case DELEGATED_HOST_REGISTERED:
301 goto registration_error;
302 lcf->state = SLAVE_HOST_REGISTERED;
303 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
306 GNUNET_assert (0); /* Shouldn't reach here */
311 LOG (GNUNET_ERROR_TYPE_WARNING, "Host registration failed with message: %s\n",
313 lcf->state = FINISHED;
314 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
319 * The Link Controller forwarding task
321 * @param cls the LCFContext
322 * @param tc the Task context from scheduler
325 lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
329 * Task to free resources when forwarded link controllers has been timedout
331 * @param cls the LCFContext
332 * @param tc the task context from scheduler
335 lcf_forwarded_operation_timeout (void *cls,
336 const struct GNUNET_SCHEDULER_TaskContext *tc)
338 struct LCFContext *lcf = cls;
340 lcf->timeout_task = GNUNET_SCHEDULER_NO_TASK;
341 // GST_forwarded_operation_timeout (lcf->fopc, tc);
342 LOG (GNUNET_ERROR_TYPE_WARNING,
343 "A forwarded controller link operation has timed out\n");
344 send_controller_link_response (lcf->client, lcf->operation_id, NULL,
345 "A forwarded controller link operation has "
347 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id);
348 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
353 * The Link Controller forwarding task
355 * @param cls the LCFContext
356 * @param tc the Task context from scheduler
359 lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
361 struct LCFContext *lcf = cls;
362 struct LCFContextQueue *lcfq;
364 lcf_proc_task_id = GNUNET_SCHEDULER_NO_TASK;
369 GNUNET_TESTBED_is_host_registered_ (GST_host_list
370 [lcf->delegated_host_id],
371 lcf->gateway->controller))
373 GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf,
374 GST_host_list[lcf->delegated_host_id]);
378 lcf->state = DELEGATED_HOST_REGISTERED;
379 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
382 case DELEGATED_HOST_REGISTERED:
384 GNUNET_TESTBED_is_host_registered_ (GST_host_list[lcf->slave_host_id],
385 lcf->gateway->controller))
387 GST_queue_host_registration (lcf->gateway, lcf_proc_cc, lcf,
388 GST_host_list[lcf->slave_host_id]);
392 lcf->state = SLAVE_HOST_REGISTERED;
393 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
396 case SLAVE_HOST_REGISTERED:
397 lcf->op = GNUNET_TESTBED_controller_link (lcf,
398 lcf->gateway->controller,
399 GST_host_list[lcf->delegated_host_id],
400 GST_host_list[lcf->slave_host_id],
402 lcf->is_subordinate);
404 GNUNET_SCHEDULER_add_delayed (GST_timeout, &lcf_forwarded_operation_timeout,
406 lcf->state = FINISHED;
410 GNUNET_assert (lcfq->lcf == lcf);
411 GNUNET_assert (NULL != lcf->cfg);
412 GNUNET_CONFIGURATION_destroy (lcf->cfg);
413 GNUNET_SERVER_client_drop (lcf->client);
414 GNUNET_TESTBED_operation_done (lcf->op);
416 GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq);
418 if (NULL != lcfq_head)
420 GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcfq_head->lcf);
426 * Callback for event from slave controllers
428 * @param cls struct Slave *
429 * @param event information about the event
432 slave_event_callback (void *cls,
433 const struct GNUNET_TESTBED_EventInformation *event)
435 struct RegisteredHostContext *rhc;
436 struct LCFContext *lcf;
437 struct GNUNET_CONFIGURATION_Handle *cfg;
438 struct GNUNET_TESTBED_Operation *old_op;
440 /* We currently only get here when working on RegisteredHostContexts and
442 GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type);
444 if (CLOSURE_TYPE_RHC == rhc->type)
446 GNUNET_assert (rhc->sub_op == event->op);
450 cfg = event->details.operation_finished.generic;
451 old_op = rhc->sub_op;
452 rhc->state = RHC_LINK;
454 GNUNET_TESTBED_controller_link (rhc, rhc->gateway->controller,
455 rhc->reg_host, rhc->host, cfg,
457 GNUNET_TESTBED_operation_done (old_op);
460 LOG_DEBUG ("OL: Linking controllers successfull\n");
461 GNUNET_TESTBED_operation_done (rhc->sub_op);
463 rhc->state = RHC_OL_CONNECT;
464 GST_process_next_focc (rhc);
472 if (CLOSURE_TYPE_LCF == lcf->type)
474 GNUNET_assert (lcf->op == event->op);
475 GNUNET_assert (FINISHED == lcf->state);
476 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != lcf->timeout_task);
477 GNUNET_SCHEDULER_cancel (lcf->timeout_task);
478 if (NULL == event->details.operation_finished.emsg)
479 send_controller_link_response (lcf->client, lcf->operation_id,
480 GNUNET_TESTBED_host_get_cfg_
481 (GST_host_list[lcf->delegated_host_id]),
484 send_controller_link_response (lcf->client, lcf->operation_id,
486 event->details.operation_finished.emsg);
487 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id);
488 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf);
496 * Callback to signal successfull startup of the controller process
498 * @param cls the handle to the slave whose status is to be found here
499 * @param cfg the configuration with which the controller has been started;
500 * NULL if status is not GNUNET_OK
501 * @param status GNUNET_OK if the startup is successfull; GNUNET_SYSERR if not,
502 * GNUNET_TESTBED_controller_stop() shouldn't be called in this case
505 slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg,
508 struct Slave *slave = cls;
509 struct LinkControllersContext *lcc;
512 if (GNUNET_SYSERR == status)
514 slave->controller_proc = NULL;
515 GST_slave_list[slave->host_id] = NULL;
518 LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected slave shutdown\n");
519 GNUNET_SCHEDULER_shutdown (); /* We too shutdown */
523 GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id],
524 EVENT_MASK, &slave_event_callback,
526 if (NULL != slave->controller)
528 send_controller_link_response (lcc->client, lcc->operation_id, cfg, NULL);
532 send_controller_link_response (lcc->client, lcc->operation_id, NULL,
533 "Could not connect to delegated controller");
534 GNUNET_TESTBED_controller_stop (slave->controller_proc);
535 GST_slave_list[slave->host_id] = NULL;
543 if (NULL != lcc->client)
545 GNUNET_SERVER_receive_done (lcc->client, GNUNET_OK);
546 GNUNET_SERVER_client_drop (lcc->client);
557 * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message
560 * @param client identification of the client
561 * @param message the actual message
564 GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client,
565 const struct GNUNET_MessageHeader *message)
567 const struct GNUNET_TESTBED_ControllerLinkRequest *msg;
568 struct GNUNET_CONFIGURATION_Handle *cfg;
569 struct LCFContextQueue *lcfq;
571 struct Route *new_route;
572 uint32_t delegated_host_id;
573 uint32_t slave_host_id;
576 if (NULL == GST_context)
579 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
582 msize = ntohs (message->size);
583 if (sizeof (struct GNUNET_TESTBED_ControllerLinkRequest) >= msize)
586 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
589 msg = (const struct GNUNET_TESTBED_ControllerLinkRequest *) message;
590 delegated_host_id = ntohl (msg->delegated_host_id);
591 if (delegated_host_id == GST_context->host_id)
594 LOG (GNUNET_ERROR_TYPE_WARNING, "Trying to link ourselves\n");
595 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
598 if ((delegated_host_id >= GST_host_list_size) ||
599 (NULL == GST_host_list[delegated_host_id]))
601 LOG (GNUNET_ERROR_TYPE_WARNING,
602 "Delegated host %u not registered with us\n", delegated_host_id);
603 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
606 slave_host_id = ntohl (msg->slave_host_id);
607 if ((slave_host_id >= GST_host_list_size) ||
608 (NULL == GST_host_list[slave_host_id]))
610 LOG (GNUNET_ERROR_TYPE_WARNING, "Slave host %u not registered with us\n",
612 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
615 if (slave_host_id == delegated_host_id)
617 LOG (GNUNET_ERROR_TYPE_WARNING, "Slave and delegated host are same\n");
618 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
621 cfg = GNUNET_TESTBED_extract_config_ (message); /* destroy cfg here or in lcfcontext */
624 GNUNET_break (0); /* Configuration parsing error */
625 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
628 if (slave_host_id == GST_context->host_id) /* Link from us */
631 struct LinkControllersContext *lcc;
633 if ((delegated_host_id < GST_slave_list_size) &&
634 (NULL != GST_slave_list[delegated_host_id]))
637 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
640 slave = GNUNET_malloc (sizeof (struct Slave));
641 slave->host_id = delegated_host_id;
642 slave->reghost_map = GNUNET_CONTAINER_multihashmap_create (100, GNUNET_NO);
643 slave_list_add (slave);
644 if (1 != msg->is_subordinate)
647 GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id],
648 EVENT_MASK, &slave_event_callback,
650 if (NULL != slave->controller)
651 send_controller_link_response (client,
652 GNUNET_ntohll (msg->operation_id),
656 send_controller_link_response (client,
657 GNUNET_ntohll (msg->operation_id),
659 "Could not connect to delegated controller");
660 GNUNET_SERVER_receive_done (client, GNUNET_OK);
663 lcc = GNUNET_malloc (sizeof (struct LinkControllersContext));
664 lcc->operation_id = GNUNET_ntohll (msg->operation_id);
665 GNUNET_SERVER_client_keep (client);
666 lcc->client = client;
668 slave->controller_proc =
669 GNUNET_TESTBED_controller_start (GST_context->master_ip,
670 GST_host_list[slave->host_id], cfg,
671 &slave_status_callback, slave);
672 GNUNET_CONFIGURATION_destroy (cfg);
673 new_route = GNUNET_malloc (sizeof (struct Route));
674 new_route->dest = delegated_host_id;
675 new_route->thru = GST_context->host_id;
676 route_list_add (new_route);
680 /* Route the request */
681 if (slave_host_id >= route_list_size)
683 LOG (GNUNET_ERROR_TYPE_WARNING, "No route towards slave host");
684 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
687 lcfq = GNUNET_malloc (sizeof (struct LCFContextQueue));
688 lcfq->lcf = GNUNET_malloc (sizeof (struct LCFContext));
689 lcfq->lcf->type = CLOSURE_TYPE_LCF;
690 lcfq->lcf->delegated_host_id = delegated_host_id;
691 lcfq->lcf->slave_host_id = slave_host_id;
692 route = GST_find_dest_route (slave_host_id);
693 GNUNET_assert (NULL != route); /* because we add routes carefully */
694 GNUNET_assert (route->dest < GST_slave_list_size);
695 GNUNET_assert (NULL != GST_slave_list[route->dest]);
696 lcfq->lcf->cfg = cfg;
697 lcfq->lcf->is_subordinate = msg->is_subordinate;
698 lcfq->lcf->state = INIT;
699 lcfq->lcf->operation_id = GNUNET_ntohll (msg->operation_id);
700 lcfq->lcf->gateway = GST_slave_list[route->dest];
701 GNUNET_SERVER_client_keep (client);
702 lcfq->lcf->client = client;
703 if (NULL == lcfq_head)
705 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id);
706 GNUNET_CONTAINER_DLL_insert_tail (lcfq_head, lcfq_tail, lcfq);
707 lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcfq->lcf);
710 GNUNET_CONTAINER_DLL_insert_tail (lcfq_head, lcfq_tail, lcfq);
711 /* FIXME: Adding a new route should happen after the controllers are linked
713 if (1 != msg->is_subordinate)
715 GNUNET_SERVER_receive_done (client, GNUNET_OK);
718 if ((delegated_host_id < route_list_size) &&
719 (NULL != route_list[delegated_host_id]))
721 GNUNET_break_op (0); /* Are you trying to link delegated host twice
722 * with is subordinate flag set to GNUNET_YES? */
723 GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
726 new_route = GNUNET_malloc (sizeof (struct Route));
727 new_route->dest = delegated_host_id;
728 new_route->thru = route->dest;
729 route_list_add (new_route);
730 GNUNET_SERVER_receive_done (client, GNUNET_OK);
735 * Cleans up the queue used for forwarding link controllers requests
740 struct LCFContextQueue *lcfq;
742 if (NULL != lcfq_head)
744 if (GNUNET_SCHEDULER_NO_TASK != lcf_proc_task_id)
746 GNUNET_SCHEDULER_cancel (lcf_proc_task_id);
747 lcf_proc_task_id = GNUNET_SCHEDULER_NO_TASK;
750 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id);
751 for (lcfq = lcfq_head; NULL != lcfq; lcfq = lcfq_head)
753 GNUNET_SERVER_client_drop (lcfq->lcf->client);
754 GNUNET_assert (NULL != lcfq->lcf->cfg);
755 GNUNET_CONFIGURATION_destroy (lcfq->lcf->cfg);
756 GNUNET_free (lcfq->lcf);
757 GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq);