X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Ftestbed%2Fgnunet-service-testbed_links.c;h=766c47471342f36919a906324d521bfc23cd8e59;hb=27c12911f4f2aba2d90099270d70de846e83854f;hp=6f71d8a611ee7c0a5a3d2daa7f864f9c8d74966b;hpb=dd2b8d87339226bb3e9a04f7111561740328c40e;p=oweals%2Fgnunet.git diff --git a/src/testbed/gnunet-service-testbed_links.c b/src/testbed/gnunet-service-testbed_links.c index 6f71d8a61..766c47471 100644 --- a/src/testbed/gnunet-service-testbed_links.c +++ b/src/testbed/gnunet-service-testbed_links.c @@ -4,7 +4,7 @@ GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -19,18 +19,276 @@ */ /** - * @file testbed/gnunet-service-testbed.c - * @brief implementation of the TESTBED service + * @file testbed/gnunet-service-testbed_links.c + * @brief TESTBED service components that deals with starting slave controllers + * and establishing lateral links between controllers * @author Sree Harsha Totakura */ #include "gnunet-service-testbed.h" +/** + * Redefine LOG with a changed log component string + */ +#ifdef LOG +#undef LOG +#endif +#define LOG(kind,...) \ + GNUNET_log_from (kind, "testbed-links", __VA_ARGS__) + /** * The event mask for the events we listen from sub-controllers */ #define EVENT_MASK (1LL << GNUNET_TESTBED_ET_OPERATION_FINISHED) + +/** + * States of LCFContext + */ +enum LCFContextState +{ + /** + * The Context has been initialized; Nothing has been done on it + */ + INIT, + + /** + * Delegated host has been registered at the forwarding controller + */ + DELEGATED_HOST_REGISTERED, + + /** + * The slave host has been registred at the forwarding controller + */ + SLAVE_HOST_REGISTERED, + + /** + * The context has been finished (may have error) + */ + FINISHED +}; + + +/** + * Link controllers request forwarding context + */ +struct LCFContext +{ + /** + * The gateway which will pass the link message to delegated host + */ + struct Slave *gateway; + + /** + * The client which has asked to perform this operation + */ + struct GNUNET_SERVER_Client *client; + + /** + * Handle for operations which are forwarded while linking controllers + */ + struct GNUNET_TESTBED_Operation *op; + + /** + * The timeout task + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * The id of the operation which created this context + */ + uint64_t operation_id; + + /** + * should the slave controller start the delegated controller? + */ + int is_subordinate; + + /** + * The state of this context + */ + enum LCFContextState state; + + /** + * The delegated host + */ + uint32_t delegated_host_id; + + /** + * The slave host + */ + uint32_t slave_host_id; + +}; + + +/** + * Structure of a queue entry in LCFContext request queue + */ +struct LCFContextQueue +{ + /** + * The LCFContext + */ + struct LCFContext *lcf; + + /** + * Head prt for DLL + */ + struct LCFContextQueue *next; + + /** + * Tail ptr for DLL + */ + struct LCFContextQueue *prev; +}; + + +/** + * Notification context to be used to notify when connection to the neighbour's + * controller is opened + */ +struct NeighbourConnectNotification +{ + /** + * DLL next for inclusion in neighbour's list of notification requests + */ + struct NeighbourConnectNotification *next; + + /** + * DLL prev + */ + struct NeighbourConnectNotification *prev; + + /** + * The neighbour + */ + struct Neighbour *n; + + /** + * The notification callback to call when we are connect to neighbour + */ + GST_NeigbourConnectNotifyCallback cb; + + /** + * The closure for the above callback + */ + void *cb_cls; +}; + + +/** + * A connected controller which is not our child + */ +struct Neighbour +{ + /** + * The controller handle + */ + struct GNUNET_TESTBED_Controller *controller; + + /** + * Operation handle for opening a lateral connection to another controller. + * Will be NULL if the slave controller is started by this controller + */ + struct GNUNET_TESTBED_Operation *conn_op; + + /** + * DLL head for the list of notification requests + */ + struct NeighbourConnectNotification *nl_head; + + /** + * DLL tail for the list of notification requests + */ + struct NeighbourConnectNotification *nl_tail; + + /** + * Task id for the task to call notifications from the notification list + */ + GNUNET_SCHEDULER_TaskIdentifier notify_task; + + /** + * How many references are present currently to this neighbour's connection + */ + unsigned int reference_cnt; + + /** + * Is the conn_op inactivated? + */ + unsigned int inactive; + + /** + * The id of the host this controller is running on + */ + uint32_t host_id; +}; + + +/** + * The neighbour list + */ +static struct Neighbour **neighbour_list; + +/** + * The size of the neighbour list + */ +static unsigned int neighbour_list_size; + + +/** + * Context information for establishing a link to neighbour (Used is + * GST_handle_link_controllers() + */ +struct NeighbourConnectCtxt +{ + /** + * DLL next for inclusion in the corresponding context list + */ + struct NeighbourConnectCtxt *next; + + /** + * DLL tail + */ + struct NeighbourConnectCtxt *prev; + + /** + * The neighbour to whom connection should be made + */ + struct Neighbour *n; + + /** + * The client requesting the connection + */ + struct GNUNET_SERVER_Client *client; + + /** + * Task to be run upon timeout + */ + GNUNET_SCHEDULER_TaskIdentifier timeout_task; + + /** + * The notification handle associated with the neighbour's connection request + */ + struct NeighbourConnectNotification *nh; + + /** + * The id of the link-controllers operation responsible for creating this + * context + */ + uint64_t op_id; +}; + +/** + * DLL head for the list of neighbour connect contexts + */ +struct NeighbourConnectCtxt *ncc_head; + +/** + * DLL tail for the list of neighbour connect contexts + */ +struct NeighbourConnectCtxt *ncc_tail; + /** * A list of directly linked neighbours */ @@ -98,6 +356,22 @@ route_list_add (struct Route *route) } +/** + * Add a neighbour to the neighbour list. Grows the neighbour list + * automatically. + * + * @param n the neighbour to add + */ +static void +neighbour_list_add (struct Neighbour *n) +{ + if (n->host_id >= neighbour_list_size) + GST_array_grow_large_enough (neighbour_list, neighbour_list_size, n->host_id); + GNUNET_assert (NULL == neighbour_list[n->host_id]); + neighbour_list[n->host_id] = n; +} + + /** * Cleans up the route list */ @@ -105,7 +379,7 @@ void GST_route_list_clear () { unsigned int id; - + for (id = 0; id < route_list_size; id++) if (NULL != route_list[id]) GNUNET_free (route_list[id]); @@ -140,47 +414,85 @@ reghost_free_iterator (void *cls, const struct GNUNET_HashCode *key, GNUNET_CONTAINER_DLL_remove (rhc->focc_dll_head, rhc->focc_dll_tail, focc); GST_cleanup_focc (focc); } - if (NULL != rhc->sub_op) - GNUNET_TESTBED_operation_done (rhc->sub_op); - if (NULL != rhc->client) - GNUNET_SERVER_client_drop (rhc->client); GNUNET_free (value); return GNUNET_YES; } +/** + * Kill a #Slave object + * + * @param slave the #Slave object + */ +static void +kill_slave (struct Slave *slave) +{ + struct HostRegistration *hr_entry; + + while (NULL != (hr_entry = slave->hr_dll_head)) + { + GNUNET_CONTAINER_DLL_remove (slave->hr_dll_head, slave->hr_dll_tail, + hr_entry); + GNUNET_free (hr_entry); + } + if (NULL != slave->rhandle) + GNUNET_TESTBED_cancel_registration (slave->rhandle); + GNUNET_assert (GNUNET_SYSERR != + GNUNET_CONTAINER_multihashmap_iterate (slave->reghost_map, + reghost_free_iterator, + slave)); + GNUNET_CONTAINER_multihashmap_destroy (slave->reghost_map); + if (NULL != slave->controller) + GNUNET_TESTBED_controller_disconnect (slave->controller); + if (NULL != slave->controller_proc) + { + LOG_DEBUG ("Stopping a slave\n"); + GNUNET_TESTBED_controller_kill_ (slave->controller_proc); + } +} + + +/** + * Destroy a #Slave object + * + * @param slave the #Slave object + */ +static void +destroy_slave (struct Slave *slave) +{ + if (NULL != slave->controller_proc) + { + GNUNET_TESTBED_controller_destroy_ (slave->controller_proc); + LOG_DEBUG ("Slave stopped\n"); + } + GST_slave_list[slave->host_id] = NULL; + GNUNET_free (slave); +} + + /** * Cleans up the slave list */ void GST_slave_list_clear () { + struct Slave *slave; unsigned int id; - struct HostRegistration *hr_entry; for (id = 0; id < GST_slave_list_size; id++) - if (NULL != GST_slave_list[id]) - { - while (NULL != (hr_entry = GST_slave_list[id]->hr_dll_head)) - { - GNUNET_CONTAINER_DLL_remove (GST_slave_list[id]->hr_dll_head, - GST_slave_list[id]->hr_dll_tail, hr_entry); - GNUNET_free (hr_entry); - } - if (NULL != GST_slave_list[id]->rhandle) - GNUNET_TESTBED_cancel_registration (GST_slave_list[id]->rhandle); - (void) - GNUNET_CONTAINER_multihashmap_iterate (GST_slave_list - [id]->reghost_map, - reghost_free_iterator, - GST_slave_list[id]); - GNUNET_CONTAINER_multihashmap_destroy (GST_slave_list[id]->reghost_map); - if (NULL != GST_slave_list[id]->controller) - GNUNET_TESTBED_controller_disconnect (GST_slave_list[id]->controller); - if (NULL != GST_slave_list[id]->controller_proc) - GNUNET_TESTBED_controller_stop (GST_slave_list[id]->controller_proc); - GNUNET_free (GST_slave_list[id]); - } + { + slave = GST_slave_list[id]; + if (NULL == slave) + continue; + kill_slave (slave); + } + for (id = 0; id < GST_slave_list_size; id++) + { + slave = GST_slave_list[id]; + if (NULL == slave) + continue; + destroy_slave (slave); + } GNUNET_free_non_null (GST_slave_list); GST_slave_list = NULL; } @@ -233,7 +545,7 @@ send_controller_link_response (struct GNUNET_SERVER_Client *client, struct GNUNET_TESTBED_ControllerLinkResponse *msg; char *xconfig; size_t config_size; - size_t xconfig_size; + size_t xconfig_size; uint16_t msize; GNUNET_assert ((NULL == cfg) || (NULL == emsg)); @@ -259,7 +571,10 @@ send_controller_link_response (struct GNUNET_SERVER_Client *client, msg->operation_id = GNUNET_htonll (operation_id); msg->config_size = htons ((uint16_t) config_size); if (NULL != xconfig) + { memcpy (&msg[1], xconfig, xconfig_size); + GNUNET_free (xconfig); + } if (NULL != emsg) memcpy (&msg[1], emsg, strlen (emsg)); GST_queue_message (client, &msg->header); @@ -398,7 +713,6 @@ lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) lcf->gateway->controller, GST_host_list[lcf->delegated_host_id], GST_host_list[lcf->slave_host_id], - NULL, lcf->is_subordinate); lcf->timeout_task = GNUNET_SCHEDULER_add_delayed (GST_timeout, &lcf_forwarded_operation_timeout, @@ -408,10 +722,9 @@ lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) case FINISHED: lcfq = lcfq_head; GNUNET_assert (lcfq->lcf == lcf); - GNUNET_assert (NULL != lcf->cfg); - GNUNET_CONFIGURATION_destroy (lcf->cfg); GNUNET_SERVER_client_drop (lcf->client); - GNUNET_TESTBED_operation_done (lcf->op); + if (NULL != lcf->op) + GNUNET_TESTBED_operation_done (lcf->op); GNUNET_free (lcf); GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); GNUNET_free (lcfq); @@ -425,70 +738,35 @@ lcf_proc_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) /** * Callback for event from slave controllers * - * @param cls struct Slave * + * @param cls NULL * @param event information about the event */ static void -slave_event_callback (void *cls, - const struct GNUNET_TESTBED_EventInformation *event) +slave_event_cb (void *cls, const struct GNUNET_TESTBED_EventInformation *event) { - struct RegisteredHostContext *rhc; struct LCFContext *lcf; - struct GNUNET_CONFIGURATION_Handle *cfg; - struct GNUNET_TESTBED_Operation *old_op; - /* We currently only get here when working on RegisteredHostContexts and - LCFContexts */ + /* We currently only get here when working on LCFContexts */ GNUNET_assert (GNUNET_TESTBED_ET_OPERATION_FINISHED == event->type); - rhc = event->op_cls; - if (CLOSURE_TYPE_RHC == rhc->type) - { - GNUNET_assert (rhc->sub_op == event->op); - switch (rhc->state) - { - case RHC_GET_CFG: - cfg = event->details.operation_finished.generic; - old_op = rhc->sub_op; - rhc->state = RHC_LINK; - rhc->sub_op = - GNUNET_TESTBED_controller_link (rhc, rhc->gateway->controller, - rhc->reg_host, rhc->host, cfg, - GNUNET_NO); - GNUNET_TESTBED_operation_done (old_op); - break; - case RHC_LINK: - LOG_DEBUG ("OL: Linking controllers successfull\n"); - GNUNET_TESTBED_operation_done (rhc->sub_op); - rhc->sub_op = NULL; - rhc->state = RHC_OL_CONNECT; - GST_process_next_focc (rhc); - break; - default: - GNUNET_assert (0); - } - return; - } lcf = event->op_cls; - if (CLOSURE_TYPE_LCF == lcf->type) - { - GNUNET_assert (lcf->op == event->op); - GNUNET_assert (FINISHED == lcf->state); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != lcf->timeout_task); - GNUNET_SCHEDULER_cancel (lcf->timeout_task); - if (NULL == event->details.operation_finished.emsg) - send_controller_link_response (lcf->client, lcf->operation_id, - GNUNET_TESTBED_host_get_cfg_ - (GST_host_list[lcf->delegated_host_id]), - NULL); - else - send_controller_link_response (lcf->client, lcf->operation_id, - NULL, - event->details.operation_finished.emsg); - GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); - lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); - return; - } - GNUNET_assert (0); + GNUNET_assert (lcf->op == event->op); + GNUNET_TESTBED_operation_done (lcf->op); + lcf->op = NULL; + GNUNET_assert (FINISHED == lcf->state); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != lcf->timeout_task); + GNUNET_SCHEDULER_cancel (lcf->timeout_task); + if (NULL == event->details.operation_finished.emsg) + send_controller_link_response (lcf->client, lcf->operation_id, + GNUNET_TESTBED_host_get_cfg_ + (GST_host_list[lcf->delegated_host_id]), + NULL); + else + send_controller_link_response (lcf->client, lcf->operation_id, + NULL, + event->details.operation_finished.emsg); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); + lcf_proc_task_id = GNUNET_SCHEDULER_add_now (&lcf_proc_task, lcf); + return; } @@ -502,8 +780,8 @@ slave_event_callback (void *cls, * GNUNET_TESTBED_controller_stop() shouldn't be called in this case */ static void -slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, - int status) +slave_status_cb (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, + int status) { struct Slave *slave = cls; struct LinkControllersContext *lcc; @@ -512,16 +790,21 @@ slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, if (GNUNET_SYSERR == status) { slave->controller_proc = NULL; - GST_slave_list[slave->host_id] = NULL; - GNUNET_free (slave); + /* Stop all link controller forwarding tasks since we shutdown here anyway + and as these tasks they depend on the operation queues which are created + through GNUNET_TESTBED_controller_connect() and in kill_slave() we call + the destructor function GNUNET_TESTBED_controller_disconnect() */ + GST_free_lcfq (); + kill_slave (slave); + destroy_slave (slave); slave = NULL; LOG (GNUNET_ERROR_TYPE_WARNING, "Unexpected slave shutdown\n"); GNUNET_SCHEDULER_shutdown (); /* We too shutdown */ goto clean_lcc; } slave->controller = - GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id], - EVENT_MASK, &slave_event_callback, + GNUNET_TESTBED_controller_connect (GST_host_list[slave->host_id], + EVENT_MASK, &slave_event_cb, slave); if (NULL != slave->controller) { @@ -531,13 +814,12 @@ slave_status_callback (void *cls, const struct GNUNET_CONFIGURATION_Handle *cfg, { send_controller_link_response (lcc->client, lcc->operation_id, NULL, "Could not connect to delegated controller"); - GNUNET_TESTBED_controller_stop (slave->controller_proc); - GST_slave_list[slave->host_id] = NULL; - GNUNET_free (slave); + kill_slave (slave); + destroy_slave (slave); slave = NULL; } -clean_lcc: + clean_lcc: if (NULL != lcc) { if (NULL != lcc->client) @@ -553,6 +835,341 @@ clean_lcc: } +/** + * Trigger notification task if there are notification requests currently + * waiting in the given neighbour. Also activates the neighbour connect operation + * if it was previously inactivated so that the connection to the neighbour can + * be re-used + * + * @param n the neighbour + */ +static void +trigger_notifications (struct Neighbour *n); + + +/** + * Task to call the notification queued in the notifications list of the given + * neighbour + * + * @param cls the neighbour + * @param tc scheduler task context + */ +static void +neighbour_connect_notify_task (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct Neighbour *n = cls; + struct NeighbourConnectNotification *h; + + GNUNET_assert (NULL != (h = n->nl_head)); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != n->notify_task); + n->notify_task = GNUNET_SCHEDULER_NO_TASK; + GNUNET_assert (NULL != n->controller); + GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h); + trigger_notifications (n); + h->cb (h->cb_cls, n->controller); + GNUNET_free (h); +} + + +/** + * Trigger notification task if there are notification requests currently + * waiting in the given neighbour. Also activates the neighbour connect operation + * if it was previously inactivated so that the connection to the neighbour can + * be re-used + * + * @param n the neighbour + */ +static void +trigger_notifications (struct Neighbour *n) +{ + GNUNET_assert (NULL != n->conn_op); + if (NULL == n->nl_head) + return; + if (NULL == n->controller) + return; + if (GNUNET_SCHEDULER_NO_TASK != n->notify_task) + return; + if (1 == n->inactive) + { + GNUNET_assert (0 == n->reference_cnt); + GNUNET_TESTBED_operation_activate_ (n->conn_op); + n->inactive = 0; + } + n->reference_cnt++; + n->notify_task = + GNUNET_SCHEDULER_add_now (&neighbour_connect_notify_task, n); +} + + +/** + * Callback to be called when the neighbour connect operation is started. The + * connection to the neigbour is opened here and any pending notifications are + * trigger. + * + * @param cls the neighbour + */ +static void +opstart_neighbour_conn (void *cls) +{ + struct Neighbour *n = cls; + + GNUNET_assert (NULL != n->conn_op); + GNUNET_assert (NULL == n->controller); + LOG_DEBUG ("Opening connection to controller on host %u\n", n->host_id); + n->controller = GNUNET_TESTBED_controller_connect (GST_host_list[n->host_id], + EVENT_MASK, + &slave_event_cb, + NULL); + trigger_notifications (n); +} + + +/** + * Callback to be called when the neighbour connect operation is released + * + * @param cls the neighbour + */ +static void +oprelease_neighbour_conn (void *cls) +{ + struct Neighbour *n = cls; + + GNUNET_assert (0 == n->reference_cnt); + GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == n->notify_task); + GNUNET_assert (NULL == n->nl_head); + if (NULL != n->controller) + { + LOG_DEBUG ("Closing connection to controller on host %u\n", n->host_id); + GNUNET_TESTBED_controller_disconnect (n->controller); + n->controller = NULL; + } + n->conn_op = NULL; + n->inactive = 0; +} + + +/** + * Try to open a connection to the given neigbour. If the connection is open + * already, then it is re-used. If not, the request is queued in the operation + * queues responsible for bounding the total number of file descriptors. The + * actual connection will happen when the operation queue marks the + * corresponding operation as active. + * + * @param n the neighbour to open a connection to + * @param cb the notification callback to call when the connection is opened + * @param cb_cls the closure for the above callback + */ +struct NeighbourConnectNotification * +GST_neighbour_get_connection (struct Neighbour *n, + GST_NeigbourConnectNotifyCallback cb, + void *cb_cls) +{ + struct NeighbourConnectNotification *h; + + GNUNET_assert (NULL != cb); + LOG_DEBUG ("Attempting to get connection to controller on host %u\n", + n->host_id); + h = GNUNET_malloc (sizeof (struct NeighbourConnectNotification)); + h->n = n; + h->cb = cb; + h->cb_cls = cb_cls; + GNUNET_CONTAINER_DLL_insert_tail (n->nl_head, n->nl_tail, h); + if (NULL == n->conn_op) + { + GNUNET_assert (NULL == n->controller); + n->conn_op = GNUNET_TESTBED_operation_create_ (n, &opstart_neighbour_conn, + &oprelease_neighbour_conn); + GNUNET_TESTBED_operation_queue_insert_ (GST_opq_openfds, n->conn_op); + GNUNET_TESTBED_operation_begin_wait_ (n->conn_op); + return h; + } + trigger_notifications (n); + return h; +} + + +/** + * Cancel the request for opening a connection to the neighbour + * + * @param h the notification handle + */ +void +GST_neighbour_get_connection_cancel (struct NeighbourConnectNotification *h) +{ + struct Neighbour *n; + int cleanup_task; + + n = h->n; + cleanup_task = (h == n->nl_head) ? GNUNET_YES : GNUNET_NO; + GNUNET_CONTAINER_DLL_remove (n->nl_head, n->nl_tail, h); + GNUNET_free (h); + if (GNUNET_NO == cleanup_task) + return; + if (GNUNET_SCHEDULER_NO_TASK == n->notify_task) + return; + GNUNET_assert (0 < n->reference_cnt); + n->reference_cnt--; + GNUNET_SCHEDULER_cancel (n->notify_task); + n->notify_task = GNUNET_SCHEDULER_NO_TASK; + if (NULL == n->nl_head) + { + if ( (0 == n->reference_cnt) && (0 == n->inactive) ) + { + n->inactive = 1; + GNUNET_TESTBED_operation_inactivate_ (n->conn_op); + } + return; + } + trigger_notifications (n); +} + + +/** + * Release the connection to the neighbour. The actual connection will be + * closed if connections to other neighbour are waiting (to maintain a bound on + * the total number of connections that are open). + * + * @param n the neighbour whose connection can be closed + */ +void +GST_neighbour_release_connection (struct Neighbour *n) +{ + GNUNET_assert (0 == n->inactive); + GNUNET_assert (0 < n->reference_cnt); + n->reference_cnt--; + if (0 == n->reference_cnt) + { + n->inactive = 1; + GNUNET_TESTBED_operation_inactivate_ (n->conn_op); + } +} + + +/** + * Cleanup neighbour connect contexts + * + * @param ncc the neighbour connect context to cleanup + */ +static void +cleanup_ncc (struct NeighbourConnectCtxt *ncc) +{ + if (NULL != ncc->nh) + GST_neighbour_get_connection_cancel (ncc->nh); + if (GNUNET_SCHEDULER_NO_TASK != ncc->timeout_task) + GNUNET_SCHEDULER_cancel (ncc->timeout_task); + GNUNET_SERVER_client_drop (ncc->client); + GNUNET_CONTAINER_DLL_remove (ncc_head, ncc_tail, ncc); + GNUNET_free (ncc); +} + + +/** + * Cleans up the neighbour list + */ +void +GST_neighbour_list_clean() +{ + struct Neighbour *n; + unsigned int id; + + for (id = 0; id < neighbour_list_size; id++) + { + if (NULL == (n = neighbour_list[id])) + continue; + if (NULL != n->conn_op) + GNUNET_TESTBED_operation_release_ (n->conn_op); + GNUNET_free (n); + neighbour_list[id] = NULL; + } + GNUNET_free_non_null (neighbour_list); +} + + +/** + * Get a neighbour from the neighbour list + * + * @param id the index of the neighbour in the neighbour list + * @return the Neighbour; NULL if the given index in invalid (index greater than + * the list size or neighbour at that index is NULL) + */ +struct Neighbour * +GST_get_neighbour (uint32_t id) +{ + if (neighbour_list_size <= id) + return NULL; + else + return neighbour_list[id]; +} + + +/** + * Function to cleanup the neighbour connect contexts + */ +void +GST_free_nccq () +{ + while (NULL != ncc_head) + cleanup_ncc (ncc_head); +} + + +/** + * Task to be run upon timeout while attempting to connect to the neighbour + * + * @param cls the NeighbourConnectCtxt created in GST_handle_link_controllers() + * @param tc the scheduler task context + */ +static void +timeout_neighbour_connect (void *cls, + const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct NeighbourConnectCtxt *ncc = cls; + + ncc->timeout_task = GNUNET_SCHEDULER_NO_TASK; + send_controller_link_response (ncc->client, ncc->op_id, NULL, + "Could not connect to delegated controller"); + cleanup_ncc (ncc); +} + + +/** + * Callback called when a connection to the neighbour is made + * + * @param cls the NeighbourConnectCtxt created in GST_handle_link_controllers() + * @param c the handle the neighbour's controller + */ +static void +neighbour_connect_cb (void *cls, struct GNUNET_TESTBED_Controller *c) +{ + struct NeighbourConnectCtxt *ncc = cls; + + GNUNET_SCHEDULER_cancel (ncc->timeout_task); + ncc->timeout_task = GNUNET_SCHEDULER_NO_TASK; + ncc->nh = NULL; + GST_neighbour_release_connection (ncc->n); + send_controller_link_response (ncc->client, ncc->op_id, NULL, NULL); + cleanup_ncc (ncc); +} + + +/** + * Function to create a neigbour and add it into the neighbour list + * + * @param host the host of the neighbour + */ +struct Neighbour * +GST_create_neighbour (struct GNUNET_TESTBED_Host *host) +{ + struct Neighbour *n; + + n = GNUNET_malloc (sizeof (struct Neighbour)); + n->host_id = GNUNET_TESTBED_host_get_id_ (host); + neighbour_list_add (n); /* just add; connect on-demand */ + return n; +} + + /** * Message handler for GNUNET_MESSAGE_TYPE_TESTBED_LCONTROLLERS message * @@ -562,16 +1179,15 @@ clean_lcc: */ void GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, - const struct GNUNET_MessageHeader *message) + const struct GNUNET_MessageHeader *message) { const struct GNUNET_TESTBED_ControllerLinkRequest *msg; - struct GNUNET_CONFIGURATION_Handle *cfg; struct LCFContextQueue *lcfq; struct Route *route; struct Route *new_route; + uint64_t op_id; uint32_t delegated_host_id; uint32_t slave_host_id; - uint16_t msize; if (NULL == GST_context) { @@ -579,13 +1195,6 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - msize = ntohs (message->size); - if (sizeof (struct GNUNET_TESTBED_ControllerLinkRequest) >= msize) - { - GNUNET_break (0); - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } msg = (const struct GNUNET_TESTBED_ControllerLinkRequest *) message; delegated_host_id = ntohl (msg->delegated_host_id); if (delegated_host_id == GST_context->host_id) @@ -618,18 +1227,41 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } - cfg = GNUNET_TESTBED_extract_config_ (message); /* destroy cfg here or in lcfcontext */ - if (NULL == cfg) - { - GNUNET_break (0); /* Configuration parsing error */ - GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); - return; - } + op_id = GNUNET_ntohll (msg->operation_id); if (slave_host_id == GST_context->host_id) /* Link from us */ { struct Slave *slave; struct LinkControllersContext *lcc; + + if (1 != msg->is_subordinate) + { + struct Neighbour *n; + struct NeighbourConnectCtxt *ncc; + + if ((delegated_host_id < neighbour_list_size) && + (NULL != neighbour_list[delegated_host_id])) + { + GNUNET_break (0); + GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); + return; + } + LOG_DEBUG ("Received request to establish a link to host %u\n", + delegated_host_id); + n = GST_create_neighbour (GST_host_list[delegated_host_id]); + ncc = GNUNET_malloc (sizeof (struct NeighbourConnectCtxt)); + ncc->n = n; + ncc->op_id = op_id; + ncc->client = client; + GNUNET_SERVER_client_keep (client); + ncc->nh = GST_neighbour_get_connection (n, neighbour_connect_cb, ncc); + ncc->timeout_task = GNUNET_SCHEDULER_add_delayed (GST_timeout, + &timeout_neighbour_connect, + ncc); + GNUNET_CONTAINER_DLL_insert_tail (ncc_head, ncc_tail, ncc); + GNUNET_SERVER_receive_done (client, GNUNET_OK); + return; + } if ((delegated_host_id < GST_slave_list_size) && (NULL != GST_slave_list[delegated_host_id])) { @@ -637,39 +1269,21 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); return; } + LOG_DEBUG ("Received request to start and establish a link to host %u\n", + delegated_host_id); slave = GNUNET_malloc (sizeof (struct Slave)); slave->host_id = delegated_host_id; slave->reghost_map = GNUNET_CONTAINER_multihashmap_create (100, GNUNET_NO); slave_list_add (slave); - if (1 != msg->is_subordinate) - { - slave->controller = - GNUNET_TESTBED_controller_connect (cfg, GST_host_list[slave->host_id], - EVENT_MASK, &slave_event_callback, - slave); - if (NULL != slave->controller) - send_controller_link_response (client, - GNUNET_ntohll (msg->operation_id), - NULL, - NULL); - else - send_controller_link_response (client, - GNUNET_ntohll (msg->operation_id), - NULL, - "Could not connect to delegated controller"); - GNUNET_SERVER_receive_done (client, GNUNET_OK); - return; - } lcc = GNUNET_malloc (sizeof (struct LinkControllersContext)); - lcc->operation_id = GNUNET_ntohll (msg->operation_id); + lcc->operation_id = op_id; GNUNET_SERVER_client_keep (client); lcc->client = client; slave->lcc = lcc; slave->controller_proc = GNUNET_TESTBED_controller_start (GST_context->master_ip, - GST_host_list[slave->host_id], cfg, - &slave_status_callback, slave); - GNUNET_CONFIGURATION_destroy (cfg); + GST_host_list[slave->host_id], + &slave_status_cb, slave); new_route = GNUNET_malloc (sizeof (struct Route)); new_route->dest = delegated_host_id; new_route->thru = GST_context->host_id; @@ -686,17 +1300,15 @@ GST_handle_link_controllers (void *cls, struct GNUNET_SERVER_Client *client, } lcfq = GNUNET_malloc (sizeof (struct LCFContextQueue)); lcfq->lcf = GNUNET_malloc (sizeof (struct LCFContext)); - lcfq->lcf->type = CLOSURE_TYPE_LCF; lcfq->lcf->delegated_host_id = delegated_host_id; lcfq->lcf->slave_host_id = slave_host_id; route = GST_find_dest_route (slave_host_id); GNUNET_assert (NULL != route); /* because we add routes carefully */ GNUNET_assert (route->dest < GST_slave_list_size); GNUNET_assert (NULL != GST_slave_list[route->dest]); - lcfq->lcf->cfg = cfg; lcfq->lcf->is_subordinate = msg->is_subordinate; lcfq->lcf->state = INIT; - lcfq->lcf->operation_id = GNUNET_ntohll (msg->operation_id); + lcfq->lcf->operation_id = op_id; lcfq->lcf->gateway = GST_slave_list[route->dest]; GNUNET_SERVER_client_keep (client); lcfq->lcf->client = client; @@ -738,7 +1350,8 @@ void GST_free_lcfq () { struct LCFContextQueue *lcfq; - + struct LCFContext *lcf; + if (NULL != lcfq_head) { if (GNUNET_SCHEDULER_NO_TASK != lcf_proc_task_id) @@ -750,10 +1363,13 @@ GST_free_lcfq () GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == lcf_proc_task_id); for (lcfq = lcfq_head; NULL != lcfq; lcfq = lcfq_head) { - GNUNET_SERVER_client_drop (lcfq->lcf->client); - GNUNET_assert (NULL != lcfq->lcf->cfg); - GNUNET_CONFIGURATION_destroy (lcfq->lcf->cfg); - GNUNET_free (lcfq->lcf); + lcf = lcfq->lcf; + GNUNET_SERVER_client_drop (lcf->client); + if (NULL != lcf->op) + GNUNET_TESTBED_operation_done (lcf->op); + if (GNUNET_SCHEDULER_NO_TASK != lcf->timeout_task) + GNUNET_SCHEDULER_cancel (lcf->timeout_task); + GNUNET_free (lcf); GNUNET_CONTAINER_DLL_remove (lcfq_head, lcfq_tail, lcfq); GNUNET_free (lcfq); }