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 * - we could avoid a linear scan over the
28 * active addresses in some cases, so if
29 * there is need, we can still optimize here
30 * - we might want to split off the logic to
31 * determine LAN vs. WAN, as it has nothing
32 * to do with accessing the ATS service.
35 #include "gnunet_ats_service.h"
39 * How frequently do we scan the interfaces for changes to the addresses?
41 #define INTERFACE_PROCESSING_INTERVAL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
45 * Session ID we use if there is no session / slot.
51 * Information we track per address, incoming or outgoing. It also
52 * doesn't matter if we have a session, any address that ATS is
53 * allowed to suggest right now should be tracked.
55 struct GNUNET_ATS_AddressRecord
59 * Scheduling handle this address record belongs to.
61 struct GNUNET_ATS_SchedulingHandle *sh;
66 struct GNUNET_HELLO_Address *address;
69 * Session handle. NULL if we have an address but no
70 * active session for this address.
72 struct Session *session;
75 * Array with performance data about the address.
77 struct GNUNET_ATS_Information *ats;
80 * Number of entries in @e ats.
85 * Which slot (index) in the session array does
86 * this record correspond to? FIXME:
87 * FIXME: a linear search on this is really crappy!
88 * Maybe switch to a 64-bit global counter and be
89 * done with it? Or does that then cause too much
90 * trouble on the ATS-service side?
95 * Is this address currently in use? In use means
96 * that the transport service will use this address
102 * We're about to destroy this address record, just ATS does
103 * not know this yet. Once ATS confirms its destruction,
111 * We keep a list of our local networks so we can answer
112 * LAN vs. WAN questions. Note: WLAN is not detected yet.
113 * (maybe we can do that heuristically based on interface
114 * name in the future?).
116 * FIXME: should this be part of the ATS scheduling API?
117 * Seems to be more generic and independent of ATS.
124 struct ATS_Network *next;
129 struct ATS_Network *prev;
134 struct sockaddr *network;
137 * Netmask to determine what is in the LAN.
139 struct sockaddr *netmask;
142 * How long are @e network and @e netmask?
149 * Handle for ATS address suggestion requests.
151 struct GNUNET_ATS_SuggestHandle
154 * ID of the peer for which address suggestion was requested.
156 struct GNUNET_PeerIdentity id;
161 * Handle to the ATS subsystem for bandwidth/transport scheduling information.
163 struct GNUNET_ATS_SchedulingHandle
169 const struct GNUNET_CONFIGURATION_Handle *cfg;
172 * Callback to invoke on suggestions.
174 GNUNET_ATS_AddressSuggestionCallback suggest_cb;
177 * Closure for @e suggest_cb.
179 void *suggest_cb_cls;
182 * Map with the identities of all the peers for which we would
183 * like to have address suggestions. The key is the PID, the
184 * value is currently the `struct GNUNET_ATS_SuggestHandle`
186 struct GNUNET_CONTAINER_MultiPeerMap *sug_requests;
189 * Connection to ATS service.
191 struct GNUNET_CLIENT_Connection *client;
194 * Message queue for sending requests to the ATS service.
196 struct GNUNET_MQ_Handle *mq;
199 * Head of LAN networks list.
201 struct ATS_Network *net_head;
204 * Tail of LAN networks list.
206 struct ATS_Network *net_tail;
209 * Array of session objects (we need to translate them to numbers and back
210 * for the protocol; the offset in the array is the session number on the
211 * network). Index 0 is always NULL and reserved to represent the NULL pointer.
212 * Unused entries are also NULL.
214 struct GNUNET_ATS_AddressRecord **session_array;
217 * Task to trigger reconnect.
219 struct GNUNET_SCHEDULER_Task *task;
222 * Task for periodically refreshing our LAN network list.
224 struct GNUNET_SCHEDULER_Task *interface_task;
227 * Size of the @e session_array.
229 unsigned int session_array_size;
235 * Re-establish the connection to the ATS service.
237 * @param sh handle to use to re-connect.
240 reconnect (struct GNUNET_ATS_SchedulingHandle *sh);
244 * Re-establish the connection to the ATS service.
246 * @param cls handle to use to re-connect.
247 * @param tc scheduler context
250 reconnect_task (void *cls,
251 const struct GNUNET_SCHEDULER_TaskContext *tc)
253 struct GNUNET_ATS_SchedulingHandle *sh = cls;
261 * Disconnect from ATS and then reconnect.
263 * @param sh our handle
266 force_reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
270 GNUNET_MQ_destroy (sh->mq);
273 if (NULL != sh->client)
275 GNUNET_CLIENT_disconnect (sh->client);
278 sh->task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
285 * Find the session object corresponding to the given session ID.
287 * @param sh our handle
288 * @param session_id current session ID
289 * @param peer peer the session belongs to
290 * @return the session object (or NULL)
292 static struct GNUNET_ATS_AddressRecord *
293 find_session (struct GNUNET_ATS_SchedulingHandle *sh,
295 const struct GNUNET_PeerIdentity *peer)
297 struct GNUNET_ATS_AddressRecord *ar;
299 if (session_id >= sh->session_array_size)
306 ar = sh->session_array[session_id];
312 if (NULL == ar->address)
314 /* address was destroyed in the meantime, this can happen
315 as we communicate asynchronously with the ATS service. */
318 if (0 != memcmp (peer,
320 sizeof (struct GNUNET_PeerIdentity)))
323 force_reconnect (sh);
331 * Get an available session ID.
333 * @param sh our handle
334 * @return an unused slot, but never NOT_FOUND (0)
337 find_empty_session_slot (struct GNUNET_ATS_SchedulingHandle *sh)
343 while ( ( (NOT_FOUND == off) ||
344 (NULL != sh->session_array[off % sh->session_array_size]) ) &&
345 (i < sh->session_array_size) )
350 if ( (NOT_FOUND != off % sh->session_array_size) &&
351 (NULL == sh->session_array[off % sh->session_array_size]) )
353 i = sh->session_array_size;
354 GNUNET_array_grow (sh->session_array,
355 sh->session_array_size,
356 sh->session_array_size * 2);
362 * Get the ID for the given session object.
364 * @param sh our handle
365 * @param session session object
366 * @param address the address we are looking for
367 * @return the session id or NOT_FOUND for error
370 find_session_id (struct GNUNET_ATS_SchedulingHandle *sh,
371 struct Session *session,
372 const struct GNUNET_HELLO_Address *address)
381 for (i = 1; i < sh->session_array_size; i++)
382 if ( (NULL != sh->session_array[i]) &&
383 ( (session == sh->session_array[i]->session) ||
384 (NULL == sh->session_array[i]->session) ) &&
385 (0 == GNUNET_HELLO_address_cmp (address,
386 sh->session_array[i]->address)) )
393 * Release the session slot from the session table (ATS service is
394 * also done using it).
396 * @param sh our handle
397 * @param session_id identifies session that is no longer valid
400 release_session (struct GNUNET_ATS_SchedulingHandle *sh,
403 struct GNUNET_ATS_AddressRecord *ar;
405 if (NOT_FOUND == session_id)
407 if (session_id >= sh->session_array_size)
410 force_reconnect (sh);
413 /* this slot should have been removed from remove_session before */
414 ar = sh->session_array[session_id];
415 if (NULL != ar->session)
418 force_reconnect (sh);
421 GNUNET_HELLO_address_free (ar->address);
423 sh->session_array[session_id] = NULL;
428 * Type of a function to call when we receive a session release
429 * message from the service.
431 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
432 * @param msg message received, NULL on timeout or fatal error
435 process_ats_session_release_message (void *cls,
436 const struct GNUNET_MessageHeader *msg)
438 struct GNUNET_ATS_SchedulingHandle *sh = cls;
439 const struct SessionReleaseMessage *srm;
441 srm = (const struct SessionReleaseMessage *) msg;
442 /* Note: peer field in srm not necessary right now,
443 but might be good to have in the future */
445 ntohl (srm->session_id));
450 * Type of a function to call when we receive a address suggestion
451 * message from the service.
453 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
454 * @param msg message received, NULL on timeout or fatal error
457 process_ats_address_suggestion_message (void *cls,
458 const struct GNUNET_MessageHeader *msg)
460 struct GNUNET_ATS_SchedulingHandle *sh = cls;
461 const struct AddressSuggestionMessage *m;
462 struct GNUNET_ATS_AddressRecord *ar;
465 m = (const struct AddressSuggestionMessage *) msg;
466 session_id = ntohl (m->session_id);
470 force_reconnect (sh);
473 ar = find_session (sh, session_id, &m->peer);
477 force_reconnect (sh);
480 if (NULL == sh->suggest_cb)
482 if (GNUNET_YES == ar->in_destroy)
484 /* ignore suggestion, as this address is dying */
487 if ( (NULL == ar->session) &&
488 (GNUNET_HELLO_address_check_option (ar->address,
489 GNUNET_HELLO_ADDRESS_INFO_INBOUND)) )
494 sh->suggest_cb (sh->suggest_cb_cls,
504 * We encountered an error handling the MQ to the
505 * ATS service. Reconnect.
507 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
508 * @param error details about the error
511 error_handler (void *cls,
512 enum GNUNET_MQ_Error error)
514 struct GNUNET_ATS_SchedulingHandle *sh = cls;
516 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
517 "ATS connection died (code %d), reconnecting\n",
519 force_reconnect (sh);
524 * Generate and transmit the `struct AddressAddMessage` for the given
527 * @param sh the scheduling handle to use for transmission
528 * @param ar the address to inform the ATS service about
531 send_add_address_message (struct GNUNET_ATS_SchedulingHandle *sh,
532 const struct GNUNET_ATS_AddressRecord *ar)
534 struct GNUNET_MQ_Envelope *ev;
535 struct AddressAddMessage *m;
536 struct GNUNET_ATS_Information *am;
542 return; /* disconnected, skip for now */
543 namelen = (NULL == ar->address->transport_name)
545 : strlen (ar->address->transport_name) + 1;
546 msize = ar->address->address_length +
547 ar->ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
549 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_ADD);
550 m->ats_count = htonl (ar->ats_count);
551 m->peer = ar->address->peer;
552 m->address_length = htons (ar->address->address_length);
553 m->address_local_info = htonl ((uint32_t) ar->address->local_info);
554 m->plugin_name_length = htons (namelen);
555 m->session_id = htonl (ar->slot);
557 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
558 "Adding address for peer `%s', plugin `%s', session %p id %u\n",
559 GNUNET_i2s (&ar->address->peer),
560 ar->address->transport_name,
563 am = (struct GNUNET_ATS_Information *) &m[1];
566 ar->ats_count * sizeof (struct GNUNET_ATS_Information));
567 pm = (char *) &am[ar->ats_count];
569 ar->address->address,
570 ar->address->address_length);
571 if (NULL != ar->address->transport_name)
572 memcpy (&pm[ar->address->address_length],
573 ar->address->transport_name,
575 GNUNET_MQ_send (sh->mq, ev);
580 * Transmit request for an address suggestion.
582 * @param cls the `struct GNUNET_ATS_SchedulingHandle`
583 * @param peer peer to ask for an address suggestion for
584 * @param value the `struct GNUNET_ATS_SuggestHandle`
585 * @return #GNUNET_OK (continue to iterate), #GNUNET_SYSERR on
586 * failure (message queue no longer exists)
589 transmit_suggestion (void *cls,
590 const struct GNUNET_PeerIdentity *peer,
593 struct GNUNET_ATS_SchedulingHandle *sh = cls;
594 struct GNUNET_MQ_Envelope *ev;
595 struct RequestAddressMessage *m;
598 return GNUNET_SYSERR;
599 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS);
600 m->reserved = htonl (0);
602 GNUNET_MQ_send (sh->mq, ev);
608 * Generate and transmit the `struct AddressUseMessage` for the given
611 * @param ar the address to inform the ATS service about
612 * @param in_use say if it is in use or not
615 send_in_use_message (struct GNUNET_ATS_AddressRecord *ar,
618 struct GNUNET_ATS_SchedulingHandle *sh = ar->sh;
619 struct GNUNET_MQ_Envelope *ev;
620 struct AddressUseMessage *m;
622 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_IN_USE);
623 m->peer = ar->address->peer;
624 m->in_use = htonl ((uint32_t) in_use);
625 m->session_id = htonl (ar->slot);
626 GNUNET_MQ_send (sh->mq, ev);
631 * Re-establish the connection to the ATS service.
633 * @param sh handle to use to re-connect.
636 reconnect (struct GNUNET_ATS_SchedulingHandle *sh)
638 static const struct GNUNET_MQ_MessageHandler handlers[] =
639 { { &process_ats_session_release_message,
640 GNUNET_MESSAGE_TYPE_ATS_SESSION_RELEASE,
641 sizeof (struct SessionReleaseMessage) },
642 { &process_ats_address_suggestion_message,
643 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
644 sizeof (struct AddressSuggestionMessage) },
646 struct GNUNET_MQ_Envelope *ev;
647 struct ClientStartMessage *init;
649 struct GNUNET_ATS_AddressRecord *ar;
651 GNUNET_assert (NULL == sh->client);
652 sh->client = GNUNET_CLIENT_connect ("ats", sh->cfg);
653 if (NULL == sh->client)
655 force_reconnect (sh);
658 sh->mq = GNUNET_MQ_queue_for_connection_client (sh->client,
662 ev = GNUNET_MQ_msg (init,
663 GNUNET_MESSAGE_TYPE_ATS_START);
664 init->start_flag = htonl (START_FLAG_SCHEDULING);
665 GNUNET_MQ_send (sh->mq, ev);
668 for (i=0;i<sh->session_array_size;i++)
670 ar = sh->session_array[i];
673 send_add_address_message (sh, ar);
675 send_in_use_message (ar, GNUNET_YES);
679 GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
680 &transmit_suggestion,
686 * Delete all entries from the current network list.
688 * @param sh scheduling handle to clean up
691 delete_networks (struct GNUNET_ATS_SchedulingHandle *sh)
693 struct ATS_Network *cur;
695 while (NULL != (cur = sh->net_head))
697 GNUNET_CONTAINER_DLL_remove (sh->net_head,
706 * Function invoked for each interface found. Adds the interface's
707 * network addresses to the respective DLL, so we can distinguish
708 * between LAN and WAN.
711 * @param name name of the interface (can be NULL for unknown)
712 * @param isDefault is this presumably the default interface
713 * @param addr address of this interface (can be NULL for unknown or unassigned)
714 * @param broadcast_addr the broadcast address (can be NULL for unknown or unassigned)
715 * @param netmask the network mask (can be NULL for unknown or unassigned)
716 * @param addrlen length of the address
717 * @return #GNUNET_OK to continue iteration
720 interface_proc (void *cls,
723 const struct sockaddr *addr,
724 const struct sockaddr *broadcast_addr,
725 const struct sockaddr *netmask,
728 struct GNUNET_ATS_SchedulingHandle *sh = cls;
729 /* Calculate network */
730 struct ATS_Network *net = NULL;
732 /* Skipping IPv4 loopback addresses since we have special check */
733 if (addr->sa_family == AF_INET)
735 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
737 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
740 /* Skipping IPv6 loopback addresses since we have special check */
741 if (addr->sa_family == AF_INET6)
743 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
744 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
748 if (addr->sa_family == AF_INET)
750 const struct sockaddr_in *addr4 = (const struct sockaddr_in *) addr;
751 const struct sockaddr_in *netmask4 = (const struct sockaddr_in *) netmask;
752 struct sockaddr_in *tmp;
753 struct sockaddr_in network4;
755 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in));
756 tmp = (struct sockaddr_in *) &net[1];
757 net->network = (struct sockaddr *) &tmp[0];
758 net->netmask = (struct sockaddr *) &tmp[1];
759 net->length = addrlen;
761 memset (&network4, 0, sizeof (network4));
762 network4.sin_family = AF_INET;
763 #if HAVE_SOCKADDR_IN_SIN_LEN
764 network4.sin_len = sizeof (network4);
766 network4.sin_addr.s_addr = (addr4->sin_addr.s_addr & netmask4->sin_addr.s_addr);
768 memcpy (net->netmask, netmask4, sizeof (struct sockaddr_in));
769 memcpy (net->network, &network4, sizeof (struct sockaddr_in));
772 if (addr->sa_family == AF_INET6)
774 const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *) addr;
775 const struct sockaddr_in6 *netmask6 = (const struct sockaddr_in6 *) netmask;
776 struct sockaddr_in6 * tmp;
777 struct sockaddr_in6 network6;
779 net = GNUNET_malloc (sizeof (struct ATS_Network) + 2 * sizeof (struct sockaddr_in6));
780 tmp = (struct sockaddr_in6 *) &net[1];
781 net->network = (struct sockaddr *) &tmp[0];
782 net->netmask = (struct sockaddr *) &tmp[1];
783 net->length = addrlen;
785 memset (&network6, 0, sizeof (network6));
786 network6.sin6_family = AF_INET6;
787 #if HAVE_SOCKADDR_IN_SIN_LEN
788 network6.sin6_len = sizeof (network6);
791 uint32_t *addr_elem = (uint32_t *) &addr6->sin6_addr;
792 uint32_t *mask_elem = (uint32_t *) &netmask6->sin6_addr;
793 uint32_t *net_elem = (uint32_t *) &network6.sin6_addr;
794 for (c = 0; c < 4; c++)
795 net_elem[c] = addr_elem[c] & mask_elem[c];
797 memcpy (net->netmask, netmask6, sizeof (struct sockaddr_in6));
798 memcpy (net->network, &network6, sizeof (struct sockaddr_in6));
801 return GNUNET_OK; /* odd / unsupported address family */
805 char * netmask = GNUNET_strdup (GNUNET_a2s((struct sockaddr *) net->netmask, addrlen));
806 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
807 "Adding network `%s', netmask `%s'\n",
808 GNUNET_a2s ((struct sockaddr *) net->network,
811 GNUNET_free (netmask);
813 GNUNET_CONTAINER_DLL_insert (sh->net_head,
822 * Periodically get list of network addresses from our interfaces.
825 * @param tc Task context
828 get_addresses (void *cls,
829 const struct GNUNET_SCHEDULER_TaskContext *tc)
831 struct GNUNET_ATS_SchedulingHandle *sh = cls;
833 sh->interface_task = NULL;
834 delete_networks (sh);
835 GNUNET_OS_network_interfaces_list (&interface_proc,
837 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
844 * Convert a `enum GNUNET_ATS_Network_Type` to a string
846 * @param net the network type
847 * @return a string or NULL if invalid
850 GNUNET_ATS_print_network_type (enum GNUNET_ATS_Network_Type net)
854 case GNUNET_ATS_NET_UNSPECIFIED:
855 return "UNSPECIFIED";
856 case GNUNET_ATS_NET_LOOPBACK:
858 case GNUNET_ATS_NET_LAN:
860 case GNUNET_ATS_NET_WAN:
862 case GNUNET_ATS_NET_WLAN:
864 case GNUNET_ATS_NET_BT:
873 * Convert a ATS property to a string
875 * @param type the property type
876 * @return a string or NULL if invalid
879 GNUNET_ATS_print_property_type (enum GNUNET_ATS_Property type)
883 case GNUNET_ATS_ARRAY_TERMINATOR:
885 case GNUNET_ATS_UTILIZATION_OUT:
886 return "UTILIZATION_UP";
887 case GNUNET_ATS_UTILIZATION_IN:
888 return "UTILIZATION_DOWN";
889 case GNUNET_ATS_UTILIZATION_PAYLOAD_OUT:
890 return "UTILIZATION_PAYLOAD_UP";
891 case GNUNET_ATS_UTILIZATION_PAYLOAD_IN:
892 return "UTILIZATION_PAYLOAD_DOWN";
893 case GNUNET_ATS_NETWORK_TYPE:
894 return "NETWORK_TYPE";
895 case GNUNET_ATS_QUALITY_NET_DELAY:
897 case GNUNET_ATS_QUALITY_NET_DISTANCE:
899 case GNUNET_ATS_COST_WAN:
901 case GNUNET_ATS_COST_LAN:
903 case GNUNET_ATS_COST_WLAN:
912 * Returns where the address is located: LAN or WAN or ...
914 * @param sh the scheduling handle
915 * @param addr address
916 * @param addrlen address length
917 * @return type of the network the address belongs to
919 enum GNUNET_ATS_Network_Type
920 GNUNET_ATS_address_get_type (struct GNUNET_ATS_SchedulingHandle *sh,
921 const struct sockaddr *addr,
924 struct ATS_Network *cur = sh->net_head;
925 enum GNUNET_ATS_NetworkType type = GNUNET_ATS_NET_UNSPECIFIED;
927 switch (addr->sa_family)
930 type = GNUNET_ATS_NET_LOOPBACK;
934 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
936 if ((a4->sin_addr.s_addr & htonl(0xff000000)) == htonl (0x7f000000))
937 type = GNUNET_ATS_NET_LOOPBACK;
942 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
944 if (IN6_IS_ADDR_LOOPBACK (&a6->sin6_addr))
945 type = GNUNET_ATS_NET_LOOPBACK;
953 /* Check local networks */
954 while ((NULL != cur) && (GNUNET_ATS_NET_UNSPECIFIED == type))
956 if (addrlen != cur->length)
961 if (addr->sa_family == AF_INET)
963 const struct sockaddr_in *a4 = (const struct sockaddr_in *) addr;
964 const struct sockaddr_in *net4 = (const struct sockaddr_in *) cur->network;
965 const struct sockaddr_in *mask4 = (const struct sockaddr_in *) cur->netmask;
967 if (((a4->sin_addr.s_addr & mask4->sin_addr.s_addr)) == net4->sin_addr.s_addr)
968 type = GNUNET_ATS_NET_LAN;
970 if (addr->sa_family == AF_INET6)
972 const struct sockaddr_in6 *a6 = (const struct sockaddr_in6 *) addr;
973 const struct sockaddr_in6 *net6 = (const struct sockaddr_in6 *) cur->network;
974 const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *) cur->netmask;
976 int res = GNUNET_YES;
978 uint32_t *addr_elem = (uint32_t *) &a6->sin6_addr;
979 uint32_t *mask_elem = (uint32_t *) &mask6->sin6_addr;
980 uint32_t *net_elem = (uint32_t *) &net6->sin6_addr;
981 for (c = 0; c < 4; c++)
982 if ((addr_elem[c] & mask_elem[c]) != net_elem[c])
985 if (res == GNUNET_YES)
986 type = GNUNET_ATS_NET_LAN;
991 /* no local network found for this address, default: WAN */
992 if (type == GNUNET_ATS_NET_UNSPECIFIED)
993 type = GNUNET_ATS_NET_WAN;
994 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
995 "ats-scheduling-api",
996 "`%s' is in network `%s'\n",
999 GNUNET_ATS_print_network_type (type));
1005 * Initialize the ATS subsystem.
1007 * @param cfg configuration to use
1008 * @param suggest_cb notification to call whenever the suggestation changed
1009 * @param suggest_cb_cls closure for @a suggest_cb
1010 * @return ats context
1012 struct GNUNET_ATS_SchedulingHandle *
1013 GNUNET_ATS_scheduling_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
1014 GNUNET_ATS_AddressSuggestionCallback suggest_cb,
1015 void *suggest_cb_cls)
1017 struct GNUNET_ATS_SchedulingHandle *sh;
1019 sh = GNUNET_new (struct GNUNET_ATS_SchedulingHandle);
1021 sh->suggest_cb = suggest_cb;
1022 sh->suggest_cb_cls = suggest_cb_cls;
1023 GNUNET_array_grow (sh->session_array,
1024 sh->session_array_size,
1026 sh->sug_requests = GNUNET_CONTAINER_multipeermap_create (32,
1028 GNUNET_OS_network_interfaces_list (&interface_proc,
1030 sh->interface_task = GNUNET_SCHEDULER_add_delayed (INTERFACE_PROCESSING_INTERVAL,
1039 * Function called to free all `struct GNUNET_ATS_SuggestHandles`
1043 * @param key the key
1044 * @param value the value to free
1045 * @return #GNUNET_OK (continue to iterate)
1048 free_sug_handle (void *cls,
1049 const struct GNUNET_PeerIdentity *key,
1052 struct GNUNET_ATS_SuggestHandle *cur = value;
1060 * Client is done with ATS scheduling, release resources.
1062 * @param sh handle to release
1065 GNUNET_ATS_scheduling_done (struct GNUNET_ATS_SchedulingHandle *sh)
1069 GNUNET_MQ_destroy (sh->mq);
1072 if (NULL != sh->client)
1074 GNUNET_CLIENT_disconnect (sh->client);
1077 if (NULL != sh->task)
1079 GNUNET_SCHEDULER_cancel (sh->task);
1082 GNUNET_CONTAINER_multipeermap_iterate (sh->sug_requests,
1085 GNUNET_CONTAINER_multipeermap_destroy (sh->sug_requests);
1086 if (NULL != sh->interface_task)
1088 GNUNET_SCHEDULER_cancel (sh->interface_task);
1089 sh->interface_task = NULL;
1091 delete_networks (sh);
1092 GNUNET_array_grow (sh->session_array,
1093 sh->session_array_size,
1100 * We would like to reset the address suggestion block time for this
1104 * @param peer identity of the peer we want to reset
1107 GNUNET_ATS_reset_backoff (struct GNUNET_ATS_SchedulingHandle *sh,
1108 const struct GNUNET_PeerIdentity *peer)
1110 struct GNUNET_MQ_Envelope *ev;
1111 struct ResetBackoffMessage *m;
1113 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_RESET_BACKOFF);
1114 m->reserved = htonl (0);
1116 GNUNET_MQ_send (sh->mq, ev);
1121 * We would like to receive address suggestions for a peer. ATS will
1122 * respond with a call to the continuation immediately containing an address or
1123 * no address if none is available. ATS can suggest more addresses until we call
1124 * #GNUNET_ATS_suggest_address_cancel().
1127 * @param peer identity of the peer we need an address for
1128 * @return suggest handle, NULL if a request is already pending
1130 struct GNUNET_ATS_SuggestHandle *
1131 GNUNET_ATS_suggest_address (struct GNUNET_ATS_SchedulingHandle *sh,
1132 const struct GNUNET_PeerIdentity *peer)
1134 struct GNUNET_ATS_SuggestHandle *s;
1136 s = GNUNET_new (struct GNUNET_ATS_SuggestHandle);
1139 GNUNET_CONTAINER_multipeermap_put (sh->sug_requests,
1142 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1149 (void) transmit_suggestion (sh,
1157 * We would like to stop receiving address updates for this peer
1160 * @param peer identity of the peer
1163 GNUNET_ATS_suggest_address_cancel (struct GNUNET_ATS_SchedulingHandle *sh,
1164 const struct GNUNET_PeerIdentity *peer)
1166 struct GNUNET_MQ_Envelope *ev;
1167 struct RequestAddressMessage *m;
1168 struct GNUNET_ATS_SuggestHandle *s;
1170 s = GNUNET_CONTAINER_multipeermap_get (sh->sug_requests,
1177 GNUNET_assert (GNUNET_OK ==
1178 GNUNET_CONTAINER_multipeermap_remove (sh->sug_requests,
1184 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_REQUEST_ADDRESS_CANCEL);
1185 m->reserved = htonl (0);
1187 GNUNET_MQ_send (sh->mq, ev);
1192 * Test if a address and a session is known to ATS
1194 * @param sh the scheduling handle
1195 * @param address the address
1196 * @param session the session
1197 * @return #GNUNET_YES or #GNUNET_NO
1200 GNUNET_ATS_session_known (struct GNUNET_ATS_SchedulingHandle *sh,
1201 const struct GNUNET_HELLO_Address *address,
1202 struct Session *session)
1204 if (NULL == session)
1206 if (NOT_FOUND != find_session_id (sh,
1209 return GNUNET_YES; /* Exists */
1215 * We have a new address ATS should know. Addresses have to be added
1216 * with this function before they can be: updated, set in use and
1220 * @param address the address
1221 * @param session session handle, can be NULL
1222 * @param ats performance data for the address
1223 * @param ats_count number of performance records in @a ats
1224 * @return handle to the address representation inside ATS, NULL
1225 * on error (i.e. ATS knows this exact address already)
1227 struct GNUNET_ATS_AddressRecord *
1228 GNUNET_ATS_address_add (struct GNUNET_ATS_SchedulingHandle *sh,
1229 const struct GNUNET_HELLO_Address *address,
1230 struct Session *session,
1231 const struct GNUNET_ATS_Information *ats,
1234 struct GNUNET_ATS_AddressRecord *ar;
1239 if (NULL == address)
1241 /* we need a valid address */
1245 namelen = (NULL == address->transport_name)
1247 : strlen (address->transport_name) + 1;
1248 msize = address->address_length +
1249 ats_count * sizeof (struct GNUNET_ATS_Information) + namelen;
1250 if ((msize + sizeof (struct AddressUpdateMessage) >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1251 (address->address_length >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1252 (namelen >= GNUNET_SERVER_MAX_MESSAGE_SIZE) ||
1254 GNUNET_SERVER_MAX_MESSAGE_SIZE / sizeof (struct GNUNET_ATS_Information)))
1256 /* address too large for us, this should not happen */
1261 if (NOT_FOUND != find_session_id (sh, session, address))
1263 /* Already existing, nothing todo, but this should not happen */
1267 s = find_empty_session_slot (sh);
1268 ar = GNUNET_new (struct GNUNET_ATS_AddressRecord);
1271 ar->session = session;
1272 ar->address = GNUNET_HELLO_address_copy (address);
1273 GNUNET_array_grow (ar->ats,
1278 ats_count * sizeof (struct GNUNET_ATS_Information));
1279 sh->session_array[s] = ar;
1280 send_add_address_message (sh, ar);
1286 * An address was used to initiate a session.
1288 * @param ar address record to update information for
1289 * @param session session handle
1292 GNUNET_ATS_address_add_session (struct GNUNET_ATS_AddressRecord *ar,
1293 struct Session *session)
1295 GNUNET_break (NULL == ar->session);
1296 ar->session = session;
1301 * A session was destroyed, disassociate it from the
1302 * given address record. If this was an incoming
1303 * addess, destroy the address as well.
1305 * @param ar address record to update information for
1306 * @param session session handle
1307 * @return #GNUNET_YES if the @a ar was destroyed because
1308 * it was an incoming address,
1309 * #GNUNET_NO if the @ar was kept because we can
1310 * use it still to establish a new session
1313 GNUNET_ATS_address_del_session (struct GNUNET_ATS_AddressRecord *ar,
1314 struct Session *session)
1316 GNUNET_break (session == ar->session);
1318 GNUNET_break (GNUNET_NO == ar->in_use);
1319 if (GNUNET_HELLO_address_check_option (ar->address,
1320 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
1322 GNUNET_ATS_address_destroy (ar);
1330 * We have updated performance statistics for a given address. Note
1331 * that this function can be called for addresses that are currently
1332 * in use as well as addresses that are valid but not actively in use.
1333 * Furthermore, the peer may not even be connected to us right now (in
1334 * which case the call may be ignored or the information may be stored
1335 * for later use). Update bandwidth assignments.
1337 * @param ar address record to update information for
1338 * @param ats performance data for the address
1339 * @param ats_count number of performance records in @a ats
1342 GNUNET_ATS_address_update (struct GNUNET_ATS_AddressRecord *ar,
1343 const struct GNUNET_ATS_Information *ats,
1346 struct GNUNET_ATS_SchedulingHandle *sh = ar->sh;
1347 struct GNUNET_MQ_Envelope *ev;
1348 struct AddressUpdateMessage *m;
1349 struct GNUNET_ATS_Information *am;
1352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1353 "Adding address for peer `%s', plugin `%s', session %p id %u\n",
1354 GNUNET_i2s (&ar->address->peer),
1355 ar->address->transport_name,
1358 GNUNET_array_grow (ar->ats,
1363 ats_count * sizeof (struct GNUNET_ATS_Information));
1366 return; /* disconnected, skip for now */
1367 msize = ar->ats_count * sizeof (struct GNUNET_ATS_Information);
1368 ev = GNUNET_MQ_msg_extra (m, msize, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_UPDATE);
1369 m->ats_count = htonl (ar->ats_count);
1370 m->peer = ar->address->peer;
1371 m->session_id = htonl (ar->slot);
1372 am = (struct GNUNET_ATS_Information *) &m[1];
1375 ar->ats_count * sizeof (struct GNUNET_ATS_Information));
1376 GNUNET_MQ_send (sh->mq, ev);
1381 * An address is now in use or not used any more.
1383 * @param ar the address
1384 * @param in_use #GNUNET_YES if this address is now used, #GNUNET_NO
1385 * if address is not used any more
1388 GNUNET_ATS_address_set_in_use (struct GNUNET_ATS_AddressRecord *ar,
1391 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1392 "Setting address used to %s for peer `%s', plugin `%s', session %p\n",
1393 (GNUNET_YES == in_use) ? "YES" : "NO",
1394 GNUNET_i2s (&ar->address->peer),
1395 ar->address->transport_name,
1397 ar->in_use = in_use;
1398 if (NULL == ar->sh->mq)
1400 send_in_use_message (ar, in_use);
1405 * An address got destroyed, stop using it as a valid address.
1407 * @param ar address to destroy
1410 GNUNET_ATS_address_destroy (struct GNUNET_ATS_AddressRecord *ar)
1412 struct GNUNET_ATS_SchedulingHandle *sh = ar->sh;
1413 struct GNUNET_MQ_Envelope *ev;
1414 struct AddressDestroyedMessage *m;
1416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1417 "Deleting address for peer `%s', plugin `%s', session %p\n",
1418 GNUNET_i2s (&ar->address->peer),
1419 ar->address->transport_name,
1421 GNUNET_break (NULL == ar->session);
1423 ar->in_destroy = GNUNET_YES;
1424 GNUNET_array_grow (ar->ats,
1429 ev = GNUNET_MQ_msg (m, GNUNET_MESSAGE_TYPE_ATS_ADDRESS_DESTROYED);
1430 m->session_id = htonl (ar->slot);
1431 m->peer = ar->address->peer;
1432 GNUNET_MQ_send (sh->mq, ev);
1436 /* end of ats_api_scheduling.c */