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
53 * Scheduling handle this address record belongs to.
55 struct GNUNET_ATS_SchedulingHandle *sh;
60 struct GNUNET_HELLO_Address *address;
63 * Session handle. NULL if we have an address but no
64 * active session for this address.
66 struct Session *session;
69 * Array with performance data about the address.
71 struct GNUNET_ATS_Information *ats;
74 * Number of entries in @e ats.
79 * Which slot (index) in the session array does
80 * this record correspond to? FIXME:
81 * FIXME: a linear search on this is really crappy!
82 * Maybe switch to a 64-bit global counter and be
83 * done with it? Or does that then cause too much
84 * trouble on the ATS-service side?
89 * Is this address currently in use?
90 * FIXME: document what "in use" means, and why it
96 * We're about to destroy this address record, just ATS does
104 * We keep a list of our local networks so we can answer
105 * LAN vs. WAN questions. Note: WLAN is not detected yet.
106 * (maybe we can do that heuristically based on interface
107 * name in the future?)
114 struct ATS_Network *next;
119 struct ATS_Network *prev;
124 struct sockaddr *network;
127 * Netmask to determine what is in the LAN.
129 struct sockaddr *netmask;
132 * How long are @e network and @e netmask?
139 * Handle for ATS address suggestion requests.
141 struct GNUNET_ATS_SuggestHandle
144 * ID of the peer for which address suggestion was requested.
146 struct GNUNET_PeerIdentity id;
151 * Handle to the ATS subsystem for bandwidth/transport scheduling information.
153 struct GNUNET_ATS_SchedulingHandle
159 const struct GNUNET_CONFIGURATION_Handle *cfg;
162 * Callback to invoke on suggestions.
164 GNUNET_ATS_AddressSuggestionCallback suggest_cb;
167 * Closure for @e suggest_cb.
169 void *suggest_cb_cls;
172 * Map with the identities of all the peers for which we would
173 * like to have address suggestions. The key is the PID, the
174 * value is currently the `struct GNUNET_ATS_SuggestHandle`
176 struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
179 * Connection to ATS service.
181 struct GNUNET_CLIENT_Connection *client;
184 * Message queue for sending requests to the ATS service.
186 struct GNUNET_MQ_Handle *mq;
189 * Head of LAN networks list.
191 struct ATS_Network *net_head;
194 * Tail of LAN networks list.
196 struct ATS_Network *net_tail;
199 * Array of session objects (we need to translate them to numbers and back
200 * for the protocol; the offset in the array is the session number on the
201 * network). Index 0 is always NULL and reserved to represent the NULL pointer.
202 * Unused entries are also NULL.
204 struct GNUNET_ATS_AddressRecord **session_array;
207 * Task to trigger reconnect.
209 struct GNUNET_SCHEDULER_Task *task;
212 * Task for periodically refreshing our LAN network list.
214 struct GNUNET_SCHEDULER_Task *interface_task;
217 * Size of the @e session_array.
219 unsigned int session_array_size;
225 * Re-establish the connection to the ATS service.
227 * @param sh handle to use to re-connect.
230 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
234 * Re-establish the connection to the ATS service.
236 * @param cls handle to use to re-connect.
237 * @param tc scheduler context
240 reconnect_task (void *cls,
241 const struct GNUNET_SCHEDULER_TaskContext *tc)
243 struct GNUNET_ATS_SchedulingHandle *sh = cls;
251 * Disconnect from ATS and then reconnect.
253 * @param sh our handle
256 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
260 GNUNET_MQ_destroy (sh->mq);
263 if (NULL != sh->client)
265 GNUNET_CLIENT_disconnect (sh->client);
268 sh->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
275 * Find the session object corresponding to the given session ID.
277 * @param sh our handle
278 * @param session_id current session ID
279 * @param peer peer the session belongs to
280 * @return the session object (or NULL)
282 static struct GNUNET_ATS_AddressRecord *
283 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
285 const struct GNUNET_PeerIdentity *peer)
287 struct GNUNET_ATS_AddressRecord *ar;
289 if (session_id >= sh->session_array_size)
296 ar = sh->session_array[session_id];
302 if (NULL == ar->address)
304 /* address was destroyed in the meantime, this can happen
305 as we communicate asynchronously with the ATS service. */
308 if (0 != memcmp (peer,
310 sizeof (struct GNUNET_PeerIdentity)))
313 force_reconnect (sh);
321 * Get an available session ID.
323 * @param sh our handle
324 * @return an unused slot, but never NOT_FOUND (0)
327 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh)
333 while ( ( (NOT_FOUND == off) ||
334 (NULL != sh->session_array[off % sh->session_array_size]) ) &&
335 (i < sh->session_array_size) )
340 if ( (NOT_FOUND != off % sh->session_array_size) &&
341 (NULL == sh->session_array[off % sh->session_array_size]) )
343 i = sh->session_array_size;
344 GNUNET_array_grow (sh->session_array,
345 sh->session_array_size,
346 sh->session_array_size * 2);
352 * Get the ID for the given session object.
354 * @param sh our handle
355 * @param session session object
356 * @param address the address we are looking for
357 * @return the session id or NOT_FOUND for error
360 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
361 struct Session *session,
362 const struct GNUNET_HELLO_Address *address)
371 for (i = 1; i < sh->session_array_size; i++)
372 if ( (NULL != sh->session_array[i]) &&
373 ( (session == sh->session_array[i]->session) ||
374 (NULL == sh->session_array[i]->session) ) &&
375 (0 == GNUNET_HELLO_address_cmp (address,
376 sh->session_array[i]->address)) )
383 * Release the session slot from the session table (ATS service is
384 * also done using it).
386 * @param sh our handle
387 * @param session_id identifies session that is no longer valid
390 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
393 struct GNUNET_ATS_AddressRecord *ar;
395 if (NOT_FOUND == session_id)
397 if (session_id >= sh->session_array_size)
400 force_reconnect (sh);
403 /* this slot should have been removed from remove_session before */
404 ar = sh->session_array[session_id];
405 if (NULL != ar->session)
408 force_reconnect (sh);
411 GNUNET_HELLO_address_free (ar->address);
413 sh->session_array[session_id] = NULL;
418 * Type of a function to call when we receive a session release
419 * message from the service.
421 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
422 * @param msg message received, NULL on timeout or fatal error
425 process_ats_session_release_message (void *cls,
426 const struct GNUNET_MessageHeader *msg)
428 struct GNUNET_ATS_SchedulingHandle *sh = cls;
429 const struct SessionReleaseMessage *srm;
431 srm = (const struct SessionReleaseMessage *) msg;
432 /* FIXME: peer field in srm not necessary anymore */
434 ntohl (srm->session_id));
439 * Type of a function to call when we receive a address suggestion
440 * message from the service.
442 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
443 * @param msg message received, NULL on timeout or fatal error
446 process_ats_address_suggestion_message (void *cls,
447 const struct GNUNET_MessageHeader *msg)
449 struct GNUNET_ATS_SchedulingHandle *sh = cls;
450 const struct AddressSuggestionMessage *m;
451 const struct GNUNET_ATS_Information *atsi;
452 const char *plugin_address;
453 const char *plugin_name;
454 uint16_t plugin_address_length;
455 uint16_t plugin_name_length;
457 struct GNUNET_HELLO_Address address;
459 struct GNUNET_ATS_AddressRecord *ar;
461 if (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage))
464 force_reconnect (sh);
467 /* FIXME: we have all the address details, no need for ATS
468 to send those back to us any longer! */
469 m = (const struct AddressSuggestionMessage *) msg;
470 ats_count = ntohl (m->ats_count);
471 plugin_address_length = ntohs (m->address_length);
472 atsi = (const struct GNUNET_ATS_Information *) &m[1];
473 plugin_address = (const char *) &atsi[ats_count];
474 plugin_name = &plugin_address[plugin_address_length];
475 plugin_name_length = ntohs (m->plugin_name_length);
476 if ((plugin_address_length + plugin_name_length +
477 ats_count * sizeof (struct GNUNET_ATS_Information) +
478 sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
480 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
481 || (plugin_name[plugin_name_length - 1] != '\0'))
484 force_reconnect (sh);
487 uint32_t session_id = ntohl (m->session_id);
492 force_reconnect (sh);
495 ar = find_session (sh, session_id, &m->peer);
499 force_reconnect (sh);
503 if (NULL == sh->suggest_cb)
505 if ( (GNUNET_YES == ar->in_destroy) &&
506 ( (0 != ntohl (m->bandwidth_out.value__)) ||
507 (0 != ntohl (m->bandwidth_in.value__)) ) )
509 /* ignore suggestion, as this address is dying */
512 address.peer = m->peer;
513 address.address = plugin_address;
514 address.address_length = plugin_address_length;
515 address.transport_name = plugin_name;
516 address.local_info = ntohl(m->address_local_info);
518 if ((s == NULL) && (0 == address.address_length))
520 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
521 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
522 GNUNET_i2s (&address.peer),
523 address.transport_name,
524 plugin_address_length,
529 sh->suggest_cb (sh->suggest_cb_cls,
539 * We encountered an error handling the MQ to the
540 * ATS service. Reconnect.
542 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
543 * @param error details about the error
546 error_handler (void *cls,
547 enum GNUNET_MQ_Error error)
549 struct GNUNET_ATS_SchedulingHandle *sh = cls;
551 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
552 "ATS connection died (code %d), reconnecting\n",
554 force_reconnect (sh);
559 * Generate the "AddressUpdateMessage" for the given
562 * @param sh the scheduling handle to use for transmission
563 * @param ar the address to inform the ATS service about
564 * @param msg_type the message type to use when sending the message
566 * FIXME: maybe overloading with msg_type was not the best idea here...
569 send_add_address_message (struct GNUNET_ATS_SchedulingHandle *sh,
570 const struct GNUNET_ATS_AddressRecord *ar,
573 struct GNUNET_MQ_Envelope *ev;
574 struct AddressUpdateMessage *m;
575 struct GNUNET_ATS_Information *am;
581 return; /* disconnected, skip for now */
582 namelen = (NULL == ar->address->transport_name)
584 : strlen (ar->address->transport_name) + 1;
585 msize = ar->address->address_length +
586 ar->ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
588 ev = GNUNET_MQ_msg_extra (m, msize, msg_type);
589 m->ats_count = htonl (ar->ats_count);
590 m->peer = ar->address->peer;
591 m->address_length = htons (ar->address->address_length);
592 m->address_local_info = htonl ((uint32_t) ar->address->local_info);
593 m->plugin_name_length = htons (namelen);
594 m->session_id = htonl (ar->slot);
596 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
597 "Adding address for peer `%s', plugin `%s', session %p id %u\n",
598 GNUNET_i2s (&ar->address->peer),
599 ar->address->transport_name,
602 am = (struct GNUNET_ATS_Information *) &m[1];
605 ar->ats_count * sizeof (struct GNUNET_ATS_Information));
606 pm = (char *) &am[ar->ats_count];
608 ar->address->address,
609 ar->address->address_length);
610 if (NULL != ar->address->transport_name)
611 memcpy (&pm[ar->address->address_length],
612 ar->address->transport_name,
614 GNUNET_MQ_send (sh->mq, ev);
619 * Re-establish the connection to the ATS service.
621 * @param sh handle to use to re-connect.
624 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
626 static const struct GNUNET_MQ_MessageHandler handlers[] =
627 { { &process_ats_session_release_message,
628 GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
629 sizeof (struct SessionReleaseMessage) },
630 { &process_ats_address_suggestion_message,
631 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
634 struct GNUNET_MQ_Envelope *ev;
635 struct ClientStartMessage *init;
637 GNUNET_assert (NULL == sh->client);
638 sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
639 if (NULL == sh->client)
641 force_reconnect (sh);
644 sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
648 ev = GNUNET_MQ_msg (init,
649 GNUNET_MESSAGE_TYPE_ATS_START);
650 init->start_flag = htonl (START_FLAG_SCHEDULING);
651 GNUNET_MQ_send (sh->mq, ev);
652 // FIXME: iterate over addresses...
653 // FIXME: iterate over peermap for address suggestion requests!
658 * Delete all entries from the current network list.
660 * @param sh scheduling handle to clean up
663 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
665 struct ATS_Network *cur;
667 while (NULL != (cur = sh->net_head))
669 GNUNET_CONTAINER_DLL_remove (sh->net_head,
678 * Function invoked for each interface found. Adds the interface's
679 * network addresses to the respective DLL, so we can distinguish
680 * between LAN and WAN.
683 * @param name name of the interface (can be NULL for unknown)
684 * @param isDefault is this presumably the default interface
685 * @param addr address of this interface (can be NULL for unknown or unassigned)
686 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
687 * @param netmask the network mask (can be NULL for unknown or unassigned)
688 * @param addrlen length of the address
689 * @return #GNUNET_OK to continue iteration
692 interface_proc (void *cls,
695 const struct sockaddr *addr,
696 const struct sockaddr *broadcast_addr,
697 const struct sockaddr *netmask,
700 struct GNUNET_ATS_SchedulingHandle *sh = cls;
701 /* Calculate network */
702 struct ATS_Network *net = NULL;
704 /* Skipping IPv4 loopback addresses since we have special check */
705 if (addr->sa_family == AF_INET)
707 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
709 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
712 /* Skipping IPv6 loopback addresses since we have special check */
713 if (addr->sa_family == AF_INET6)
715 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
716 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
720 if (addr->sa_family == AF_INET)
722 const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
723 const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
724 struct sockaddr_in *tmp;
725 struct sockaddr_in network4;
727 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
728 tmp = (struct sockaddr_in *) &net[1];
729 net->network = (struct sockaddr *) &tmp[0];
730 net->netmask = (struct sockaddr *) &tmp[1];
731 net->length = addrlen;
733 memset (&network4, 0, sizeof (network4));
734 network4.sin_family = AF_INET;
735 #if HAVE_SOCKADDR_IN_SIN_LEN
736 network4.sin_len = sizeof (network4);
738 network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
740 memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
741 memcpy (net->network, &network4, sizeof (struct sockaddr_in));
744 if (addr->sa_family == AF_INET6)
746 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
747 const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
748 struct sockaddr_in6 * tmp;
749 struct sockaddr_in6 network6;
751 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
752 tmp = (struct sockaddr_in6 *) &net[1];
753 net->network = (struct sockaddr *) &tmp[0];
754 net->netmask = (struct sockaddr *) &tmp[1];
755 net->length = addrlen;
757 memset (&network6, 0, sizeof (network6));
758 network6.sin6_family = AF_INET6;
759 #if HAVE_SOCKADDR_IN_SIN_LEN
760 network6.sin6_len = sizeof (network6);
763 uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
764 uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
765 uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
766 for (c = 0; c < 4; c++)
767 net_elem[c] = addr_elem[c] & mask_elem[c];
769 memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
770 memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
773 return GNUNET_OK; /* odd / unsupported address family */
777 char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
778 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
779 "Adding network `%s', netmask `%s'\n",
780 GNUNET_a2s ((struct sockaddr *) net->network,
783 GNUNET_free (netmask);
785 GNUNET_CONTAINER_DLL_insert (sh->net_head,
794 * Periodically get list of network addresses from our interfaces.
797 * @param tc Task context
800 get_addresses (void *cls,
801 const struct GNUNET_SCHEDULER_TaskContext *tc)
803 struct GNUNET_ATS_SchedulingHandle *sh = cls;
805 sh->interface_task = NULL;
806 delete_networks (sh);
807 GNUNET_OS_network_interfaces_list (&interface_proc,
809 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
816 * Convert a `enum GNUNET_ATS_Network_Type` to a string
818 * @param net the network type
819 * @return a string or NULL if invalid
822 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
826 case GNUNET_ATS_NET_UNSPECIFIED:
827 return "UNSPECIFIED";
828 case GNUNET_ATS_NET_LOOPBACK:
830 case GNUNET_ATS_NET_LAN:
832 case GNUNET_ATS_NET_WAN:
834 case GNUNET_ATS_NET_WLAN:
836 case GNUNET_ATS_NET_BT:
845 * Convert a ATS property to a string
847 * @param type the property type
848 * @return a string or NULL if invalid
851 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
855 case GNUNET_ATS_ARRAY_TERMINATOR:
857 case GNUNET_ATS_UTILIZATION_OUT:
858 return "UTILIZATION_UP";
859 case GNUNET_ATS_UTILIZATION_IN:
860 return "UTILIZATION_DOWN";
861 case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
862 return "UTILIZATION_PAYLOAD_UP";
863 case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
864 return "UTILIZATION_PAYLOAD_DOWN";
865 case GNUNET_ATS_NETWORK_TYPE:
866 return "NETWORK_TYPE";
867 case GNUNET_ATS_QUALITY_NET_DELAY:
869 case GNUNET_ATS_QUALITY_NET_DISTANCE:
871 case GNUNET_ATS_COST_WAN:
873 case GNUNET_ATS_COST_LAN:
875 case GNUNET_ATS_COST_WLAN:
884 * Returns where the address is located: LAN or WAN or ...
886 * @param sh the scheduling handle
887 * @param addr address
888 * @param addrlen address length
889 * @return type of the network the address belongs to
891 enum GNUNET_ATS_Network_Type
892 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle *sh,
893 const struct sockaddr *addr,
896 struct ATS_Network *cur = sh->net_head;
897 enum GNUNET_ATS_NetworkType type = GNUNET_ATS_NET_UNSPECIFIED;
899 switch (addr->sa_family)
902 type = GNUNET_ATS_NET_LOOPBACK;
906 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
908 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
909 type = GNUNET_ATS_NET_LOOPBACK;
914 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
916 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
917 type = GNUNET_ATS_NET_LOOPBACK;
925 /* Check local networks */
926 while ((NULL != cur) && (GNUNET_ATS_NET_UNSPECIFIED == type))
928 if (addrlen != cur->length)
933 if (addr->sa_family == AF_INET)
935 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
936 const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network;
937 const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask;
939 if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
940 type = GNUNET_ATS_NET_LAN;
942 if (addr->sa_family == AF_INET6)
944 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
945 const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network;
946 const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask;
948 int res = GNUNET_YES;
950 uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
951 uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
952 uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
953 for (c = 0; c < 4; c++)
954 if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
957 if (res == GNUNET_YES)
958 type = GNUNET_ATS_NET_LAN;
963 /* no local network found for this address, default: WAN */
964 if (type == GNUNET_ATS_NET_UNSPECIFIED)
965 type = GNUNET_ATS_NET_WAN;
966 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
967 "ats-scheduling-api",
968 "`%s' is in network `%s'\n",
971 GNUNET_ATS_print_network_type (type));
977 * Initialize the ATS subsystem.
979 * @param cfg configuration to use
980 * @param suggest_cb notification to call whenever the suggestation changed
981 * @param suggest_cb_cls closure for @a suggest_cb
982 * @return ats context
984 struct GNUNET_ATS_SchedulingHandle *
985 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
986 GNUNET_ATS_AddressSuggestionCallback suggest_cb,
987 void *suggest_cb_cls)
989 struct GNUNET_ATS_SchedulingHandle *sh;
991 sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
993 sh->suggest_cb = suggest_cb;
994 sh->suggest_cb_cls = suggest_cb_cls;
995 GNUNET_array_grow (sh->session_array,
996 sh->session_array_size,
998 sh->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
1000 GNUNET_OS_network_interfaces_list (&interface_proc,
1002 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
1011 * Function called to free all `struct GNUNET_ATS_SuggestHandles`
1015 * @param key the key
1016 * @param value the value to free
1017 * @return #GNUNET_OK (continue to iterate)
1020 free_sug_handle (void *cls,
1021 const struct GNUNET_PeerIdentity *key,
1024 struct GNUNET_ATS_SuggestHandle *cur = value;
1033 * Client is done with ATS scheduling, release resources.
1035 * @param sh handle to release
1038 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1042 GNUNET_MQ_destroy (sh->mq);
1045 if (NULL != sh->client)
1047 GNUNET_CLIENT_disconnect (sh->client);
1050 if (NULL != sh->task)
1052 GNUNET_SCHEDULER_cancel (sh->task);
1055 GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
1058 GNUNET_CONTAINER_multipeermap_destroy (sh->sug_requests);
1059 if (NULL != sh->interface_task)
1061 GNUNET_SCHEDULER_cancel (sh->interface_task);
1062 sh->interface_task = NULL;
1064 delete_networks (sh);
1065 GNUNET_array_grow (sh->session_array,
1066 sh->session_array_size,
1073 * We would like to reset the address suggestion block time for this
1077 * @param peer identity of the peer we want to reset
1080 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1081 const struct GNUNET_PeerIdentity *peer)
1083 struct GNUNET_MQ_Envelope *ev;
1084 struct ResetBackoffMessage *m;
1086 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1087 m->reserved = htonl (0);
1089 GNUNET_MQ_send (sh->mq, ev);
1094 * We would like to receive address suggestions for a peer. ATS will
1095 * respond with a call to the continuation immediately containing an address or
1096 * no address if none is available. ATS can suggest more addresses until we call
1097 * #GNUNET_ATS_suggest_address_cancel().
1100 * @param peer identity of the peer we need an address for
1101 * @return suggest handle, NULL if a request is already pending
1103 struct GNUNET_ATS_SuggestHandle *
1104 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1105 const struct GNUNET_PeerIdentity *peer)
1107 struct GNUNET_MQ_Envelope *ev;
1108 struct RequestAddressMessage *m;
1109 struct GNUNET_ATS_SuggestHandle *s;
1111 s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1114 GNUNET_CONTAINER_multipeermap_put (sh->sug_requests,
1117 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1124 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1125 m->reserved = htonl (0);
1127 GNUNET_MQ_send (sh->mq, ev);
1133 * We would like to stop receiving address updates for this peer
1136 * @param peer identity of the peer
1139 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1140 const struct GNUNET_PeerIdentity *peer)
1142 struct GNUNET_MQ_Envelope *ev;
1143 struct RequestAddressMessage *m;
1144 struct GNUNET_ATS_SuggestHandle *s;
1146 s = GNUNET_CONTAINER_multipeermap_get (sh->sug_requests,
1153 GNUNET_assert (GNUNET_OK ==
1154 GNUNET_CONTAINER_multipeermap_remove (sh->sug_requests,
1160 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1161 m->reserved = htonl (0);
1163 GNUNET_MQ_send (sh->mq, ev);
1168 * Test if a address and a session is known to ATS
1170 * @param sh the scheduling handle
1171 * @param address the address
1172 * @param session the session
1173 * @return #GNUNET_YES or #GNUNET_NO
1176 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1177 const struct GNUNET_HELLO_Address *address,
1178 struct Session *session)
1180 if (NULL == session)
1182 if (NOT_FOUND != find_session_id (sh,
1185 return GNUNET_YES; /* Exists */
1191 * We have a new address ATS should know. Addresses have to be added
1192 * with this function before they can be: updated, set in use and
1196 * @param address the address
1197 * @param session session handle, can be NULL
1198 * @param ats performance data for the address
1199 * @param ats_count number of performance records in @a ats
1200 * @return handle to the address representation inside ATS, NULL
1201 * on error (i.e. ATS knows this exact address already)
1203 struct GNUNET_ATS_AddressRecord *
1204 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1205 const struct GNUNET_HELLO_Address *address,
1206 struct Session *session,
1207 const struct GNUNET_ATS_Information *ats,
1210 struct GNUNET_ATS_AddressRecord *ar;
1215 if (NULL == address)
1217 /* we need a valid address */
1221 namelen = (NULL == address->transport_name)
1223 : strlen (address->transport_name) + 1;
1224 msize = address->address_length +
1225 ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1226 if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1227 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1228 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1230 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1232 /* address too large for us, this should not happen */
1237 if (NOT_FOUND != find_session_id (sh, session, address))
1239 /* Already existing, nothing todo, but this should not happen */
1243 s = find_empty_session_slot (sh);
1244 ar = GNUNET_new (struct GNUNET_ATS_AddressRecord);
1247 ar->session = session;
1248 ar->address = GNUNET_HELLO_address_copy (address);
1249 GNUNET_array_grow (ar->ats,
1252 memcpy (ar->ats, ats, ats_count * sizeof (struct GNUNET_ATS_Information));
1253 sh->session_array[s] = ar;
1254 send_add_address_message (sh, ar, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1260 * We have updated performance statistics for a given address. Note
1261 * that this function can be called for addresses that are currently
1262 * in use as well as addresses that are valid but not actively in use.
1263 * Furthermore, the peer may not even be connected to us right now (in
1264 * which case the call may be ignored or the information may be stored
1265 * for later use). Update bandwidth assignments.
1267 * @param ar address record to update information for
1268 * @param session session handle, can be NULL
1269 * @param ats performance data for the address
1270 * @param ats_count number of performance records in @a ats
1273 GNUNET_ATS_address_update (struct GNUNET_ATS_AddressRecord *ar,
1274 struct Session *session,
1275 const struct GNUNET_ATS_Information *ats,
1278 GNUNET_array_grow (ar->ats,
1283 ats_count * sizeof (struct GNUNET_ATS_Information));
1284 ar->session = session;
1285 send_add_address_message (ar->sh,
1287 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1292 * An address is now in use or not used any more.
1295 * @param address the address
1296 * @param session session handle, can be NULL
1297 * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1298 * if address is not used any more
1301 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1302 const struct GNUNET_HELLO_Address *address,
1303 struct Session *session,
1306 struct GNUNET_MQ_Envelope *ev;
1307 struct AddressUseMessage *m;
1308 struct GNUNET_ATS_AddressRecord *ar;
1314 s = find_session_id (sh, session, address);
1317 /* trying to set unknown address to NO */
1321 ar = sh->session_array[s];
1322 ar->in_use = in_use;
1323 namelen = (NULL == address->transport_name)
1325 : strlen (address->transport_name) + 1;
1326 msize = address->address_length + namelen;
1328 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1329 m->peer = address->peer;
1330 m->in_use = htons (in_use);
1331 m->address_length = htons (address->address_length);
1332 m->address_local_info = htonl ((uint32_t) address->local_info);
1333 m->plugin_name_length = htons (namelen);
1335 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1336 "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1337 (GNUNET_YES == in_use) ? "YES" : "NO",
1338 GNUNET_i2s (&address->peer),
1339 address->transport_name,
1342 m->session_id = htonl (s);
1343 pm = (char *) &m[1];
1344 /* FIXME: no need to send the address data */
1345 memcpy (pm, address->address, address->address_length);
1346 memcpy (&pm[address->address_length], address->transport_name, namelen);
1347 GNUNET_MQ_send (sh->mq, ev);
1352 * An address got destroyed, stop including it as a valid address.
1354 * If a session is given, only the session will be removed, if no session is
1355 * given the full address will be deleted.
1357 * FIXME: the above sentence already indicates that this API is a
1358 * mess and troublesome. FIX IT!
1361 * @param address the address
1362 * @param session session handle that is no longer valid, can be NULL
1365 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1366 const struct GNUNET_HELLO_Address *address,
1367 struct Session *session)
1370 struct GNUNET_MQ_Envelope *ev;
1371 struct AddressDestroyedMessage *m;
1372 struct GNUNET_ATS_AddressRecord *ar;
1377 s = find_session_id (sh, session, address);
1383 ar = sh->session_array[s];
1384 if (NULL != session)
1386 /* FIXME: this is yucky, fix API! */
1387 GNUNET_break (ar->session == session);
1393 GNUNET_assert (NULL != address->transport_name);
1394 namelen = strlen (address->transport_name) + 1;
1395 GNUNET_assert (namelen > 1);
1396 msize = address->address_length + namelen;
1397 if ((msize + sizeof (struct AddressDestroyedMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1398 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1399 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1405 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1406 m->peer = address->peer;
1407 m->address_length = htons (address->address_length);
1408 m->address_local_info = htonl ((uint32_t) address->local_info);
1409 m->plugin_name_length = htons (namelen);
1411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1412 "Deleting address for peer `%s', plugin `%s', session %p\n",
1413 GNUNET_i2s (&address->peer),
1414 address->transport_name,
1417 m->session_id = htonl (s);
1418 pm = (char *) &m[1];
1421 address->address_length);
1422 memcpy (&pm[address->address_length],
1423 address->transport_name,
1425 GNUNET_MQ_send (sh->mq, ev);
1427 ar->in_destroy = GNUNET_YES;
1428 GNUNET_array_grow (ar->ats,
1434 /* end of ats_api_scheduling.c */