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 session.
48 * Identity of the peer (just needed for error checking).
50 struct GNUNET_PeerIdentity peer;
55 struct Session *session;
58 * Set to #GNUNET_YES if the slot is used.
65 * We keep a list of our local networks so we can answer
66 * LAN vs. WAN questions. Note: WLAN is not detected yet.
67 * (maybe we can do that heuristically based on interface
68 * name in the future?)
75 struct ATS_Network *next;
80 struct ATS_Network *prev;
85 struct sockaddr *network;
88 * Netmask to determine what is in the LAN.
90 struct sockaddr *netmask;
93 * How long are @e network and @e netmask?
100 * Handle for ATS address suggestion requests.
102 struct GNUNET_ATS_SuggestHandle
105 * ID of the peer for which address suggestion was requested.
107 struct GNUNET_PeerIdentity id;
112 * Handle to the ATS subsystem for bandwidth/transport scheduling information.
114 struct GNUNET_ATS_SchedulingHandle
120 const struct GNUNET_CONFIGURATION_Handle *cfg;
123 * Callback to invoke on suggestions.
125 GNUNET_ATS_AddressSuggestionCallback suggest_cb;
128 * Closure for @e suggest_cb.
130 void *suggest_cb_cls;
133 * Map with the identities of all the peers for which we would
134 * like to have address suggestions. The key is the PID, the
135 * value is currently the `struct GNUNET_ATS_SuggestHandle`
137 struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
140 * Connection to ATS service.
142 struct GNUNET_CLIENT_Connection *client;
145 * Message queue for sending requests to the ATS service.
147 struct GNUNET_MQ_Handle *mq;
150 * Head of LAN networks list.
152 struct ATS_Network *net_head;
155 * Tail of LAN networks list.
157 struct ATS_Network *net_tail;
160 * Array of session objects (we need to translate them to numbers and back
161 * for the protocol; the offset in the array is the session number on the
162 * network). Index 0 is always NULL and reserved to represent the NULL pointer.
163 * Unused entries are also NULL.
165 struct SessionRecord *session_array;
168 * Task to trigger reconnect.
170 struct GNUNET_SCHEDULER_Task *task;
173 * Task for periodically refreshing our LAN network list.
175 struct GNUNET_SCHEDULER_Task *interface_task;
178 * Size of the @e session_array.
180 unsigned int session_array_size;
186 * Re-establish the connection to the ATS service.
188 * @param sh handle to use to re-connect.
191 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
195 * Re-establish the connection to the ATS service.
197 * @param cls handle to use to re-connect.
198 * @param tc scheduler context
201 reconnect_task (void *cls,
202 const struct GNUNET_SCHEDULER_TaskContext *tc)
204 struct GNUNET_ATS_SchedulingHandle *sh = cls;
212 * Disconnect from ATS and then reconnect.
214 * @param sh our handle
217 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
221 GNUNET_MQ_destroy (sh->mq);
224 if (NULL != sh->client)
226 GNUNET_CLIENT_disconnect (sh->client);
229 sh->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
236 * Find the session object corresponding to the given session ID.
238 * @param sh our handle
239 * @param session_id current session ID
240 * @param peer peer the session belongs to
241 * @return the session object (or NULL)
243 static struct Session *
244 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
246 const struct GNUNET_PeerIdentity *peer)
248 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
249 "Find session %u from peer %s in %p\n",
250 (unsigned int) session_id, GNUNET_i2s (peer), sh);
252 if (session_id >= sh->session_array_size)
259 if (sh->session_array[session_id].session == NULL)
262 memcmp (peer, &sh->session_array[session_id].peer,
263 sizeof (struct GNUNET_PeerIdentity)));
268 memcmp (peer, &sh->session_array[session_id].peer,
269 sizeof (struct GNUNET_PeerIdentity)))
272 force_reconnect (sh);
275 /* This check exploits the fact that first field of a session object
279 memcmp (peer, sh->session_array[session_id].session,
280 sizeof (struct GNUNET_PeerIdentity)))
282 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
283 "ats-scheduling-api",
284 "Session %p belongs to peer `%s'\n",
285 sh->session_array[session_id].session,
286 GNUNET_i2s_full ((struct GNUNET_PeerIdentity *) &sh->session_array[session_id].peer));
289 sh->reconnect = GNUNET_YES;
293 return sh->session_array[session_id].session;
298 * Get an available session ID for the given session object.
300 * @param sh our handle
301 * @param session session object
302 * @param peer peer the session belongs to
303 * @return the session id
306 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh,
307 struct Session *session,
308 const struct GNUNET_PeerIdentity *peer)
313 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
314 "ats-scheduling-api",
315 "Get session ID for session %p from peer %s in %p\n",
322 for (i = 1; i < sh->session_array_size; i++)
324 if ((f == 0) && (sh->session_array[i].slot_used == GNUNET_NO))
329 f = sh->session_array_size;
330 GNUNET_array_grow (sh->session_array, sh->session_array_size,
331 sh->session_array_size * 2);
333 GNUNET_assert (f > 0);
334 sh->session_array[f].session = session;
335 sh->session_array[f].peer = *peer;
336 sh->session_array[f].slot_used = GNUNET_YES;
338 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
339 "Assigning session ID %u for session %p of peer %s in %p\n", f,
340 session, GNUNET_i2s (peer), sh);
347 * Get the ID for the given session object.
349 * @param sh our handle
350 * @param session session object
351 * @param peer peer the session belongs to
352 * @return the session id or NOT_FOUND for error
355 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
356 struct Session *session,
357 const struct GNUNET_PeerIdentity *peer)
362 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
363 "Get session ID for session %p from peer %s in %p\n", session,
364 GNUNET_i2s (peer), sh);
368 for (i = 1; i < sh->session_array_size; i++)
370 if (session == sh->session_array[i].session)
372 if (0 != memcmp (peer, &sh->session_array[i].peer,
373 sizeof (struct GNUNET_PeerIdentity)))
375 p2 = strdup (GNUNET_i2s (&sh->session_array[i].peer));
376 GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "ats-scheduling-api",
377 "Session %p did not match: old session was for peer `%s' new session is for `%s'\n",
378 session, GNUNET_i2s (peer), p2);
390 * Remove the session of the given session ID from the session
391 * table (it is no longer valid).
393 * @param sh our handle
394 * @param session_id identifies session that is no longer valid
395 * @param peer peer the session belongs to
398 remove_session (struct GNUNET_ATS_SchedulingHandle *sh,
400 const struct GNUNET_PeerIdentity *peer)
402 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
403 "ats-scheduling-api",
404 "Release sessionID %u from peer %s in %p\n",
405 (unsigned int) session_id,
412 GNUNET_assert (session_id < sh->session_array_size);
413 GNUNET_assert (GNUNET_YES == sh->session_array[session_id].slot_used);
414 GNUNET_assert (0 == memcmp (peer,
415 &sh->session_array[session_id].peer,
416 sizeof (struct GNUNET_PeerIdentity)));
417 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "ats-scheduling-api",
418 "Session %p for peer `%s' removed from slot %u \n",
419 sh->session_array[session_id].session,
422 sh->session_array[session_id].session = NULL;
427 * Release the session slot from the session table (ATS service is
428 * also done using it).
430 * @param sh our handle
431 * @param session_id identifies session that is no longer valid
432 * @param peer peer the session belongs to
435 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
437 const struct GNUNET_PeerIdentity *peer)
439 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
440 "ats-scheduling-api",
441 "Release sessionID %u from peer %s in %p\n",
442 (unsigned int) session_id,
445 if (session_id >= sh->session_array_size)
448 force_reconnect (sh);
451 /* this slot should have been removed from remove_session before */
452 GNUNET_assert (sh->session_array[session_id].session == NULL);
455 memcmp (peer, &sh->session_array[session_id].peer,
456 sizeof (struct GNUNET_PeerIdentity)))
459 force_reconnect (sh);
462 sh->session_array[session_id].slot_used = GNUNET_NO;
463 memset (&sh->session_array[session_id].peer,
465 sizeof (struct GNUNET_PeerIdentity));
470 * Type of a function to call when we receive a session release
471 * message from the service.
473 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
474 * @param msg message received, NULL on timeout or fatal error
477 process_ats_session_release_message (void *cls,
478 const struct GNUNET_MessageHeader *msg)
480 struct GNUNET_ATS_SchedulingHandle *sh = cls;
481 const struct SessionReleaseMessage *srm;
483 srm = (const struct SessionReleaseMessage *) msg;
486 ntohl (srm->session_id),
492 * Type of a function to call when we receive a address suggestion
493 * message from the service.
495 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
496 * @param msg message received, NULL on timeout or fatal error
499 process_ats_address_suggestion_message (void *cls,
500 const struct GNUNET_MessageHeader *msg)
502 struct GNUNET_ATS_SchedulingHandle *sh = cls;
503 const struct AddressSuggestionMessage *m;
504 const struct GNUNET_ATS_Information *atsi;
505 const char *plugin_address;
506 const char *plugin_name;
507 uint16_t plugin_address_length;
508 uint16_t plugin_name_length;
510 struct GNUNET_HELLO_Address address;
513 if (ntohs (msg->size) <= sizeof (struct AddressSuggestionMessage))
516 force_reconnect (sh);
519 m = (const struct AddressSuggestionMessage *) msg;
520 ats_count = ntohl (m->ats_count);
521 plugin_address_length = ntohs (m->address_length);
522 atsi = (const struct GNUNET_ATS_Information *) &m[1];
523 plugin_address = (const char *) &atsi[ats_count];
524 plugin_name = &plugin_address[plugin_address_length];
525 plugin_name_length = ntohs (m->plugin_name_length);
526 if ((plugin_address_length + plugin_name_length +
527 ats_count * sizeof (struct GNUNET_ATS_Information) +
528 sizeof (struct AddressSuggestionMessage) != ntohs (msg->size)) ||
530 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information))
531 || (plugin_name[plugin_name_length - 1] != '\0'))
534 force_reconnect (sh);
537 uint32_t session_id = ntohl (m->session_id);
543 s = find_session (sh, session_id, &m->peer);
546 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
547 "ats-scheduling-api",
548 "ATS tries to use outdated session `%s'\n",
549 GNUNET_i2s (&m->peer));
553 if (NULL == sh->suggest_cb)
555 address.peer = m->peer;
556 address.address = plugin_address;
557 address.address_length = plugin_address_length;
558 address.transport_name = plugin_name;
559 address.local_info = ntohl(m->address_local_info);
561 if ((s == NULL) && (0 == address.address_length))
563 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
564 "ATS returned invalid address for peer `%s' transport `%s' address length %i, session_id %i\n",
565 GNUNET_i2s (&address.peer),
566 address.transport_name,
567 plugin_address_length,
572 sh->suggest_cb (sh->suggest_cb_cls,
583 * We encountered an error handling the MQ to the
584 * ATS service. Reconnect.
586 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
587 * @param error details about the error
590 error_handler (void *cls,
591 enum GNUNET_MQ_Error error)
593 struct GNUNET_ATS_SchedulingHandle *sh = cls;
595 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
596 "ATS connection died (code %d), reconnecting\n",
598 force_reconnect (sh);
603 * Re-establish the connection to the ATS service.
605 * @param sh handle to use to re-connect.
608 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
610 static const struct GNUNET_MQ_MessageHandler handlers[] =
611 { { &process_ats_session_release_message,
612 GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
613 sizeof (struct SessionReleaseMessage) },
614 { &process_ats_address_suggestion_message,
615 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
618 struct GNUNET_MQ_Envelope *ev;
619 struct ClientStartMessage *init;
621 GNUNET_assert (NULL == sh->client);
622 sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
623 if (NULL == sh->client)
625 force_reconnect (sh);
628 sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
632 ev = GNUNET_MQ_msg (init,
633 GNUNET_MESSAGE_TYPE_ATS_START);
634 init->start_flag = htonl (START_FLAG_SCHEDULING);
635 GNUNET_MQ_send (sh->mq, ev);
636 // FIXME: iterate over addresses...
637 // FIXME: iterate over peermap for address suggestion requests!
642 * Delete all entries from the current network list.
644 * @param sh scheduling handle to clean up
647 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
649 struct ATS_Network *cur;
651 while (NULL != (cur = sh->net_head))
653 GNUNET_CONTAINER_DLL_remove (sh->net_head,
662 * Function invoked for each interface found. Adds the interface's
663 * network addresses to the respective DLL, so we can distinguish
664 * between LAN and WAN.
667 * @param name name of the interface (can be NULL for unknown)
668 * @param isDefault is this presumably the default interface
669 * @param addr address of this interface (can be NULL for unknown or unassigned)
670 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
671 * @param netmask the network mask (can be NULL for unknown or unassigned)
672 * @param addrlen length of the address
673 * @return #GNUNET_OK to continue iteration
676 interface_proc (void *cls,
679 const struct sockaddr *addr,
680 const struct sockaddr *broadcast_addr,
681 const struct sockaddr *netmask,
684 struct GNUNET_ATS_SchedulingHandle *sh = cls;
685 /* Calculate network */
686 struct ATS_Network *net = NULL;
688 /* Skipping IPv4 loopback addresses since we have special check */
689 if (addr->sa_family == AF_INET)
691 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
693 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
696 /* Skipping IPv6 loopback addresses since we have special check */
697 if (addr->sa_family == AF_INET6)
699 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
700 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
704 if (addr->sa_family == AF_INET)
706 const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
707 const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
708 struct sockaddr_in *tmp;
709 struct sockaddr_in network4;
711 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
712 tmp = (struct sockaddr_in *) &net[1];
713 net->network = (struct sockaddr *) &tmp[0];
714 net->netmask = (struct sockaddr *) &tmp[1];
715 net->length = addrlen;
717 memset (&network4, 0, sizeof (network4));
718 network4.sin_family = AF_INET;
719 #if HAVE_SOCKADDR_IN_SIN_LEN
720 network4.sin_len = sizeof (network4);
722 network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
724 memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
725 memcpy (net->network, &network4, sizeof (struct sockaddr_in));
728 if (addr->sa_family == AF_INET6)
730 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
731 const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
732 struct sockaddr_in6 * tmp;
733 struct sockaddr_in6 network6;
735 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
736 tmp = (struct sockaddr_in6 *) &net[1];
737 net->network = (struct sockaddr *) &tmp[0];
738 net->netmask = (struct sockaddr *) &tmp[1];
739 net->length = addrlen;
741 memset (&network6, 0, sizeof (network6));
742 network6.sin6_family = AF_INET6;
743 #if HAVE_SOCKADDR_IN_SIN_LEN
744 network6.sin6_len = sizeof (network6);
747 uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
748 uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
749 uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
750 for (c = 0; c < 4; c++)
751 net_elem[c] = addr_elem[c] & mask_elem[c];
753 memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
754 memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
757 return GNUNET_OK; /* odd / unsupported address family */
761 char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
762 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
763 "Adding network `%s', netmask `%s'\n",
764 GNUNET_a2s ((struct sockaddr *) net->network,
767 GNUNET_free (netmask);
769 GNUNET_CONTAINER_DLL_insert (sh->net_head,
778 * Periodically get list of network addresses from our interfaces.
781 * @param tc Task context
784 get_addresses (void *cls,
785 const struct GNUNET_SCHEDULER_TaskContext *tc)
787 struct GNUNET_ATS_SchedulingHandle *sh = cls;
789 sh->interface_task = NULL;
790 delete_networks (sh);
791 GNUNET_OS_network_interfaces_list (&interface_proc,
793 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
800 * Convert a `enum GNUNET_ATS_Network_Type` to a string
802 * @param net the network type
803 * @return a string or NULL if invalid
806 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
810 case GNUNET_ATS_NET_UNSPECIFIED:
811 return "UNSPECIFIED";
812 case GNUNET_ATS_NET_LOOPBACK:
814 case GNUNET_ATS_NET_LAN:
816 case GNUNET_ATS_NET_WAN:
818 case GNUNET_ATS_NET_WLAN:
820 case GNUNET_ATS_NET_BT:
829 * Convert a ATS property to a string
831 * @param type the property type
832 * @return a string or NULL if invalid
835 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
839 case GNUNET_ATS_ARRAY_TERMINATOR:
841 case GNUNET_ATS_UTILIZATION_OUT:
842 return "UTILIZATION_UP";
843 case GNUNET_ATS_UTILIZATION_IN:
844 return "UTILIZATION_DOWN";
845 case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
846 return "UTILIZATION_PAYLOAD_UP";
847 case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
848 return "UTILIZATION_PAYLOAD_DOWN";
849 case GNUNET_ATS_NETWORK_TYPE:
850 return "NETWORK_TYPE";
851 case GNUNET_ATS_QUALITY_NET_DELAY:
853 case GNUNET_ATS_QUALITY_NET_DISTANCE:
855 case GNUNET_ATS_COST_WAN:
857 case GNUNET_ATS_COST_LAN:
859 case GNUNET_ATS_COST_WLAN:
868 * Returns where the address is located: LAN or WAN or ...
870 * @param sh the scheduling handle
871 * @param addr address
872 * @param addrlen address length
873 * @return type of the network the address belongs to
875 enum GNUNET_ATS_Network_Type
876 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle *sh,
877 const struct sockaddr *addr,
880 struct ATS_Network *cur = sh->net_head;
881 enum GNUNET_ATS_NetworkType type = GNUNET_ATS_NET_UNSPECIFIED;
883 switch (addr->sa_family)
886 type = GNUNET_ATS_NET_LOOPBACK;
890 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
892 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
893 type = GNUNET_ATS_NET_LOOPBACK;
898 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
900 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
901 type = GNUNET_ATS_NET_LOOPBACK;
909 /* Check local networks */
910 while ((NULL != cur) && (GNUNET_ATS_NET_UNSPECIFIED == type))
912 if (addrlen != cur->length)
917 if (addr->sa_family == AF_INET)
919 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
920 const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network;
921 const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask;
923 if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
924 type = GNUNET_ATS_NET_LAN;
926 if (addr->sa_family == AF_INET6)
928 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
929 const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network;
930 const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask;
932 int res = GNUNET_YES;
934 uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
935 uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
936 uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
937 for (c = 0; c < 4; c++)
938 if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
941 if (res == GNUNET_YES)
942 type = GNUNET_ATS_NET_LAN;
947 /* no local network found for this address, default: WAN */
948 if (type == GNUNET_ATS_NET_UNSPECIFIED)
949 type = GNUNET_ATS_NET_WAN;
950 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
951 "ats-scheduling-api",
952 "`%s' is in network `%s'\n",
955 GNUNET_ATS_print_network_type (type));
961 * Initialize the ATS subsystem.
963 * @param cfg configuration to use
964 * @param suggest_cb notification to call whenever the suggestation changed
965 * @param suggest_cb_cls closure for @a suggest_cb
966 * @return ats context
968 struct GNUNET_ATS_SchedulingHandle *
969 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
970 GNUNET_ATS_AddressSuggestionCallback suggest_cb,
971 void *suggest_cb_cls)
973 struct GNUNET_ATS_SchedulingHandle *sh;
975 sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
977 sh->suggest_cb = suggest_cb;
978 sh->suggest_cb_cls = suggest_cb_cls;
979 GNUNET_array_grow (sh->session_array,
980 sh->session_array_size,
982 sh->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
984 GNUNET_OS_network_interfaces_list (&interface_proc,
986 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
995 * Function called to free all `struct GNUNET_ATS_SuggestHandles`
1000 * @param value the value to free
1001 * @return #GNUNET_OK (continue to iterate)
1004 free_sug_handle (void *cls,
1005 const struct GNUNET_PeerIdentity *key,
1008 struct GNUNET_ATS_SuggestHandle *cur = value;
1017 * Client is done with ATS scheduling, release resources.
1019 * @param sh handle to release
1022 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1026 GNUNET_MQ_destroy (sh->mq);
1029 if (NULL != sh->client)
1031 GNUNET_CLIENT_disconnect (sh->client);
1034 if (NULL != sh->task)
1036 GNUNET_SCHEDULER_cancel (sh->task);
1039 GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
1042 GNUNET_CONTAINER_multipeermap_destroy (sh->sug_requests);
1043 if (NULL != sh->interface_task)
1045 GNUNET_SCHEDULER_cancel (sh->interface_task);
1046 sh->interface_task = NULL;
1048 delete_networks (sh);
1049 GNUNET_array_grow (sh->session_array,
1050 sh->session_array_size,
1057 * We would like to reset the address suggestion block time for this
1061 * @param peer identity of the peer we want to reset
1064 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1065 const struct GNUNET_PeerIdentity *peer)
1067 struct GNUNET_MQ_Envelope *ev;
1068 struct ResetBackoffMessage *m;
1070 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1071 m->reserved = htonl (0);
1073 GNUNET_MQ_send (sh->mq, ev);
1078 * We would like to receive address suggestions for a peer. ATS will
1079 * respond with a call to the continuation immediately containing an address or
1080 * no address if none is available. ATS can suggest more addresses until we call
1081 * #GNUNET_ATS_suggest_address_cancel().
1084 * @param peer identity of the peer we need an address for
1085 * @return suggest handle
1087 struct GNUNET_ATS_SuggestHandle *
1088 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1089 const struct GNUNET_PeerIdentity *peer)
1091 struct GNUNET_MQ_Envelope *ev;
1092 struct RequestAddressMessage *m;
1093 struct GNUNET_ATS_SuggestHandle *s;
1095 s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1097 GNUNET_break (GNUNET_OK ==
1098 GNUNET_CONTAINER_multipeermap_put (sh->sug_requests,
1101 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1104 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
1105 m->reserved = htonl (0);
1107 GNUNET_MQ_send (sh->mq, ev);
1113 * We would like to stop receiving address updates for this peer
1116 * @param peer identity of the peer
1119 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1120 const struct GNUNET_PeerIdentity *peer)
1122 struct GNUNET_MQ_Envelope *ev;
1123 struct RequestAddressMessage *m;
1124 struct GNUNET_ATS_SuggestHandle *s;
1126 s = GNUNET_CONTAINER_multipeermap_get (sh->sug_requests,
1133 GNUNET_assert (GNUNET_OK ==
1134 GNUNET_CONTAINER_multipeermap_remove (sh->sug_requests,
1140 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1141 m->reserved = htonl (0);
1143 GNUNET_MQ_send (sh->mq, ev);
1148 * Test if a address and a session is known to ATS
1150 * @param sh the scheduling handle
1151 * @param address the address
1152 * @param session the session
1153 * @return #GNUNET_YES or #GNUNET_NO
1156 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1157 const struct GNUNET_HELLO_Address *address,
1158 struct Session *session)
1160 if (NULL == session)
1162 if (NOT_FOUND != find_session_id (sh,
1165 return GNUNET_YES; /* Exists */
1171 * We have a new address ATS should know. Addresses have to be added
1172 * with this function before they can be: updated, set in use and
1176 * @param address the address
1177 * @param session session handle, can be NULL
1178 * @param ats performance data for the address
1179 * @param ats_count number of performance records in @a ats
1180 * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1183 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1184 const struct GNUNET_HELLO_Address *address,
1185 struct Session *session,
1186 const struct GNUNET_ATS_Information *ats,
1189 struct GNUNET_MQ_Envelope *ev;
1190 struct AddressUpdateMessage *m;
1191 struct GNUNET_ATS_Information *am;
1197 if (NULL == address)
1200 return GNUNET_SYSERR;
1203 namelen = (NULL == address->transport_name)
1205 : strlen (address->transport_name) + 1;
1207 msize = address->address_length +
1208 ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1209 if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1210 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1211 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1213 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1216 return GNUNET_SYSERR;
1219 if (NULL != session)
1221 if (NOT_FOUND != (s = find_session_id (sh, session, &address->peer)))
1223 /* Already existing, nothing todo */
1224 return GNUNET_SYSERR;
1226 s = find_empty_session_slot (sh,
1229 GNUNET_break (NOT_FOUND != s);
1232 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
1233 m->ats_count = htonl (ats_count);
1234 m->peer = address->peer;
1235 m->address_length = htons (address->address_length);
1236 m->address_local_info = htonl ((uint32_t) address->local_info);
1237 m->plugin_name_length = htons (namelen);
1238 m->session_id = htonl (s);
1240 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1241 "Adding address for peer `%s', plugin `%s', session %p id %u\n",
1242 GNUNET_i2s (&address->peer),
1243 address->transport_name,
1246 am = (struct GNUNET_ATS_Information *) &m[1];
1249 ats_count * sizeof (struct GNUNET_ATS_Information));
1250 pm = (char *) &am[ats_count];
1251 memcpy (pm, address->address, address->address_length);
1252 if (NULL != address->transport_name)
1253 memcpy (&pm[address->address_length],
1254 address->transport_name,
1256 GNUNET_MQ_send (sh->mq, ev);
1262 * We have updated performance statistics for a given address. Note
1263 * that this function can be called for addresses that are currently
1264 * in use as well as addresses that are valid but not actively in use.
1265 * Furthermore, the peer may not even be connected to us right now (in
1266 * which case the call may be ignored or the information may be stored
1267 * for later use). Update bandwidth assignments.
1270 * @param address the address
1271 * @param session session handle, can be NULL
1272 * @param ats performance data for the address
1273 * @param ats_count number of performance records in @a ats
1274 * @return #GNUNET_YES on success, #GNUNET_NO if address or session are unknown,
1275 * #GNUNET_SYSERR on hard failure
1278 GNUNET_ATS_address_update (struct GNUNET_ATS_SchedulingHandle *sh,
1279 const struct GNUNET_HELLO_Address *address,
1280 struct Session *session,
1281 const struct GNUNET_ATS_Information *ats,
1284 struct GNUNET_MQ_Envelope *ev;
1285 struct AddressUpdateMessage *m;
1286 struct GNUNET_ATS_Information *am;
1292 if (NULL == address)
1295 return GNUNET_SYSERR;
1300 return GNUNET_SYSERR;
1303 namelen = (address->transport_name ==
1304 NULL) ? 0 : strlen (address->transport_name) + 1;
1305 msize = address->address_length +
1306 ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1307 if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1308 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1309 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1311 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1314 return GNUNET_SYSERR;
1317 if (NULL != session)
1319 s = find_session_id (sh, session, &address->peer);
1324 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1325 m->ats_count = htonl (ats_count);
1326 m->peer = address->peer;
1327 m->address_length = htons (address->address_length);
1328 m->address_local_info = htonl ((uint32_t) address->local_info);
1329 m->plugin_name_length = htons (namelen);
1330 m->session_id = htonl (s);
1332 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1333 "Updating address for peer `%s', plugin `%s', session %p id %u\n",
1334 GNUNET_i2s (&address->peer),
1335 address->transport_name,
1339 am = (struct GNUNET_ATS_Information *) &m[1];
1342 ats_count * sizeof (struct GNUNET_ATS_Information));
1343 pm = (char *) &am[ats_count];
1344 memcpy (pm, address->address, address->address_length);
1345 memcpy (&pm[address->address_length], address->transport_name, namelen);
1346 GNUNET_MQ_send (sh->mq, ev);
1352 * An address is now in use or not used any more.
1355 * @param address the address
1356 * @param session session handle, can be NULL
1357 * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1358 * if address is not used any more
1361 GNUNET_ATS_address_in_use (struct GNUNET_ATS_SchedulingHandle *sh,
1362 const struct GNUNET_HELLO_Address *address,
1363 struct Session *session,
1366 struct GNUNET_MQ_Envelope *ev;
1367 struct AddressUseMessage *m;
1374 (address->transport_name ==
1375 NULL) ? 0 : strlen (address->transport_name) + 1;
1376 msize = address->address_length + namelen;
1377 if ((msize + sizeof (struct AddressUseMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1378 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1379 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1385 if (NULL != session)
1387 s = find_session_id (sh, session, &address->peer);
1388 if ((s == NOT_FOUND) && (GNUNET_NO == in_use))
1390 /* trying to set unknown address to NO */
1391 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1392 "Trying to set unknown address to unused for peer `%s', plugin `%s', session %p\n",
1393 GNUNET_i2s (&address->peer), address->transport_name, session);
1397 if ((s == NOT_FOUND) && (GNUNET_YES == in_use))
1399 /* trying to set new address to YES */
1400 s = find_empty_session_slot (sh, session, &address->peer);
1401 GNUNET_assert (NOT_FOUND != s);
1405 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
1406 m->peer = address->peer;
1407 m->in_use = htons (in_use);
1408 m->address_length = htons (address->address_length);
1409 m->address_local_info = htonl ((uint32_t) address->local_info);
1410 m->plugin_name_length = htons (namelen);
1412 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1413 "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1414 (GNUNET_YES == in_use) ? "YES" : "NO",
1415 GNUNET_i2s (&address->peer), address->transport_name, session);
1417 m->session_id = htonl (s);
1418 pm = (char *) &m[1];
1419 memcpy (pm, address->address, address->address_length);
1420 memcpy (&pm[address->address_length], address->transport_name, namelen);
1421 GNUNET_MQ_send (sh->mq, ev);
1426 * An address got destroyed, stop including it as a valid address.
1428 * If a session is given, only the session will be removed, if no session is
1429 * given the full address will be deleted.
1432 * @param address the address
1433 * @param session session handle that is no longer valid, can be NULL
1436 GNUNET_ATS_address_destroyed (struct GNUNET_ATS_SchedulingHandle *sh,
1437 const struct GNUNET_HELLO_Address *address,
1438 struct Session *session)
1440 struct GNUNET_MQ_Envelope *ev;
1441 struct AddressDestroyedMessage *m;
1447 if (NULL == address)
1452 GNUNET_assert (NULL != address->transport_name);
1453 namelen = strlen (address->transport_name) + 1;
1454 GNUNET_assert (namelen > 1);
1455 msize = address->address_length + namelen;
1456 if ((msize + sizeof (struct AddressDestroyedMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1457 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1458 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE))
1464 s = find_session_id (sh, session, &address->peer);
1465 if ((NULL != session) && (NOT_FOUND == s))
1467 /* trying to delete unknown address */
1468 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1469 "Trying to delete unknown address for peer `%s', plugin `%s', session %p\n",
1470 GNUNET_i2s (&address->peer), address->transport_name, session);
1474 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1475 m->peer = address->peer;
1476 m->address_length = htons (address->address_length);
1477 m->address_local_info = htonl ((uint32_t) address->local_info);
1478 m->plugin_name_length = htons (namelen);
1480 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1481 "Deleting address for peer `%s', plugin `%s', session %p\n",
1482 GNUNET_i2s (&address->peer), address->transport_name, session);
1484 m->session_id = htonl (s);
1485 pm = (char *) &m[1];
1486 memcpy (pm, address->address, address->address_length);
1487 memcpy (&pm[address->address_length], address->transport_name, namelen);
1488 GNUNET_MQ_send (sh->mq, ev);
1489 remove_session (sh, s, &address->peer);
1492 /* end of ats_api_scheduling.c */