2 This file is part of GNUnet.
3 Copyright (C) 2010, 2011, 2016 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * @file ats/ats_api_performance.c
20 * @brief automatic transport selection and outbound bandwidth determination
21 * @author Christian Grothoff
22 * @author Matthias Wachs
25 #include "gnunet_ats_service.h"
29 #define LOG(kind,...) GNUNET_log_from(kind, "ats-performance-api", __VA_ARGS__)
33 * Linked list of pending reservations.
35 struct GNUNET_ATS_ReservationContext
41 struct GNUNET_ATS_ReservationContext *next;
46 struct GNUNET_ATS_ReservationContext *prev;
51 struct GNUNET_PeerIdentity peer;
59 * Function to call on result.
61 GNUNET_ATS_ReservationCallback rcb;
69 * Do we need to undo this reservation if it succeeded? Set to
70 * #GNUNET_YES if a reservation is cancelled. (at that point, 'info'
71 * is also set to NULL; however, info will ALSO be NULL for the
72 * reservation context that is created to undo the original request,
73 * so 'info' being NULL cannot be used to check if undo is
81 * Linked list of pending reservations.
83 struct GNUNET_ATS_AddressListHandle
89 struct GNUNET_ATS_AddressListHandle *next;
94 struct GNUNET_ATS_AddressListHandle *prev;
99 struct GNUNET_ATS_PerformanceHandle *ph;
104 GNUNET_ATS_AddressInformationCallback cb;
107 * Callback closure for @e cb
114 struct GNUNET_PeerIdentity peer;
117 * Return all or specific peer only
122 * Return all or used address only
127 * Request multiplexing
134 * ATS Handle to obtain and/or modify performance information.
136 struct GNUNET_ATS_PerformanceHandle
142 const struct GNUNET_CONFIGURATION_Handle *cfg;
145 * Callback to invoke when an address has performance changes.
147 GNUNET_ATS_AddressInformationCallback addr_info_cb;
150 * Closure for @e addr_info_cb.
152 void *addr_info_cb_cls;
155 * Connection to ATS service.
157 struct GNUNET_MQ_Handle *mq;
160 * Head of linked list of pending reservation requests.
162 struct GNUNET_ATS_ReservationContext *reservation_head;
165 * Tail of linked list of pending reservation requests.
167 struct GNUNET_ATS_ReservationContext *reservation_tail;
170 * Head of linked list of pending address list requests.
172 struct GNUNET_ATS_AddressListHandle *addresslist_head;
175 * Tail of linked list of pending address list requests.
177 struct GNUNET_ATS_AddressListHandle *addresslist_tail;
180 * Current request for transmission to ATS.
182 struct GNUNET_CLIENT_TransmitHandle *th;
185 * Task to trigger reconnect.
187 struct GNUNET_SCHEDULER_Task *task;
190 * Reconnect backoff delay.
192 struct GNUNET_TIME_Relative backoff;
195 * Monitor request multiplexing
200 * Request multiplexing
205 * Is the receive loop active?
211 * Re-establish the connection to the ATS service.
213 * @param ph handle to use to re-connect.
216 reconnect (struct GNUNET_ATS_PerformanceHandle *ph);
220 * Re-establish the connection to the ATS service.
222 * @param cls handle to use to re-connect.
225 reconnect_task (void *cls)
227 struct GNUNET_ATS_PerformanceHandle *ph = cls;
235 * Reconnect to the ATS service, something went wrong.
237 * @param ph handle to reconnect
240 do_reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
242 struct GNUNET_ATS_ReservationContext *rc;
243 struct GNUNET_ATS_AddressListHandle *alh;
244 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
248 GNUNET_MQ_destroy (ph->mq);
251 while (NULL != (rc = ph->reservation_head))
253 GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
254 ph->reservation_tail,
257 rc->rcb (rc->rcb_cls,
260 GNUNET_TIME_UNIT_FOREVER_REL);
263 bandwidth_zero.value__ = htonl (0);
264 while (NULL != (alh = ph->addresslist_head))
266 GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
267 ph->addresslist_tail,
270 alh->cb (alh->cb_cls,
278 if (NULL != ph->addr_info_cb)
280 /* Indicate reconnect */
281 ph->addr_info_cb (ph->addr_info_cb_cls,
288 ph->backoff = GNUNET_TIME_STD_BACKOFF (ph->backoff);
289 ph->task = GNUNET_SCHEDULER_add_delayed (ph->backoff,
296 * We received a peer information message. Validate and process it.
298 * @param cls our context with the callback
299 * @param pi the message
300 * @return #GNUNET_OK if the message was well-formed
303 check_peer_information (void *cls,
304 const struct PeerInformationMessage *pi)
306 const char *plugin_address;
307 const char *plugin_name;
308 uint16_t plugin_address_length;
309 uint16_t plugin_name_length;
311 plugin_address_length = ntohs (pi->address_length);
312 plugin_name_length = ntohs (pi->plugin_name_length);
313 plugin_address = (const char *) &pi[1];
314 plugin_name = &plugin_address[plugin_address_length];
315 if ( (plugin_address_length + plugin_name_length
316 + sizeof(struct PeerInformationMessage) != ntohs (pi->header.size)) ||
317 (plugin_name[plugin_name_length - 1] != '\0'))
320 return GNUNET_SYSERR;
327 * We received a peer information message. Validate and process it.
329 * @param cls our context with the callback
330 * @param pi the message
331 * @return #GNUNET_OK if the message was well-formed
334 handle_peer_information (void *cls,
335 const struct PeerInformationMessage *pi)
337 struct GNUNET_ATS_PerformanceHandle *ph = cls;
338 const char *plugin_address;
339 const char *plugin_name;
340 struct GNUNET_HELLO_Address address;
341 uint16_t plugin_address_length;
343 struct GNUNET_ATS_Properties prop;
345 if (NULL == ph->addr_info_cb)
347 plugin_address_length = ntohs (pi->address_length);
348 addr_active = (int) ntohl (pi->address_active);
349 plugin_address = (const char *) &pi[1];
350 plugin_name = &plugin_address[plugin_address_length];
352 GNUNET_ATS_properties_ntoh (&prop,
354 address.peer = pi->peer;
355 address.local_info = (enum GNUNET_HELLO_AddressInfo) ntohl (pi->address_local_info);
356 address.address = plugin_address;
357 address.address_length = plugin_address_length;
358 address.transport_name = plugin_name;
359 ph->addr_info_cb (ph->addr_info_cb_cls,
369 * We received a reservation result message. Validate and process it.
371 * @param cls our context with the callback
372 * @param rr the message
375 handle_reservation_result (void *cls,
376 const struct ReservationResultMessage *rr)
378 struct GNUNET_ATS_PerformanceHandle *ph = cls;
379 struct GNUNET_ATS_ReservationContext *rc;
382 amount = ntohl (rr->amount);
383 rc = ph->reservation_head;
384 if (0 != memcmp (&rr->peer,
386 sizeof(struct GNUNET_PeerIdentity)))
392 GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
393 ph->reservation_tail,
395 if ( (0 == amount) ||
398 /* tell client if not cancelled */
400 rc->rcb (rc->rcb_cls,
403 GNUNET_TIME_relative_ntoh (rr->res_delay));
407 /* amount non-zero, but client cancelled, consider undo! */
408 if (GNUNET_YES != rc->undo)
411 return; /* do not try to undo failed undos or negative amounts */
414 (void) GNUNET_ATS_reserve_bandwidth (ph,
422 * We received a PeerInformationMessage. Validate it.
424 * @param cls our context with the callback
425 * @param pi the message
426 * @return #GNUNET_OK if the message was well-formed
429 check_address_list (void *cls,
430 const struct PeerInformationMessage *pi)
432 const char *plugin_address;
433 const char *plugin_name;
434 uint16_t plugin_address_length;
435 uint16_t plugin_name_length;
437 plugin_address_length = ntohs (pi->address_length);
438 plugin_name_length = ntohs (pi->plugin_name_length);
439 plugin_address = (const char *) &pi[1];
440 plugin_name = &plugin_address[plugin_address_length];
441 if ( (plugin_address_length + plugin_name_length
442 + sizeof (struct PeerInformationMessage) != ntohs (pi->header.size)) ||
443 (plugin_name[plugin_name_length - 1] != '\0') )
446 return GNUNET_SYSERR;
453 * We received a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE.
456 * @param cls our context with the callback
457 * @param pi the message
460 handle_address_list (void *cls,
461 const struct PeerInformationMessage *pi)
463 struct GNUNET_ATS_PerformanceHandle *ph = cls;
464 struct GNUNET_ATS_AddressListHandle *alh;
465 struct GNUNET_ATS_AddressListHandle *next;
466 const char *plugin_address;
467 const char *plugin_name;
468 struct GNUNET_HELLO_Address address;
469 struct GNUNET_PeerIdentity allzeros;
470 struct GNUNET_BANDWIDTH_Value32NBO bandwidth_zero;
471 struct GNUNET_ATS_Properties prop;
472 uint16_t plugin_address_length;
473 uint16_t plugin_name_length;
478 active = ntohl (pi->address_active);
479 plugin_address_length = ntohs (pi->address_length);
480 plugin_name_length = ntohs (pi->plugin_name_length);
481 plugin_address = (const char *) &pi[1];
482 plugin_name = &plugin_address[plugin_address_length];
483 LOG (GNUNET_ERROR_TYPE_DEBUG,
484 "Received ATS_ADDRESSLIST_RESPONSE message for peer %s and plugin %s\n",
485 GNUNET_i2s (&pi->peer),
488 next = ph->addresslist_head;
489 while (NULL != (alh = next))
496 return; /* was canceled */
498 memset (&allzeros, '\0', sizeof (allzeros));
499 if ( (0 == memcmp (&allzeros, &pi->peer, sizeof(allzeros))) &&
500 (0 == plugin_name_length) &&
501 (0 == plugin_address_length) )
504 LOG (GNUNET_ERROR_TYPE_DEBUG,
505 "Received last message for ATS_ADDRESSLIST_RESPONSE\n");
506 bandwidth_zero.value__ = htonl (0);
507 GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
508 ph->addresslist_tail,
511 alh->cb (alh->cb_cls,
521 address.peer = pi->peer;
522 address.address = plugin_address;
523 address.address_length = plugin_address_length;
524 address.transport_name = plugin_name;
525 if ( ( (GNUNET_YES == alh->all_addresses) ||
526 (GNUNET_YES == active) ) &&
529 GNUNET_ATS_properties_ntoh (&prop,
531 alh->cb (alh->cb_cls,
542 * Generic error handler, called with the appropriate error code and
543 * the same closure specified at the creation of the message queue.
544 * Not every message queue implementation supports an error handler.
546 * @param cls closure with the `struct GNUNET_ATS_PerformanceHandle *`
547 * @param error error code
550 mq_error_handler (void *cls,
551 enum GNUNET_MQ_Error error)
553 struct GNUNET_ATS_PerformanceHandle *ph = cls;
560 * Re-establish the connection to the ATS service.
562 * @param ph handle to use to re-connect.
565 reconnect (struct GNUNET_ATS_PerformanceHandle *ph)
567 struct GNUNET_MQ_MessageHandler handlers[] = {
568 GNUNET_MQ_hd_var_size (peer_information,
569 GNUNET_MESSAGE_TYPE_ATS_PEER_INFORMATION,
570 struct PeerInformationMessage,
572 GNUNET_MQ_hd_fixed_size (reservation_result,
573 GNUNET_MESSAGE_TYPE_ATS_RESERVATION_RESULT,
574 struct ReservationResultMessage,
576 GNUNET_MQ_hd_var_size (address_list,
577 GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE,
578 struct PeerInformationMessage,
580 GNUNET_MQ_handler_end ()
582 struct GNUNET_MQ_Envelope *env;
583 struct ClientStartMessage *init;
585 GNUNET_assert (NULL == ph->mq);
586 ph->mq = GNUNET_CLIENT_connect (ph->cfg,
593 env = GNUNET_MQ_msg (init,
594 GNUNET_MESSAGE_TYPE_ATS_START);
595 init->start_flag = htonl ( (NULL == ph->addr_info_cb)
596 ? START_FLAG_PERFORMANCE_NO_PIC
597 : START_FLAG_PERFORMANCE_WITH_PIC);
598 GNUNET_MQ_send (ph->mq,
604 * Get handle to access performance API of the ATS subsystem.
606 * @param cfg configuration to use
607 * @param addr_info_cb callback called when performance characteristics for
609 * @param addr_info_cb_cls closure for @a addr_info_cb
610 * @return ats performance context
612 struct GNUNET_ATS_PerformanceHandle *
613 GNUNET_ATS_performance_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
614 GNUNET_ATS_AddressInformationCallback addr_info_cb,
615 void *addr_info_cb_cls)
617 struct GNUNET_ATS_PerformanceHandle *ph;
619 ph = GNUNET_new (struct GNUNET_ATS_PerformanceHandle);
621 ph->addr_info_cb = addr_info_cb;
622 ph->addr_info_cb_cls = addr_info_cb_cls;
634 * Client is done using the ATS performance subsystem, release resources.
639 GNUNET_ATS_performance_done (struct GNUNET_ATS_PerformanceHandle *ph)
641 struct GNUNET_ATS_ReservationContext *rc;
642 struct GNUNET_ATS_AddressListHandle *alh;
644 while (NULL != (alh = ph->addresslist_head))
646 GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
647 ph->addresslist_tail,
651 while (NULL != (rc = ph->reservation_head))
653 GNUNET_CONTAINER_DLL_remove (ph->reservation_head,
654 ph->reservation_tail,
656 GNUNET_break (NULL == rc->rcb);
659 if (NULL != ph->task)
661 GNUNET_SCHEDULER_cancel (ph->task);
666 GNUNET_MQ_destroy (ph->mq);
674 * Reserve inbound bandwidth from the given peer. ATS will look at
675 * the current amount of traffic we receive from the peer and ensure
676 * that the peer could add @a amount of data to its stream.
678 * @param ph performance handle
679 * @param peer identifies the peer
680 * @param amount reserve N bytes for receiving, negative
681 * amounts can be used to undo a (recent) reservation;
682 * @param rcb function to call with the resulting reservation information
683 * @param rcb_cls closure for @a rcb
684 * @return NULL on error
685 * @deprecated will be replaced soon
687 struct GNUNET_ATS_ReservationContext *
688 GNUNET_ATS_reserve_bandwidth (struct GNUNET_ATS_PerformanceHandle *ph,
689 const struct GNUNET_PeerIdentity *peer,
691 GNUNET_ATS_ReservationCallback rcb,
694 struct GNUNET_ATS_ReservationContext *rc;
695 struct GNUNET_MQ_Envelope *env;
696 struct ReservationRequestMessage *m;
700 rc = GNUNET_new (struct GNUNET_ATS_ReservationContext);
704 rc->rcb_cls = rcb_cls;
705 if ( (NULL != rcb) &&
707 rc->undo = GNUNET_YES;
708 GNUNET_CONTAINER_DLL_insert_tail (ph->reservation_head,
709 ph->reservation_tail,
711 env = GNUNET_MQ_msg (m,
712 GNUNET_MESSAGE_TYPE_ATS_RESERVATION_REQUEST);
713 m->amount = htonl (amount);
715 GNUNET_MQ_send (ph->mq,
722 * Cancel request for reserving bandwidth.
724 * @param rc context returned by the original #GNUNET_ATS_reserve_bandwidth() call
727 GNUNET_ATS_reserve_bandwidth_cancel (struct GNUNET_ATS_ReservationContext *rc)
734 * Get information about addresses known to the ATS subsystem.
736 * @param ph the performance handle to use
737 * @param peer peer idm can be NULL for all peers
738 * @param all #GNUNET_YES to get information about all addresses or #GNUNET_NO to
739 * get only address currently used
740 * @param infocb callback to call with the addresses,
741 * will callback with address == NULL when done
742 * @param infocb_cls closure for @a infocb
743 * @return ats performance context
745 struct GNUNET_ATS_AddressListHandle*
746 GNUNET_ATS_performance_list_addresses (struct GNUNET_ATS_PerformanceHandle *ph,
747 const struct GNUNET_PeerIdentity *peer,
749 GNUNET_ATS_AddressInformationCallback infocb,
752 struct GNUNET_ATS_AddressListHandle *alh;
753 struct GNUNET_MQ_Envelope *env;
754 struct AddressListRequestMessage *m;
763 alh = GNUNET_new (struct GNUNET_ATS_AddressListHandle);
766 alh->cb_cls = infocb_cls;
768 alh->all_addresses = all;
771 alh->all_peers = GNUNET_YES;
775 alh->all_peers = GNUNET_NO;
778 GNUNET_CONTAINER_DLL_insert (ph->addresslist_head,
779 ph->addresslist_tail,
781 env = GNUNET_MQ_msg (m,
782 GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_REQUEST);
783 m->all = htonl (all);
784 m->id = htonl (alh->id);
787 GNUNET_MQ_send (ph->mq,
794 * Cancel a pending address listing operation
796 * @param alh the handle of the request to cancel
799 GNUNET_ATS_performance_list_addresses_cancel (struct GNUNET_ATS_AddressListHandle *alh)
801 struct GNUNET_ATS_PerformanceHandle *ph = alh->ph;
803 GNUNET_CONTAINER_DLL_remove (ph->addresslist_head,
804 ph->addresslist_tail,
811 * Convert a `enum GNUNET_ATS_PreferenceType` to a string
813 * @param type the preference type
814 * @return a string or NULL if invalid
817 GNUNET_ATS_print_preference_type (enum GNUNET_ATS_PreferenceKind type)
819 const char *prefs[] = GNUNET_ATS_PreferenceTypeString;
821 if (type < GNUNET_ATS_PREFERENCE_END)
828 * Change preferences for the given peer. Preference changes are forgotten if peers
831 * @param ph performance handle
832 * @param peer identifies the peer
833 * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
836 GNUNET_ATS_performance_change_preference (struct GNUNET_ATS_PerformanceHandle *ph,
837 const struct GNUNET_PeerIdentity *peer,
840 struct GNUNET_MQ_Envelope *env;
841 struct ChangePreferenceMessage *m;
843 struct PreferenceInformation *pi;
845 enum GNUNET_ATS_PreferenceKind kind;
851 while (GNUNET_ATS_PREFERENCE_END !=
852 (kind = GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind) ))
856 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
858 (void) va_arg (ap, double);
860 case GNUNET_ATS_PREFERENCE_LATENCY:
862 (void) va_arg (ap, double);
869 env = GNUNET_MQ_msg_extra (m,
870 count * sizeof(struct PreferenceInformation),
871 GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_CHANGE);
872 m->num_preferences = htonl (count);
874 pi = (struct PreferenceInformation *) &m[1];
877 while (GNUNET_ATS_PREFERENCE_END != (kind =
878 GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind) ))
880 pi[count].preference_kind = htonl (kind);
883 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
884 pi[count].preference_value = (float) va_arg (ap, double);
888 case GNUNET_ATS_PREFERENCE_LATENCY:
889 pi[count].preference_value = (float) va_arg (ap, double);
898 GNUNET_MQ_send (ph->mq,
904 * Send feedback to ATS on how good a the requirements for a peer and a
905 * preference is satisfied by ATS
907 * @param ph performance handle
908 * @param scope the time interval this valid for: [now - scope .. now]
909 * @param peer identifies the peer
910 * @param ... #GNUNET_ATS_PREFERENCE_END-terminated specification of the desired changes
913 GNUNET_ATS_performance_give_feedback (struct GNUNET_ATS_PerformanceHandle *ph,
914 const struct GNUNET_PeerIdentity *peer,
915 const struct GNUNET_TIME_Relative scope,
918 struct GNUNET_MQ_Envelope *env;
919 struct FeedbackPreferenceMessage *m;
921 struct PreferenceInformation *pi;
923 enum GNUNET_ATS_PreferenceKind kind;
929 while (GNUNET_ATS_PREFERENCE_END !=
930 (kind = GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind) ))
934 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
936 (void) va_arg (ap, double);
938 case GNUNET_ATS_PREFERENCE_LATENCY:
940 (void) va_arg (ap, double);
947 env = GNUNET_MQ_msg_extra (m,
948 count * sizeof(struct PreferenceInformation),
949 GNUNET_MESSAGE_TYPE_ATS_PREFERENCE_FEEDBACK);
950 m->scope = GNUNET_TIME_relative_hton (scope);
951 m->num_feedback = htonl (count);
953 pi = (struct PreferenceInformation *) &m[1];
956 while (GNUNET_ATS_PREFERENCE_END != (kind =
957 GNUNET_VA_ARG_ENUM (ap, GNUNET_ATS_PreferenceKind) ))
959 pi[count].preference_kind = htonl (kind);
962 case GNUNET_ATS_PREFERENCE_BANDWIDTH:
963 pi[count].preference_value = (float) va_arg (ap, double);
967 case GNUNET_ATS_PREFERENCE_LATENCY:
968 pi[count].preference_value = (float) va_arg (ap, double);
977 GNUNET_MQ_send (ph->mq,
981 /* end of ats_api_performance.c */