2 This file is part of GNUnet.
3 (C) 2010-2015 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 3, 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.
21 * @file ats/ats_api_scheduling.c
22 * @brief automatic transport selection and outbound bandwidth determination
23 * @author Christian Grothoff
24 * @author Matthias Wachs
27 #include "gnunet_ats_service.h"
31 * How frequently do we scan the interfaces for changes to the addresses?
33 #define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
37 * Session ID we use if there is no session / slot.
43 * Information we track per address.
44 * FIXME: but what about *incoming* connections?
45 * "address" tells us about those, those
46 * are only valid while we have a session.
47 * Need to clarify all this!!!
49 struct GNUNET_ATS_AddressRecord
54 struct GNUNET_HELLO_Address *address;
57 * Session handle. NULL if we have an address but no
58 * active session for this address.
60 struct Session *session;
63 * Array with performance data about the address.
65 struct GNUNET_ATS_Information *ats;
68 * Number of entries in @e ats.
73 * Which slot (index) in the session array does
74 * this record correspond to? FIXME:
75 * FIXME: a linear search on this is really crappy!
76 * Maybe switch to a 64-bit global counter and be
77 * done with it? Or does that then cause too much
78 * trouble on the ATS-service side?
83 * Is this address currently in use?
84 * FIXME: document what "in use" means, and why it
90 * We're about to destroy this address record, just ATS does
98 * We keep a list of our local networks so we can answer
99 * LAN vs. WAN questions. Note: WLAN is not detected yet.
100 * (maybe we can do that heuristically based on interface
101 * name in the future?)
108 struct ATS_Network *next;
113 struct ATS_Network *prev;
118 struct sockaddr *network;
121 * Netmask to determine what is in the LAN.
123 struct sockaddr *netmask;
126 * How long are @e network and @e netmask?
133 * Handle for ATS address suggestion requests.
135 struct GNUNET_ATS_SuggestHandle
138 * ID of the peer for which address suggestion was requested.
140 struct GNUNET_PeerIdentity id;
145 * Handle to the ATS subsystem for bandwidth/transport scheduling information.
147 struct GNUNET_ATS_SchedulingHandle
153 const struct GNUNET_CONFIGURATION_Handle *cfg;
156 * Callback to invoke on suggestions.
158 GNUNET_ATS_AddressSuggestionCallback suggest_cb;
161 * Closure for @e suggest_cb.
163 void *suggest_cb_cls;
166 * Map with the identities of all the peers for which we would
167 * like to have address suggestions. The key is the PID, the
168 * value is currently the `struct GNUNET_ATS_SuggestHandle`
170 struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
173 * Connection to ATS service.
175 struct GNUNET_CLIENT_Connection *client;
178 * Message queue for sending requests to the ATS service.
180 struct GNUNET_MQ_Handle *mq;
183 * Head of LAN networks list.
185 struct ATS_Network *net_head;
188 * Tail of LAN networks list.
190 struct ATS_Network *net_tail;
193 * Array of session objects (we need to translate them to numbers and back
194 * for the protocol; the offset in the array is the session number on the
195 * network). Index 0 is always NULL and reserved to represent the NULL pointer.
196 * Unused entries are also NULL.
198 struct GNUNET_ATS_AddressRecord **session_array;
201 * Task to trigger reconnect.
203 struct GNUNET_SCHEDULER_Task *task;
206 * Task for periodically refreshing our LAN network list.
208 struct GNUNET_SCHEDULER_Task *interface_task;
211 * Size of the @e session_array.
213 unsigned int session_array_size;
219 * Re-establish the connection to the ATS service.
221 * @param sh handle to use to re-connect.
224 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
228 * Re-establish the connection to the ATS service.
230 * @param cls handle to use to re-connect.
231 * @param tc scheduler context
234 reconnect_task (void *cls,
235 const struct GNUNET_SCHEDULER_TaskContext *tc)
237 struct GNUNET_ATS_SchedulingHandle *sh = cls;
245 * Disconnect from ATS and then reconnect.
247 * @param sh our handle
250 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
254 GNUNET_MQ_destroy (sh->mq);
257 if (NULL != sh->client)
259 GNUNET_CLIENT_disconnect (sh->client);
262 sh->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
269 * Find the session object corresponding to the given session ID.
271 * @param sh our handle
272 * @param session_id current session ID
273 * @param peer peer the session belongs to
274 * @return the session object (or NULL)
276 static struct GNUNET_ATS_AddressRecord *
277 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
279 const struct GNUNET_PeerIdentity *peer)
281 struct GNUNET_ATS_AddressRecord *ar;
283 if (session_id >= sh->session_array_size)
290 ar = sh->session_array[session_id];
296 if (NULL == ar->address)
298 /* address was destroyed in the meantime, this can happen
299 as we communicate asynchronously with the ATS service. */
302 if (0 != memcmp (peer,
304 sizeof (struct GNUNET_PeerIdentity)))
307 force_reconnect (sh);
315 * Get an available session ID.
317 * @param sh our handle
318 * @return an unused slot, but never NOT_FOUND (0)
321 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh)
327 while ( ( (NOT_FOUND == off) ||
328 (NULL != sh->session_array[off % sh->session_array_size]) ) &&
329 (i < sh->session_array_size) )
334 if ( (NOT_FOUND != off) &&
335 (NULL == sh->session_array[off % sh->session_array_size]) )
337 i = sh->session_array_size;
338 GNUNET_array_grow (sh->session_array,
339 sh->session_array_size,
340 sh->session_array_size * 2);
346 * Get the ID for the given session object.
348 * @param sh our handle
349 * @param session session object
350 * @param address the address we are looking for
351 * @return the session id or NOT_FOUND for error
354 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
355 struct Session *session,
356 const struct GNUNET_HELLO_Address *address)
365 for (i = 1; i < sh->session_array_size; i++)
366 if ( (NULL != sh->session_array[i]) &&
367 ( (session == sh->session_array[i]->session) ||
368 (NULL == sh->session_array[i]->session) ) &&
369 (0 == GNUNET_HELLO_address_cmp (address,
370 sh->session_array[i]->address)) )
377 * Release the session slot from the session table (ATS service is
378 * also done using it).
380 * @param sh our handle
381 * @param session_id identifies session that is no longer valid
384 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
387 struct GNUNET_ATS_AddressRecord *ar;
389 if (NOT_FOUND == session_id)
391 if (session_id >= sh->session_array_size)
394 force_reconnect (sh);
397 /* this slot should have been removed from remove_session before */
398 ar = sh->session_array[session_id];
399 if (NULL != ar->session)
402 force_reconnect (sh);
405 GNUNET_HELLO_address_free (ar->address);
407 sh->session_array[session_id] = NULL;
412 * Type of a function to call when we receive a session release
413 * message from the service.
415 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
416 * @param msg message received, NULL on timeout or fatal error
419 process_ats_session_release_message (void *cls,
420 const struct GNUNET_MessageHeader *msg)
422 struct GNUNET_ATS_SchedulingHandle *sh = cls;
423 const struct SessionReleaseMessage *srm;
425 srm = (const struct SessionReleaseMessage *) msg;
426 /* FIXME: peer field in srm not necessary anymore */
428 ntohl (srm->session_id));
433 * Type of a function to call when we receive a address suggestion
434 * message from the service.
436 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
437 * @param msg message received, NULL on timeout or fatal error
440 process_ats_address_suggestion_message (void *cls,
441 const struct GNUNET_MessageHeader *msg)
443 struct GNUNET_ATS_SchedulingHandle *sh = cls;
444 const struct AddressSuggestionMessage *m;
445 const struct GNUNET_ATS_Information *atsi;
446 const char *plugin_address;
447 const char *plugin_name;
448 uint16_t plugin_address_length;
449 uint16_t plugin_name_length;
451 struct GNUNET_HELLO_Address address;
453 struct GNUNET_ATS_AddressRecord *ar;
455 if (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage))
458 force_reconnect (sh);
461 /* FIXME: we have all the address details, no need for ATS
462 to send those back to us any longer! */
463 m = (const struct AddressSuggestionMessage *) msg;
464 ats_count = ntohl (m->ats_count);
465 plugin_address_length = ntohs (m->address_length);
466 atsi = (const struct GNUNET_ATS_Information *) &m[1];
467 plugin_address = (const char *) &atsi[ats_count];
468 plugin_name = &plugin_address[plugin_address_length];
469 plugin_name_length = ntohs (m->plugin_name_length);
470 if ((plugin_address_length + plugin_name_length +
471 ats_count * sizeof (struct GNUNET_ATS_Information) +
472 sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
474 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
475 || (plugin_name[plugin_name_length - 1] != '\0'))
478 force_reconnect (sh);
481 uint32_t session_id = ntohl (m->session_id);
486 force_reconnect (sh);
489 ar = find_session (sh, session_id, &m->peer);
493 force_reconnect (sh);
497 if (NULL == sh->suggest_cb)
499 if ( (GNUNET_YES == ar->in_destroy) &&
500 ( (0 != ntohl (m->bandwidth_out.value__)) ||
501 (0 != ntohl (m->bandwidth_in.value__)) ) )
503 /* ignore suggestion, as this address is dying */
506 address.peer = m->peer;
507 address.address = plugin_address;
508 address.address_length = plugin_address_length;
509 address.transport_name = plugin_name;
510 address.local_info = ntohl(m->address_local_info);
512 if ((s == NULL) && (0 == address.address_length))
514 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
516 GNUNET_i2s (&address.peer),
517 address.transport_name,
518 plugin_address_length,
523 sh->suggest_cb (sh->suggest_cb_cls,
534 * We encountered an error handling the MQ to the
535 * ATS service. Reconnect.
537 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
538 * @param error details about the error
541 error_handler (void *cls,
542 enum GNUNET_MQ_Error error)
544 struct GNUNET_ATS_SchedulingHandle *sh = cls;
546 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
547 "ATS connection died (code %d), reconnecting\n",
549 force_reconnect (sh);
554 * Generate the "AddressUpdateMessage" for the given
557 * @param sh the scheduling handle to use for transmission
558 * @param ar the address to inform the ATS service about
559 * @param msg_type the message type to use when sending the message
561 * FIXME: maybe overloading with msg_type was not the best idea here...
564 send_add_address_message (struct GNUNET_ATS_SchedulingHandle *sh,
565 const struct GNUNET_ATS_AddressRecord *ar,
568 struct GNUNET_MQ_Envelope *ev;
569 struct AddressUpdateMessage *m;
570 struct GNUNET_ATS_Information *am;
576 return; /* disconnected, skip for now */
577 namelen = (NULL == ar->address->transport_name)
579 : strlen (ar->address->transport_name) + 1;
580 msize = ar->address->address_length +
581 ar->ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
583 ev = GNUNET_MQ_msg_extra (m, msize, msg_type);
584 m->ats_count = htonl (ar->ats_count);
585 m->peer = ar->address->peer;
586 m->address_length = htons (ar->address->address_length);
587 m->address_local_info = htonl ((uint32_t) ar->address->local_info);
588 m->plugin_name_length = htons (namelen);
589 m->session_id = htonl (ar->slot);
591 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
592 "Adding address for peer `%s', plugin `%s', session %p id %u\n",
593 GNUNET_i2s (&ar->address->peer),
594 ar->address->transport_name,
597 am = (struct GNUNET_ATS_Information *) &m[1];
600 ar->ats_count * sizeof (struct GNUNET_ATS_Information));
601 pm = (char *) &am[ar->ats_count];
603 ar->address->address,
604 ar->address->address_length);
605 if (NULL != ar->address->transport_name)
606 memcpy (&pm[ar->address->address_length],
607 ar->address->transport_name,
609 GNUNET_MQ_send (sh->mq, ev);
614 * Re-establish the connection to the ATS service.
616 * @param sh handle to use to re-connect.
619 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
621 static const struct GNUNET_MQ_MessageHandler handlers[] =
622 { { &process_ats_session_release_message,
623 GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
624 sizeof (struct SessionReleaseMessage) },
625 { &process_ats_address_suggestion_message,
626 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
629 struct GNUNET_MQ_Envelope *ev;
630 struct ClientStartMessage *init;
632 GNUNET_assert (NULL == sh->client);
633 sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
634 if (NULL == sh->client)
636 force_reconnect (sh);
639 sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
643 ev = GNUNET_MQ_msg (init,
644 GNUNET_MESSAGE_TYPE_ATS_START);
645 init->start_flag = htonl (START_FLAG_SCHEDULING);
646 GNUNET_MQ_send (sh->mq, ev);
647 // FIXME: iterate over addresses...
648 // FIXME: iterate over peermap for address suggestion requests!
653 * Delete all entries from the current network list.
655 * @param sh scheduling handle to clean up
658 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
660 struct ATS_Network *cur;
662 while (NULL != (cur = sh->net_head))
664 GNUNET_CONTAINER_DLL_remove (sh->net_head,
673 * Function invoked for each interface found. Adds the interface's
674 * network addresses to the respective DLL, so we can distinguish
675 * between LAN and WAN.
678 * @param name name of the interface (can be NULL for unknown)
679 * @param isDefault is this presumably the default interface
680 * @param addr address of this interface (can be NULL for unknown or unassigned)
681 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
682 * @param netmask the network mask (can be NULL for unknown or unassigned)
683 * @param addrlen length of the address
684 * @return #GNUNET_OK to continue iteration
687 interface_proc (void *cls,
690 const struct sockaddr *addr,
691 const struct sockaddr *broadcast_addr,
692 const struct sockaddr *netmask,
695 struct GNUNET_ATS_SchedulingHandle *sh = cls;
696 /* Calculate network */
697 struct ATS_Network *net = NULL;
699 /* Skipping IPv4 loopback addresses since we have special check */
700 if (addr->sa_family == AF_INET)
702 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
704 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
707 /* Skipping IPv6 loopback addresses since we have special check */
708 if (addr->sa_family == AF_INET6)
710 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
711 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
715 if (addr->sa_family == AF_INET)
717 const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
718 const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
719 struct sockaddr_in *tmp;
720 struct sockaddr_in network4;
722 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
723 tmp = (struct sockaddr_in *) &net[1];
724 net->network = (struct sockaddr *) &tmp[0];
725 net->netmask = (struct sockaddr *) &tmp[1];
726 net->length = addrlen;
728 memset (&network4, 0, sizeof (network4));
729 network4.sin_family = AF_INET;
730 #if HAVE_SOCKADDR_IN_SIN_LEN
731 network4.sin_len = sizeof (network4);
733 network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
735 memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
736 memcpy (net->network, &network4, sizeof (struct sockaddr_in));
739 if (addr->sa_family == AF_INET6)
741 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
742 const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
743 struct sockaddr_in6 * tmp;
744 struct sockaddr_in6 network6;
746 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
747 tmp = (struct sockaddr_in6 *) &net[1];
748 net->network = (struct sockaddr *) &tmp[0];
749 net->netmask = (struct sockaddr *) &tmp[1];
750 net->length = addrlen;
752 memset (&network6, 0, sizeof (network6));
753 network6.sin6_family = AF_INET6;
754 #if HAVE_SOCKADDR_IN_SIN_LEN
755 network6.sin6_len = sizeof (network6);
758 uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
759 uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
760 uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
761 for (c = 0; c < 4; c++)
762 net_elem[c] = addr_elem[c] & mask_elem[c];
764 memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
765 memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
768 return GNUNET_OK; /* odd / unsupported address family */
772 char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
773 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
774 "Adding network `%s', netmask `%s'\n",
775 GNUNET_a2s ((struct sockaddr *) net->network,
778 GNUNET_free (netmask);
780 GNUNET_CONTAINER_DLL_insert (sh->net_head,
789 * Periodically get list of network addresses from our interfaces.
792 * @param tc Task context
795 get_addresses (void *cls,
796 const struct GNUNET_SCHEDULER_TaskContext *tc)
798 struct GNUNET_ATS_SchedulingHandle *sh = cls;
800 sh->interface_task = NULL;
801 delete_networks (sh);
802 GNUNET_OS_network_interfaces_list (&interface_proc,
804 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
811 * Convert a `enum GNUNET_ATS_Network_Type` to a string
813 * @param net the network type
814 * @return a string or NULL if invalid
817 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
821 case GNUNET_ATS_NET_UNSPECIFIED:
822 return "UNSPECIFIED";
823 case GNUNET_ATS_NET_LOOPBACK:
825 case GNUNET_ATS_NET_LAN:
827 case GNUNET_ATS_NET_WAN:
829 case GNUNET_ATS_NET_WLAN:
831 case GNUNET_ATS_NET_BT:
840 * Convert a ATS property to a string
842 * @param type the property type
843 * @return a string or NULL if invalid
846 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
850 case GNUNET_ATS_ARRAY_TERMINATOR:
852 case GNUNET_ATS_UTILIZATION_OUT:
853 return "UTILIZATION_UP";
854 case GNUNET_ATS_UTILIZATION_IN:
855 return "UTILIZATION_DOWN";
856 case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
857 return "UTILIZATION_PAYLOAD_UP";
858 case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
859 return "UTILIZATION_PAYLOAD_DOWN";
860 case GNUNET_ATS_NETWORK_TYPE:
861 return "NETWORK_TYPE";
862 case GNUNET_ATS_QUALITY_NET_DELAY:
864 case GNUNET_ATS_QUALITY_NET_DISTANCE:
866 case GNUNET_ATS_COST_WAN:
868 case GNUNET_ATS_COST_LAN:
870 case GNUNET_ATS_COST_WLAN:
879 * Returns where the address is located: LAN or WAN or ...
881 * @param sh the scheduling handle
882 * @param addr address
883 * @param addrlen address length
884 * @return type of the network the address belongs to
886 enum GNUNET_ATS_Network_Type
887 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle *sh,
888 const struct sockaddr *addr,
891 struct ATS_Network *cur = sh->net_head;
892 enum GNUNET_ATS_NetworkType type = GNUNET_ATS_NET_UNSPECIFIED;
894 switch (addr->sa_family)
897 type = GNUNET_ATS_NET_LOOPBACK;
901 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
903 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
904 type = GNUNET_ATS_NET_LOOPBACK;
909 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
911 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
912 type = GNUNET_ATS_NET_LOOPBACK;
920 /* Check local networks */
921 while ((NULL != cur) && (GNUNET_ATS_NET_UNSPECIFIED == type))
923 if (addrlen != cur->length)
928 if (addr->sa_family == AF_INET)
930 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
931 const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network;
932 const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask;
934 if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
935 type = GNUNET_ATS_NET_LAN;
937 if (addr->sa_family == AF_INET6)
939 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
940 const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network;
941 const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask;
943 int res = GNUNET_YES;
945 uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
946 uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
947 uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
948 for (c = 0; c < 4; c++)
949 if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
952 if (res == GNUNET_YES)
953 type = GNUNET_ATS_NET_LAN;
958 /* no local network found for this address, default: WAN */
959 if (type == GNUNET_ATS_NET_UNSPECIFIED)
960 type = GNUNET_ATS_NET_WAN;
961 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
962 "ats-scheduling-api",
963 "`%s' is in network `%s'\n",
966 GNUNET_ATS_print_network_type (type));
972 * Initialize the ATS subsystem.
974 * @param cfg configuration to use
975 * @param suggest_cb notification to call whenever the suggestation changed
976 * @param suggest_cb_cls closure for @a suggest_cb
977 * @return ats context
979 struct GNUNET_ATS_SchedulingHandle *
980 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
981 GNUNET_ATS_AddressSuggestionCallback suggest_cb,
982 void *suggest_cb_cls)
984 struct GNUNET_ATS_SchedulingHandle *sh;
986 sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
988 sh->suggest_cb = suggest_cb;
989 sh->suggest_cb_cls = suggest_cb_cls;
990 GNUNET_array_grow (sh->session_array,
991 sh->session_array_size,
993 sh->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
995 GNUNET_OS_network_interfaces_list (&interface_proc,
997 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
1006 * Function called to free all `struct GNUNET_ATS_SuggestHandles`
1010 * @param key the key
1011 * @param value the value to free
1012 * @return #GNUNET_OK (continue to iterate)
1015 free_sug_handle (void *cls,
1016 const struct GNUNET_PeerIdentity *key,
1019 struct GNUNET_ATS_SuggestHandle *cur = value;
1028 * Client is done with ATS scheduling, release resources.
1030 * @param sh handle to release
1033 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1037 GNUNET_MQ_destroy (sh->mq);
1040 if (NULL != sh->client)
1042 GNUNET_CLIENT_disconnect (sh->client);
1045 if (NULL != sh->task)
1047 GNUNET_SCHEDULER_cancel (sh->task);
1050 GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
1053 GNUNET_CONTAINER_multipeermap_destroy (sh->sug_requests);
1054 if (NULL != sh->interface_task)
1056 GNUNET_SCHEDULER_cancel (sh->interface_task);
1057 sh->interface_task = NULL;
1059 delete_networks (sh);
1060 GNUNET_array_grow (sh->session_array,
1061 sh->session_array_size,
1068 * We would like to reset the address suggestion block time for this
1072 * @param peer identity of the peer we want to reset
1075 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1076 const struct GNUNET_PeerIdentity *peer)
1078 struct GNUNET_MQ_Envelope *ev;
1079 struct ResetBackoffMessage *m;
1081 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1082 m->reserved = htonl (0);
1084 GNUNET_MQ_send (sh->mq, ev);
1089 * We would like to receive address suggestions for a peer. ATS will
1090 * respond with a call to the continuation immediately containing an address or
1091 * no address if none is available. ATS can suggest more addresses until we call
1092 * #GNUNET_ATS_suggest_address_cancel().
1095 * @param peer identity of the peer we need an address for
1096 * @return suggest handle, NULL if a request is already pending
1098 struct GNUNET_ATS_SuggestHandle *
1099 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1100 const struct GNUNET_PeerIdentity *peer)
1102 struct GNUNET_MQ_Envelope *ev;
1103 struct RequestAddressMessage *m;
1104 struct GNUNET_ATS_SuggestHandle *s;
1106 s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1109 GNUNET_CONTAINER_multipeermap_put (sh->sug_requests,
1112 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1119 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1120 m->reserved = htonl (0);
1122 GNUNET_MQ_send (sh->mq, ev);
1128 * We would like to stop receiving address updates for this peer
1131 * @param peer identity of the peer
1134 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1135 const struct GNUNET_PeerIdentity *peer)
1137 struct GNUNET_MQ_Envelope *ev;
1138 struct RequestAddressMessage *m;
1139 struct GNUNET_ATS_SuggestHandle *s;
1141 s = GNUNET_CONTAINER_multipeermap_get (sh->sug_requests,
1148 GNUNET_assert (GNUNET_OK ==
1149 GNUNET_CONTAINER_multipeermap_remove (sh->sug_requests,
1155 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1156 m->reserved = htonl (0);
1158 GNUNET_MQ_send (sh->mq, ev);
1163 * Test if a address and a session is known to ATS
1165 * @param sh the scheduling handle
1166 * @param address the address
1167 * @param session the session
1168 * @return #GNUNET_YES or #GNUNET_NO
1171 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1172 const struct GNUNET_HELLO_Address *address,
1173 struct Session *session)
1175 if (NULL == session)
1177 if (NOT_FOUND != find_session_id (sh,
1180 return GNUNET_YES; /* Exists */
1186 * We have a new address ATS should know. Addresses have to be added
1187 * with this function before they can be: updated, set in use and
1191 * @param address the address
1192 * @param session session handle, can be NULL
1193 * @param ats performance data for the address
1194 * @param ats_count number of performance records in @a ats
1195 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1198 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1199 const struct GNUNET_HELLO_Address *address,
1200 struct Session *session,
1201 const struct GNUNET_ATS_Information *ats,
1204 struct GNUNET_ATS_AddressRecord *ar;
1209 if (NULL == address)
1211 /* we need a valid address */
1213 return GNUNET_SYSERR;
1215 namelen = (NULL == address->transport_name)
1217 : strlen (address->transport_name) + 1;
1218 msize = address->address_length +
1219 ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1220 if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1221 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1222 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1224 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1226 /* address too large for us, this should not happen */
1228 return GNUNET_SYSERR;
1231 if (NOT_FOUND != find_session_id (sh, session, address))
1233 /* Already existing, nothing todo, but this should not happen */
1235 return GNUNET_SYSERR;
1237 s = find_empty_session_slot (sh);
1238 ar = GNUNET_new (struct GNUNET_ATS_AddressRecord);
1240 ar->session = session;
1241 ar->address = GNUNET_HELLO_address_copy (address);
1242 GNUNET_array_grow (ar->ats,
1245 memcpy (ar->ats, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1246 sh->session_array[s] = ar;
1247 send_add_address_message (sh, ar, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1253 * We have updated performance statistics for a given address. Note
1254 * that this function can be called for addresses that are currently
1255 * in use as well as addresses that are valid but not actively in use.
1256 * Furthermore, the peer may not even be connected to us right now (in
1257 * which case the call may be ignored or the information may be stored
1258 * for later use). Update bandwidth assignments.
1260 * FIXME: change API so we do not have to do the linear search!
1263 * @param address the address
1264 * @param session session handle, can be NULL
1265 * @param ats performance data for the address
1266 * @param ats_count number of performance records in @a ats
1267 * @return #GNUNET_YES on success, #GNUNET_NO if address or session are unknown,
1268 * #GNUNET_SYSERR on hard failure
1271 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1272 const struct GNUNET_HELLO_Address *address,
1273 struct Session *session,
1274 const struct GNUNET_ATS_Information *ats,
1278 struct GNUNET_ATS_AddressRecord *ar;
1280 s = find_session_id (sh, session, address);
1286 ar = sh->session_array[s];
1287 GNUNET_array_grow (ar->ats,
1292 ats_count * sizeof (struct GNUNET_ATS_Information));
1293 ar->session = session;
1294 send_add_address_message (sh, ar, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1300 * An address is now in use or not used any more.
1303 * @param address the address
1304 * @param session session handle, can be NULL
1305 * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1306 * if address is not used any more
1309 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1310 const struct GNUNET_HELLO_Address *address,
1311 struct Session *session,
1314 struct GNUNET_MQ_Envelope *ev;
1315 struct AddressUseMessage *m;
1316 struct GNUNET_ATS_AddressRecord *ar;
1322 s = find_session_id (sh, session, address);
1325 /* trying to set unknown address to NO */
1329 ar = sh->session_array[s];
1330 ar->in_use = in_use;
1331 namelen = (NULL == address->transport_name)
1333 : strlen (address->transport_name) + 1;
1334 msize = address->address_length + namelen;
1336 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1337 m->peer = address->peer;
1338 m->in_use = htons (in_use);
1339 m->address_length = htons (address->address_length);
1340 m->address_local_info = htonl ((uint32_t) address->local_info);
1341 m->plugin_name_length = htons (namelen);
1343 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1344 "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1345 (GNUNET_YES == in_use) ? "YES" : "NO",
1346 GNUNET_i2s (&address->peer),
1347 address->transport_name,
1350 m->session_id = htonl (s);
1351 pm = (char *) &m[1];
1352 /* FIXME: no need to send the address data */
1353 memcpy (pm, address->address, address->address_length);
1354 memcpy (&pm[address->address_length], address->transport_name, namelen);
1355 GNUNET_MQ_send (sh->mq, ev);
1360 * An address got destroyed, stop including it as a valid address.
1362 * If a session is given, only the session will be removed, if no session is
1363 * given the full address will be deleted.
1365 * FIXME: the above sentence already indicates that this API is a
1366 * mess and troublesome. FIX IT!
1369 * @param address the address
1370 * @param session session handle that is no longer valid, can be NULL
1373 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1374 const struct GNUNET_HELLO_Address *address,
1375 struct Session *session)
1378 struct GNUNET_MQ_Envelope *ev;
1379 struct AddressDestroyedMessage *m;
1380 struct GNUNET_ATS_AddressRecord *ar;
1385 s = find_session_id (sh, session, address);
1391 ar = sh->session_array[s];
1392 if (NULL != session)
1394 /* FIXME: this is yucky, fix API! */
1395 GNUNET_break (ar->session == session);
1401 GNUNET_assert (NULL != address->transport_name);
1402 namelen = strlen (address->transport_name) + 1;
1403 GNUNET_assert (namelen > 1);
1404 msize = address->address_length + namelen;
1405 if ((msize + sizeof (struct AddressDestroyedMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1406 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1407 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1413 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1414 m->peer = address->peer;
1415 m->address_length = htons (address->address_length);
1416 m->address_local_info = htonl ((uint32_t) address->local_info);
1417 m->plugin_name_length = htons (namelen);
1419 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1420 "Deleting address for peer `%s', plugin `%s', session %p\n",
1421 GNUNET_i2s (&address->peer),
1422 address->transport_name,
1425 m->session_id = htonl (s);
1426 pm = (char *) &m[1];
1429 address->address_length);
1430 memcpy (&pm[address->address_length],
1431 address->transport_name,
1433 GNUNET_MQ_send (sh->mq, ev);
1435 ar->in_destroy = GNUNET_YES;
1436 GNUNET_array_grow (ar->ats,
1442 /* end of ats_api_scheduling.c */