X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fats%2Fats_api_performance.c;h=90e061c1d2873e62716ff1cb183716dcac7fcb1b;hb=f1ca38573f22205e28ac482efebe463696c9c2c7;hp=4dda50ccee8a916476fda502381ace241156694b;hpb=76fbf6c253e780078f4ac5acf43ff89f95c78253;p=oweals%2Fgnunet.git diff --git a/src/ats/ats_api_performance.c b/src/ats/ats_api_performance.c index 4dda50cce..90e061c1d 100644 --- a/src/ats/ats_api_performance.c +++ b/src/ats/ats_api_performance.c @@ -1,33 +1,32 @@ /* - This file is part of GNUnet. - (C) 2010,2011 Christian Grothoff (and other contributing authors) - - 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 3, or (at your - option) any later version. - - GNUnet is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. - - You should have received a copy of the GNU General Public License - along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. -*/ + This file is part of GNUnet. + (C) 2010,2011 Christian Grothoff (and other contributing authors) + + 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 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. + */ /** * @file ats/ats_api_performance.c * @brief automatic transport selection and outbound bandwidth determination * @author Christian Grothoff * @author Matthias Wachs - */ + */ #include "platform.h" #include "gnunet_ats_service.h" #include "ats.h" - /** * Message in linked list we should send to the ATS service. The * actual binary message follows this struct. @@ -37,12 +36,12 @@ struct PendingMessage /** * Kept in a DLL. - */ + */ struct PendingMessage *next; /** * Kept in a DLL. - */ + */ struct PendingMessage *prev; /** @@ -52,11 +51,10 @@ struct PendingMessage /** * Is this the 'ATS_START' message? - */ + */ int is_init; }; - /** * Linked list of pending reservations. */ @@ -65,19 +63,19 @@ struct GNUNET_ATS_ReservationContext /** * Kept in a DLL. - */ + */ struct GNUNET_ATS_ReservationContext *next; /** * Kept in a DLL. - */ + */ struct GNUNET_ATS_ReservationContext *prev; /** * Target peer. */ struct GNUNET_PeerIdentity peer; - + /** * Desired reservation */ @@ -86,12 +84,12 @@ struct GNUNET_ATS_ReservationContext /** * Function to call on result. */ - GNUNET_ATS_ReservationCallback info; + GNUNET_ATS_ReservationCallback rcb; /** - * Closure for 'info' + * Closure for 'rcb' */ - void *info_cls; + void *rcb_cls; /** * Do we need to undo this reservation if it succeeded? Set to @@ -104,27 +102,78 @@ struct GNUNET_ATS_ReservationContext int undo; }; +/** + * Linked list of pending reservations. + */ +struct GNUNET_ATS_AddressListHandle +{ + + /** + * Kept in a DLL. + */ + struct GNUNET_ATS_AddressListHandle *next; + + /** + * Kept in a DLL. + */ + struct GNUNET_ATS_AddressListHandle *prev; + + /** + * Performance handle + */ + struct GNUNET_ATS_PerformanceHandle *ph; + + /** + * Callback + */ + GNUNET_ATS_AddressInformationCallback cb; + + /** + * Callback closure + */ + void *cb_cls; + + /** + * Target peer. + */ + struct GNUNET_PeerIdentity peer; + + /** + * Return all or specific peer only + */ + int all_peers; + + /** + * Return all or used address only + */ + int all_addresses; + + /** + * Request multiplexing + */ + uint32_t id; +}; /** * ATS Handle to obtain and/or modify performance information. */ struct GNUNET_ATS_PerformanceHandle { - + /** * Our configuration. */ const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Callback to invoke on performance changes. + * Callback to invoke when an address has performance changes. */ - GNUNET_ATS_PeerInformationCallback infocb; - + GNUNET_ATS_AddressInformationCallback addr_info_cb; + /** - * Closure for 'infocb'. + * Closure for 'addr_info_cb'. */ - void *infocb_cls; + void *addr_info_cb_cls; /** * Connection to ATS service. @@ -151,32 +200,78 @@ struct GNUNET_ATS_PerformanceHandle */ struct GNUNET_ATS_ReservationContext *reservation_tail; + /** + * Head of linked list of pending address list requests. + */ + struct GNUNET_ATS_AddressListHandle *addresslist_head; + + /** + * Tail of linked list of pending address list requests. + */ + struct GNUNET_ATS_AddressListHandle *addresslist_tail; + /** * Current request for transmission to ATS. */ struct GNUNET_CLIENT_TransmitHandle *th; -}; + /** + * Task to trigger reconnect. + */ + GNUNET_SCHEDULER_TaskIdentifier task; + /** + * Monitor request multiplexing + */ + uint32_t monitor_id; + + /** + * Request multiplexing + */ + uint32_t id; +}; /** * Re-establish the connection to the ATS service. * - * @param sh handle to use to re-connect. + * @param ph handle to use to re-connect. */ static void reconnect (struct GNUNET_ATS_PerformanceHandle *ph); +/** + * Re-establish the connection to the ATS service. + * + * @param cls handle to use to re-connect. + * @param tc scheduler context + */ +static void +reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) +{ + struct GNUNET_ATS_PerformanceHandle *ph = cls; + + ph->task = GNUNET_SCHEDULER_NO_TASK; + reconnect (ph); +} /** * Transmit messages from the message queue to the service * (if there are any, and if we are not already trying). * - * @param sh handle to use + * @param ph handle to use */ static void do_transmit (struct GNUNET_ATS_PerformanceHandle *ph); +/** + * Type of a function to call when we receive a message + * from the service. + * + * @param cls the 'struct GNUNET_ATS_SchedulingHandle' + * @param msg message received, NULL on timeout or fatal error + */ +static void +process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg); /** * We can now transmit a message to ATS. Do it. @@ -187,9 +282,7 @@ do_transmit (struct GNUNET_ATS_PerformanceHandle *ph); * @return number of bytes copied into buf */ static size_t -transmit_message_to_ats (void *cls, - size_t size, - void *buf) +transmit_message_to_ats (void *cls, size_t size, void *buf) { struct GNUNET_ATS_PerformanceHandle *ph = cls; struct PendingMessage *p; @@ -199,21 +292,18 @@ transmit_message_to_ats (void *cls, ph->th = NULL; ret = 0; cbuf = buf; - while ( (NULL != (p = ph->pending_head)) && - (p->size <= size) ) + while ((NULL != (p = ph->pending_head)) && (p->size <= size)) { - memcpy (&cbuf[ret], &p[1], p->size); + memcpy (&cbuf[ret], &p[1], p->size); ret += p->size; - GNUNET_CONTAINER_DLL_remove (ph->pending_head, - ph->pending_tail, - p); - GNUNET_free (p); + size -= p->size; + GNUNET_CONTAINER_DLL_remove(ph->pending_head, ph->pending_tail, p); + GNUNET_free(p); } do_transmit (ph); return ret; } - /** * Transmit messages from the message queue to the service * (if there are any, and if we are not already trying). @@ -229,13 +319,220 @@ do_transmit (struct GNUNET_ATS_PerformanceHandle *ph) return; if (NULL == (p = ph->pending_head)) return; - ph->th = GNUNET_CLIENT_notify_transmit_ready (ph->client, - p->size, - GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_YES, - &transmit_message_to_ats, ph); + if (NULL == ph->client) + return; /* currently reconnecting */ + ph->th = GNUNET_CLIENT_notify_transmit_ready (ph->client, p->size, + GNUNET_TIME_UNIT_FOREVER_REL, GNUNET_YES, &transmit_message_to_ats, ph); +} + +/** + * We received a peer information message. Validate and process it. + * + * @param ph our context with the callback + * @param msg the message + * @return GNUNET_OK if the message was well-formed + */ +static int +process_pi_message (struct GNUNET_ATS_PerformanceHandle *ph, + const struct GNUNET_MessageHeader *msg) +{ + const struct PeerInformationMessage *pi; + const struct GNUNET_ATS_Information *atsi; + const char *plugin_address; + const char *plugin_name; + struct GNUNET_HELLO_Address address; + uint16_t plugin_address_length; + uint16_t plugin_name_length; + uint32_t ats_count; + int addr_active; + + if (ntohs (msg->size) < sizeof(struct PeerInformationMessage)) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + + pi = (const struct PeerInformationMessage *) msg; + ats_count = ntohl (pi->ats_count); + plugin_address_length = ntohs (pi->address_length); + plugin_name_length = ntohs (pi->plugin_name_length); + addr_active = ntohl (pi->address_active); + atsi = (const struct GNUNET_ATS_Information *) &pi[1]; + plugin_address = (const char *) &atsi[ats_count]; + plugin_name = &plugin_address[plugin_address_length]; + if ((plugin_address_length + plugin_name_length + + ats_count * sizeof(struct GNUNET_ATS_Information) + + sizeof(struct PeerInformationMessage) != ntohs (msg->size)) + || (ats_count + > GNUNET_SERVER_MAX_MESSAGE_SIZE + / sizeof(struct GNUNET_ATS_Information)) + || (plugin_name[plugin_name_length - 1] != '\0')) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + + if (NULL != ph->addr_info_cb) + { + address.peer = pi->peer; + address.address = plugin_address; + address.address_length = plugin_address_length; + address.transport_name = plugin_name; + + ph->addr_info_cb (ph->addr_info_cb_cls, &address, addr_active, + pi->bandwidth_out, pi->bandwidth_in, atsi, ats_count); + } + return GNUNET_OK; +} + +/** + * We received a reservation result message. Validate and process it. + * + * @param ph our context with the callback + * @param msg the message + * @return GNUNET_OK if the message was well-formed + */ +static int +process_rr_message (struct GNUNET_ATS_PerformanceHandle *ph, + const struct GNUNET_MessageHeader *msg) +{ + const struct ReservationResultMessage *rr; + struct GNUNET_ATS_ReservationContext *rc; + int32_t amount; + + if (ntohs (msg->size) < sizeof(struct ReservationResultMessage)) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + rr = (const struct ReservationResultMessage *) msg; + amount = ntohl (rr->amount); + rc = ph->reservation_head; + if (0 != memcmp (&rr->peer, &rc->peer, sizeof(struct GNUNET_PeerIdentity))) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + GNUNET_CONTAINER_DLL_remove(ph->reservation_head, ph->reservation_tail, rc); + if ((amount == 0) || (rc->rcb != NULL )) + { + /* tell client if not cancelled */ + if (rc->rcb != NULL ) + rc->rcb (rc->rcb_cls, &rr->peer, amount, + GNUNET_TIME_relative_ntoh (rr->res_delay)); + GNUNET_free(rc); + return GNUNET_OK; + } + /* amount non-zero, but client cancelled, consider undo! */ + if (GNUNET_YES != rc->undo) + { + GNUNET_free(rc); + return GNUNET_OK; /* do not try to undo failed undos or negative amounts */ + } + GNUNET_free(rc); + (void) GNUNET_ATS_reserve_bandwidth (ph, &rr->peer, -amount, NULL, NULL ); + return GNUNET_OK; } +/** + * We received a reservation result message. Validate and process it. + * + * @param ph our context with the callback + * @param msg the message + * @return GNUNET_OK if the message was well-formed + */ +static int +process_ar_message (struct GNUNET_ATS_PerformanceHandle *ph, + const struct GNUNET_MessageHeader *msg) +{ + const struct PeerInformationMessage *pi; + struct GNUNET_ATS_AddressListHandle *alh; + struct GNUNET_ATS_AddressListHandle *next; + const struct GNUNET_ATS_Information *atsi; + const char *plugin_address; + const char *plugin_name; + struct GNUNET_HELLO_Address address; + struct GNUNET_PeerIdentity allzeros; + struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero; + uint16_t plugin_address_length; + uint16_t plugin_name_length; + uint32_t ats_count; + uint32_t active; + uint32_t id; + + if (ntohs (msg->size) < sizeof(struct PeerInformationMessage)) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Received %s message\n"), + "ATS_ADDRESSLIST_RESPONSE"); + + pi = (const struct PeerInformationMessage *) msg; + id = ntohl (pi->id); + ats_count = ntohl (pi->ats_count); + active = ntohl (pi->address_active); + plugin_address_length = ntohs (pi->address_length); + plugin_name_length = ntohs (pi->plugin_name_length); + atsi = (const struct GNUNET_ATS_Information *) &pi[1]; + plugin_address = (const char *) &atsi[ats_count]; + plugin_name = &plugin_address[plugin_address_length]; + if ((plugin_address_length + plugin_name_length + + ats_count * sizeof(struct GNUNET_ATS_Information) + + sizeof(struct PeerInformationMessage) != ntohs (msg->size)) + || (ats_count + > GNUNET_SERVER_MAX_MESSAGE_SIZE + / sizeof(struct GNUNET_ATS_Information)) + || (plugin_name[plugin_name_length - 1] != '\0')) + { + GNUNET_break(0); + return GNUNET_SYSERR; + } + + next = ph->addresslist_head; + while (NULL != (alh = next)) + { + next = alh->next; + if (alh->id == id) + break; + } + if (NULL == alh) + { + /* was canceled */ + return GNUNET_SYSERR; + } + + memset (&allzeros, '\0', sizeof(allzeros)); + if ((0 == memcmp (&allzeros, &pi->peer, sizeof(allzeros))) + && (0 == plugin_name_length) && (0 == plugin_address_length) + && (0 == ats_count)) + { + /* Done */ + GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, _("Received last message for %s \n"), + "ATS_ADDRESSLIST_RESPONSE"); + bandwidth_zero.value__ = htonl (0); + if (NULL != alh->cb) + alh->cb (ph->addr_info_cb_cls, NULL, GNUNET_NO, bandwidth_zero, + bandwidth_zero, NULL, 0); + GNUNET_CONTAINER_DLL_remove(ph->addresslist_head, ph->addresslist_tail, + alh); + GNUNET_free(alh); + return GNUNET_OK; + } + + address.peer = pi->peer; + address.address = plugin_address; + address.address_length = plugin_address_length; + address.transport_name = plugin_name; + + if ((GNUNET_YES == alh->all_addresses) || (GNUNET_YES == active)) + { + if (NULL != alh->cb) + alh->cb (ph->addr_info_cb_cls, &address, active, pi->bandwidth_out, + pi->bandwidth_in, atsi, ats_count); + } + return GNUNET_OK; +} /** * Type of a function to call when we receive a message @@ -245,34 +542,44 @@ do_transmit (struct GNUNET_ATS_PerformanceHandle *ph) * @param msg message received, NULL on timeout or fatal error */ static void -process_ats_message (void *cls, - const struct GNUNET_MessageHeader *msg) +process_ats_message (void *cls, const struct GNUNET_MessageHeader *msg) { struct GNUNET_ATS_PerformanceHandle *ph = cls; - if (NULL == msg) - { - GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO); - ph->client = NULL; - reconnect (ph); - return; - } + if (NULL == msg) + goto reconnect; switch (ntohs (msg->type)) { - // FIXME + case GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION: + if (GNUNET_OK != process_pi_message (ph, msg)) + goto reconnect; + break; + case GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT: + if (GNUNET_OK != process_rr_message (ph, msg)) + goto reconnect; + break; + case GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE: + if (GNUNET_OK != process_ar_message (ph, msg)) + goto reconnect; + break; default: - GNUNET_break (0); - GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO); - ph->client = NULL; - reconnect (ph); - return; + GNUNET_break(0); + goto reconnect; } - GNUNET_CLIENT_receive (ph->client, - &process_ats_message, ph, - GNUNET_TIME_UNIT_FOREVER_REL); + GNUNET_CLIENT_receive (ph->client, &process_ats_message, ph, + GNUNET_TIME_UNIT_FOREVER_REL); + return; + reconnect: if (NULL != ph->th) + { + GNUNET_CLIENT_notify_transmit_ready_cancel (ph->th); + ph->th = NULL; + } + GNUNET_CLIENT_disconnect (ph->client); + ph->client = NULL; + ph->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, + &reconnect_task, ph); } - /** * Re-establish the connection to the ATS service. * @@ -284,58 +591,52 @@ reconnect (struct GNUNET_ATS_PerformanceHandle *ph) struct PendingMessage *p; struct ClientStartMessage *init; - GNUNET_assert (NULL == ph->client); + GNUNET_assert(NULL == ph->client); ph->client = GNUNET_CLIENT_connect ("ats", ph->cfg); - GNUNET_assert (NULL != ph->client); - GNUNET_CLIENT_receive (ph->client, - &process_ats_message, ph, - GNUNET_TIME_UNIT_FOREVER_REL); - if ( (NULL == (p = ph->pending_head)) || - (GNUNET_YES != p->is_init) ) + GNUNET_assert(NULL != ph->client); + GNUNET_CLIENT_receive (ph->client, &process_ats_message, ph, + GNUNET_TIME_UNIT_FOREVER_REL); + if ((NULL == (p = ph->pending_head)) || (GNUNET_YES != p->is_init)) { p = GNUNET_malloc (sizeof (struct PendingMessage) + - sizeof (struct ClientStartMessage)); - p->size = sizeof (struct ClientStartMessage); + sizeof (struct ClientStartMessage)); + p->size = sizeof(struct ClientStartMessage); p->is_init = GNUNET_YES; init = (struct ClientStartMessage *) &p[1]; init->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_START); - init->header.size = htons (sizeof (struct ClientStartMessage)); - init->start_flag = htonl ((ph->infocb == NULL) - ? START_FLAG_PERFORMANCE_NO_PIC - : START_FLAG_PERFORMANCE_WITH_PIC); - GNUNET_CONTAINER_DLL_insert (ph->pending_head, - ph->pending_tail, - p); + init->header.size = htons (sizeof(struct ClientStartMessage)); + init->start_flag = htonl ( + (NULL == ph->addr_info_cb) ? + START_FLAG_PERFORMANCE_NO_PIC : START_FLAG_PERFORMANCE_WITH_PIC); + GNUNET_CONTAINER_DLL_insert(ph->pending_head, ph->pending_tail, p); } do_transmit (ph); } - - /** * Get handle to access performance API of the ATS subsystem. * * @param cfg configuration to use - * @param infocb function to call on allocation changes, can be NULL - * @param infocb_cls closure for infocb + * @param addr_info_cb callback called when performance characteristics for + * an address change + * @param addr_info_cb_cls closure for infocb * @return ats performance context */ struct GNUNET_ATS_PerformanceHandle * GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg, - GNUNET_ATS_PeerInformationCallback infocb, - void *infocb_cls) + GNUNET_ATS_AddressInformationCallback addr_info_cb, void *addr_info_cb_cls) { struct GNUNET_ATS_PerformanceHandle *ph; - ph = GNUNET_malloc (sizeof (struct GNUNET_ATS_PerformanceHandle)); + ph = GNUNET_new (struct GNUNET_ATS_PerformanceHandle); ph->cfg = cfg; - ph->infocb = infocb; - ph->infocb_cls = infocb_cls; + ph->addr_info_cb = addr_info_cb; + ph->addr_info_cb_cls = addr_info_cb_cls; + ph->id = 0; reconnect (ph); return ph; } - /** * Client is done using the ATS performance subsystem, release resources. * @@ -346,26 +647,38 @@ GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph) { struct PendingMessage *p; struct GNUNET_ATS_ReservationContext *rc; - + struct GNUNET_ATS_AddressListHandle *alh; + while (NULL != (p = ph->pending_head)) { - GNUNET_CONTAINER_DLL_remove (ph->pending_head, - ph->pending_tail, - p); - GNUNET_free (p); + GNUNET_CONTAINER_DLL_remove(ph->pending_head, ph->pending_tail, p); + GNUNET_free(p); + } + while (NULL != (alh = ph->addresslist_head)) + { + GNUNET_CONTAINER_DLL_remove(ph->addresslist_head, ph->addresslist_tail, + alh); + GNUNET_free(alh); } while (NULL != (rc = ph->reservation_head)) { - GNUNET_CONTAINER_DLL_remove (ph->reservation_head, - ph->reservation_tail, - rc); - GNUNET_break (NULL == rc->info); - GNUNET_free (p); - } - GNUNET_CLIENT_disconnect (ph->client, GNUNET_NO); - GNUNET_free (ph); -} + GNUNET_CONTAINER_DLL_remove(ph->reservation_head, ph->reservation_tail, rc); + GNUNET_break(NULL == rc->rcb); + GNUNET_free(rc); + } + if (GNUNET_SCHEDULER_NO_TASK != ph->task) + { + GNUNET_SCHEDULER_cancel (ph->task); + ph->task = GNUNET_SCHEDULER_NO_TASK; + } + if (NULL != ph->client) + { + GNUNET_CLIENT_disconnect (ph->client); + ph->client = NULL; + } + GNUNET_free(ph); +} /** * Reserve inbound bandwidth from the given peer. ATS will look at @@ -376,97 +689,313 @@ GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph) * @param peer identifies the peer * @param amount reserve N bytes for receiving, negative * amounts can be used to undo a (recent) reservation; - * @param info function to call with the resulting reservation information - * @param info_cls closure for info + * @param rcb function to call with the resulting reservation information + * @param rcb_cls closure for info * @return NULL on error * @deprecated will be replaced soon */ struct GNUNET_ATS_ReservationContext * GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph, - const struct GNUNET_PeerIdentity *peer, - int32_t amount, - GNUNET_ATS_ReservationCallback info, - void *info_cls) + const struct GNUNET_PeerIdentity *peer, int32_t amount, + GNUNET_ATS_ReservationCallback rcb, void *rcb_cls) { struct GNUNET_ATS_ReservationContext *rc; struct PendingMessage *p; struct ReservationRequestMessage *m; - rc = GNUNET_malloc (sizeof (struct GNUNET_ATS_ReservationContext)); + rc = GNUNET_new (struct GNUNET_ATS_ReservationContext); rc->size = amount; rc->peer = *peer; - rc->info = info; - rc->info_cls = info_cls; - GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head, - ph->reservation_tail, - rc); - - p = GNUNET_malloc (sizeof (struct PendingMessage) + - sizeof (struct ReservationRequestMessage)); - p->size = sizeof (struct ReservationRequestMessage); + rc->rcb = rcb; + rc->rcb_cls = rcb_cls; + if ((rcb != NULL )&& (amount > 0))rc->undo = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert_tail(ph->reservation_head, ph->reservation_tail, + rc); + + p = GNUNET_malloc (sizeof (struct PendingMessage) + + sizeof (struct ReservationRequestMessage)); + p->size = sizeof(struct ReservationRequestMessage); p->is_init = GNUNET_NO; - m = (struct ReservationRequestMessage*) &p[1]; - m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE); - m->header.size = htons (sizeof (struct ReservationRequestMessage)); + m = (struct ReservationRequestMessage *) &p[1]; + m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_RESERVATION_REQUEST); + m->header.size = htons (sizeof(struct ReservationRequestMessage)); m->amount = htonl (amount); m->peer = *peer; - GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head, - ph->pending_tail, - p); + GNUNET_CONTAINER_DLL_insert_tail(ph->pending_head, ph->pending_tail, p); + do_transmit (ph); return rc; } - /** * Cancel request for reserving bandwidth. * * @param rc context returned by the original GNUNET_ATS_reserve_bandwidth call */ void -GNUNET_ATS_reserve_bandwidth_cancel (struct - GNUNET_ATS_ReservationContext *rc) +GNUNET_ATS_reserve_bandwidth_cancel (struct GNUNET_ATS_ReservationContext *rc) { - rc->info = NULL; + rc->rcb = NULL; } +/** + * Get information about addresses known to the ATS subsystem. + * + * @param handle the performance handle to use + * @param peer peer idm can be NULL for all peers + * @param all GNUNET_YES to get information about all addresses or GNUNET_NO to + * get only address currently used + * @param infocb callback to call with the addresses, + * will callback with address == NULL when done + * @param infocb_cls closure for infocb + * @return ats performance context + */ +struct GNUNET_ATS_AddressListHandle* +GNUNET_ATS_performance_list_addresses ( + struct GNUNET_ATS_PerformanceHandle *handle, + const struct GNUNET_PeerIdentity *peer, int all, + GNUNET_ATS_AddressInformationCallback infocb, void *infocb_cls) +{ + struct GNUNET_ATS_AddressListHandle *alh; + struct PendingMessage *p; + struct AddressListRequestMessage *m; + + GNUNET_assert(NULL != handle); + if (NULL == infocb) + return NULL ; + + alh = GNUNET_new (struct GNUNET_ATS_AddressListHandle); + alh->id = handle->id; + handle->id++; + alh->cb = infocb; + alh->cb_cls = infocb_cls; + alh->ph = handle; + alh->all_addresses = all; + if (NULL == peer) + alh->all_peers = GNUNET_YES; + else + { + alh->all_peers = GNUNET_NO; + alh->peer = (*peer); + } + + GNUNET_CONTAINER_DLL_insert(handle->addresslist_head, + handle->addresslist_tail, alh); + + p = GNUNET_malloc (sizeof (struct PendingMessage) + + sizeof (struct AddressListRequestMessage)); + p->size = sizeof(struct AddressListRequestMessage); + m = (struct AddressListRequestMessage *) &p[1]; + m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST); + m->header.size = htons (sizeof(struct AddressListRequestMessage)); + m->all = htonl (all); + m->id = htonl (alh->id); + if (NULL != peer) + m->peer = *peer; + else + { + memset (&m->peer, '\0', sizeof(struct GNUNET_PeerIdentity)); + } + GNUNET_CONTAINER_DLL_insert_tail(handle->pending_head, handle->pending_tail, + p); + + do_transmit (handle); + + return alh; +} + +/** + * Cancel a pending address listing operation + * + * @param handle the GNUNET_ATS_AddressListHandle handle to cancel + */ +void +GNUNET_ATS_performance_list_addresses_cancel ( + struct GNUNET_ATS_AddressListHandle *handle) +{ + GNUNET_assert(NULL != handle); + + GNUNET_CONTAINER_DLL_remove(handle->ph->addresslist_head, + handle->ph->addresslist_tail, handle); + GNUNET_free(handle); +} + +/** + * Convert a GNUNET_ATS_PreferenceType to a string + * + * @param type the preference type + * @return a string or NULL if invalid + */ +const char * +GNUNET_ATS_print_preference_type (uint32_t type) +{ + char *prefs[GNUNET_ATS_PreferenceCount] = GNUNET_ATS_PreferenceTypeString; + if (type < GNUNET_ATS_PreferenceCount) + return prefs[type]; + return NULL ; +} /** * Change preferences for the given peer. Preference changes are forgotten if peers * disconnect. - * + * * @param ph performance handle * @param peer identifies the peer * @param ... 0-terminated specification of the desired changes */ void -GNUNET_ATS_change_preference (struct GNUNET_ATS_PerformanceHandle *ph, - const struct GNUNET_PeerIdentity *peer, - ...) +GNUNET_ATS_performance_change_preference ( + struct GNUNET_ATS_PerformanceHandle *ph, + const struct GNUNET_PeerIdentity *peer, ...) { struct PendingMessage *p; struct ChangePreferenceMessage *m; size_t msize; uint32_t count; struct PreferenceInformation *pi; + va_list ap; + enum GNUNET_ATS_PreferenceKind kind; - // FIXME: set 'count' - p = GNUNET_malloc (sizeof (struct PendingMessage) + - sizeof (struct ChangePreferenceMessage) + - count * sizeof (struct PreferenceInformation)); + count = 0; + va_start(ap, peer); + while (GNUNET_ATS_PREFERENCE_END != (kind = + va_arg (ap, enum GNUNET_ATS_PreferenceKind) )) + { + switch (kind) + { + case GNUNET_ATS_PREFERENCE_BANDWIDTH: + count++; + (void) va_arg (ap, double); + + break; + case GNUNET_ATS_PREFERENCE_LATENCY: + count++; + (void) va_arg (ap, double); + + break; + default: + GNUNET_assert(0); + } + } + va_end(ap); + msize = count * sizeof(struct PreferenceInformation) + + sizeof(struct ChangePreferenceMessage); + p = GNUNET_malloc (sizeof (struct PendingMessage) + msize); p->size = msize; p->is_init = GNUNET_NO; - m = (struct ReservationRequestMessage*) &p[1]; - m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE); + m = (struct ChangePreferenceMessage *) &p[1]; + m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE); m->header.size = htons (msize); m->num_preferences = htonl (count); m->peer = *peer; - pi = (struct PreferenceInformation*) &m[1]; - // FIXME: fill in 'pi' + pi = (struct PreferenceInformation *) &m[1]; + count = 0; + va_start(ap, peer); + while (GNUNET_ATS_PREFERENCE_END != (kind = + va_arg (ap, enum GNUNET_ATS_PreferenceKind) )) + { + pi[count].preference_kind = htonl (kind); + switch (kind) + { + case GNUNET_ATS_PREFERENCE_BANDWIDTH: + pi[count].preference_value = (float) va_arg (ap, double); + + count++; + break; + case GNUNET_ATS_PREFERENCE_LATENCY: + pi[count].preference_value = (float) va_arg (ap, double); + + count++; + break; + default: + GNUNET_assert(0); + } + } + va_end(ap); + GNUNET_CONTAINER_DLL_insert_tail(ph->pending_head, ph->pending_tail, p); + do_transmit (ph); +} - GNUNET_CONTAINER_DLL_insert_tail (ph->pending_head, - ph->pending_tail, - p); +/** + * Send feedback to ATS on how good a the requirements for a peer and a + * preference is satisfied by ATS + * + * @param ph performance handle + * @param scope the time interval this valid for: [now - scope .. now] + * @param peer identifies the peer + * @param ... 0-terminated specification of the desired changes + */ +void +GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph, + const struct GNUNET_PeerIdentity *peer, + const struct GNUNET_TIME_Relative scope, ...) +{ + struct PendingMessage *p; + struct FeedbackPreferenceMessage *m; + size_t msize; + uint32_t count; + struct PreferenceInformation *pi; + va_list ap; + enum GNUNET_ATS_PreferenceKind kind; + + count = 0; + va_start(ap, scope); + while (GNUNET_ATS_PREFERENCE_END != (kind = + va_arg (ap, enum GNUNET_ATS_PreferenceKind) )) + { + switch (kind) + { + case GNUNET_ATS_PREFERENCE_BANDWIDTH: + count++; + (void) va_arg (ap, double); + + break; + case GNUNET_ATS_PREFERENCE_LATENCY: + count++; + (void) va_arg (ap, double); + + break; + default: + GNUNET_assert(0); + } + } + va_end(ap); + msize = count * sizeof(struct PreferenceInformation) + + sizeof(struct FeedbackPreferenceMessage); + p = GNUNET_malloc (sizeof (struct PendingMessage) + msize); + p->size = msize; + p->is_init = GNUNET_NO; + m = (struct FeedbackPreferenceMessage *) &p[1]; + m->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK); + m->header.size = htons (msize); + m->scope = GNUNET_TIME_relative_hton (scope); + m->num_feedback = htonl (count); + m->peer = *peer; + pi = (struct PreferenceInformation *) &m[1]; + count = 0; + va_start(ap, scope); + while (GNUNET_ATS_PREFERENCE_END != (kind = + va_arg (ap, enum GNUNET_ATS_PreferenceKind) )) + { + pi[count].preference_kind = htonl (kind); + switch (kind) + { + case GNUNET_ATS_PREFERENCE_BANDWIDTH: + pi[count].preference_value = (float) va_arg (ap, double); + + count++; + break; + case GNUNET_ATS_PREFERENCE_LATENCY: + pi[count].preference_value = (float) va_arg (ap, double); + + count++; + break; + default: + GNUNET_assert(0); + } + } + va_end(ap); + GNUNET_CONTAINER_DLL_insert_tail(ph->pending_head, ph->pending_tail, p); + do_transmit (ph); } /* end of ats_api_performance.c */ -