2 This file is part of GNUnet.
3 (C) 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 transport/gnunet-service-transport_ats.h
22 * @brief interfacing between transport and ATS service
23 * @author Christian Grothoff
26 #include "gnunet-service-transport.h"
27 #include "gnunet-service-transport_ats.h"
28 #include "gnunet-service-transport_manipulation.h"
29 #include "gnunet-service-transport_plugins.h"
30 #include "gnunet_ats_service.h"
34 * Information we track for each address known to ATS.
40 * The address (with peer identity).
42 struct GNUNET_HELLO_Address *address;
45 * Session (can be NULL)
47 struct Session *session;
50 * Record with ATS API for the address.
52 struct GNUNET_ATS_AddressRecord *ar;
57 * Map from peer identities to one or more `struct AddressInfo` values
60 static struct GNUNET_CONTAINER_MultiPeerMap *p2a;
64 * Closure for #find_ai().
70 * Session to look for (only used if the address is inbound).
72 struct Session *session;
75 * Address to look for.
77 const struct GNUNET_HELLO_Address *address;
80 * Where to store the result.
82 struct AddressInfo *ret;
88 * Provide an update on the `p2a` map size to statistics.
89 * This function should be called whenever the `p2a` map
93 publish_p2a_stat_update ()
95 GNUNET_STATISTICS_set (GST_stats,
96 gettext_noop ("# Addresses given to ATS"),
97 GNUNET_CONTAINER_multipeermap_size (p2a),
103 * Find matching address info.
105 * @param cls the `struct FindClosure`
106 * @param key which peer is this about
107 * @param value the `struct AddressInfo`
108 * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
111 find_ai_cb (void *cls,
112 const struct GNUNET_PeerIdentity *key,
115 struct FindClosure *fc = cls;
116 struct AddressInfo *ai = value;
119 GNUNET_HELLO_address_cmp (fc->address,
121 (fc->session == ai->session) )
126 GNUNET_assert ( (fc->session != ai->session) ||
127 (NULL == ai->session) );
133 * Find the address information struct for the
134 * given address and session.
136 * @param address address to look for
137 * @param session session to match for inbound connections
138 * @return NULL if this combination is unknown
140 static struct AddressInfo *
141 find_ai (const struct GNUNET_HELLO_Address *address,
142 struct Session *session)
144 struct FindClosure fc;
146 fc.address = address;
147 fc.session = session;
149 GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
158 * Find matching address info, ignoring sessions.
160 * @param cls the `struct FindClosure`
161 * @param key which peer is this about
162 * @param value the `struct AddressInfo`
163 * @return #GNUNET_YES to continue to iterate, #GNUNET_NO if we found the value
166 find_ai_no_session_cb (void *cls,
167 const struct GNUNET_PeerIdentity *key,
170 struct FindClosure *fc = cls;
171 struct AddressInfo *ai = value;
174 GNUNET_HELLO_address_cmp (fc->address,
185 * Find the address information struct for the
186 * given address (ignoring sessions)
188 * @param address address to look for
189 * @return NULL if this combination is unknown
191 static struct AddressInfo *
192 find_ai_no_session (const struct GNUNET_HELLO_Address *address)
194 struct FindClosure fc;
196 fc.address = address;
199 GNUNET_CONTAINER_multipeermap_get_multiple (p2a,
201 &find_ai_no_session_cb,
208 * Test if ATS knows about this address.
210 * @param address the address
211 * @param session the session
212 * @return #GNUNET_YES if address is known, #GNUNET_NO if not.
215 GST_ats_is_known (const struct GNUNET_HELLO_Address *address,
216 struct Session *session)
218 return (NULL != find_ai (address, session)) ? GNUNET_YES : GNUNET_NO;
223 * Notify ATS about the new address including the network this address is
226 * @param address the address
227 * @param session the session
228 * @param ats ats information
229 * @param ats_count number of @a ats information
232 GST_ats_add_address (const struct GNUNET_HELLO_Address *address,
233 struct Session *session,
234 const struct GNUNET_ATS_Information *ats,
237 struct GNUNET_TRANSPORT_PluginFunctions *papi;
238 struct GNUNET_ATS_Information ats2[ats_count + 1];
239 struct GNUNET_ATS_AddressRecord *ar;
240 struct AddressInfo *ai;
243 /* valid new address, let ATS know! */
244 if (NULL == address->transport_name)
250 GNUNET_HELLO_address_check_option (address,
251 GNUNET_HELLO_ADDRESS_INFO_INBOUND))
253 GNUNET_break (NULL != session);
255 ai = (NULL == session)
256 ? find_ai_no_session (address)
257 : find_ai (address, session);
262 /* in this case, we must not find an existing
263 session-less address, as the caller should
264 have checked for this case if it were possible. */
265 ai = find_ai (address, NULL);
272 if (NULL == (papi = GST_plugins_find (address->transport_name)))
274 /* we don't have the plugin for this address */
280 net = papi->get_network (papi->cls, session);
281 if (GNUNET_ATS_NET_UNSPECIFIED == net)
283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
284 _ ("Could not obtain a valid network for `%s' %s (%s)\n"),
285 GNUNET_i2s (&address->peer),
286 GST_plugins_a2s (address),
287 address->transport_name);
290 ats2[0].type = htonl (GNUNET_ATS_NETWORK_TYPE);
291 ats2[0].value = htonl (net);
294 sizeof(struct GNUNET_ATS_Information) * ats_count);
296 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
297 "Notifying ATS about peer `%s''s new address `%s' session %p in network %s\n",
298 GNUNET_i2s (&address->peer),
299 (0 == address->address_length)
301 : GST_plugins_a2s (address),
303 GNUNET_ATS_print_network_type (net));
304 ar = GNUNET_ATS_address_add (GST_ats,
307 (NULL != session) ? ats2 : ats,
308 (NULL != session) ? ats_count + 1 : ats_count);
309 ai = GNUNET_new (struct AddressInfo);
310 ai->address = GNUNET_HELLO_address_copy (address);
311 ai->session = session;
313 (void) GNUNET_CONTAINER_multipeermap_put (p2a,
316 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
317 publish_p2a_stat_update ();
322 * Notify ATS about a new session now existing for the given
325 * @param address the address
326 * @param session the session
329 GST_ats_new_session (const struct GNUNET_HELLO_Address *address,
330 struct Session *session)
332 struct AddressInfo *ai;
334 ai = find_ai (address, NULL);
337 /* We may already be aware of the session, even if some other part
338 of the code could not tell if it just created a new session or
339 just got one recycled from the plugin; hence, we may be called
340 with "new" session even for an "old" session; in that case,
341 check that this is the case, but just ignore it. */
342 GNUNET_assert (NULL != (find_ai (address, session)));
345 GNUNET_break (NULL == ai->session);
346 ai->session = session;
347 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
349 "Telling ATS about new session %p for peer %s\n",
351 GNUNET_i2s (&address->peer));
352 GNUNET_ATS_address_add_session (ai->ar,
358 * Notify ATS that the session (but not the address) of
359 * a given address is no longer relevant.
361 * @param address the address
362 * @param session the session
365 GST_ats_del_session (const struct GNUNET_HELLO_Address *address,
366 struct Session *session)
368 struct AddressInfo *ai;
375 ai = find_ai (address, session);
378 /* We sometimes create sessions just for sending a PING,
379 and if those are destroyed they were never known to
380 ATS which means we end up here (however, in this
381 case, the address must be an outbound address). */
382 GNUNET_break (GNUNET_YES !=
383 GNUNET_HELLO_address_check_option (address,
384 GNUNET_HELLO_ADDRESS_INFO_INBOUND));
388 GNUNET_assert (session == ai->session);
390 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
392 "Telling ATS to destroy session %p from peer %s\n",
394 GNUNET_i2s (&address->peer));
396 GNUNET_ATS_address_del_session (ai->ar, session))
399 GST_ats_expire_address (address);
405 * Notify ATS about property changes to an address.
407 * @param address our information about the address
408 * @param session the session
409 * @param ats performance information
410 * @param ats_count number of elements in @a ats
413 GST_ats_update_metrics (const struct GNUNET_HELLO_Address *address,
414 struct Session *session,
415 const struct GNUNET_ATS_Information *ats,
418 struct GNUNET_ATS_Information *ats_new;
419 struct AddressInfo *ai;
421 ai = find_ai (address, session);
424 /* We sometimes create sessions just for sending a PING,
425 and if we get metrics for those, they were never known to
426 ATS which means we end up here (however, in this
427 case, the address must be an outbound address). */
428 GNUNET_assert (GNUNET_YES !=
429 GNUNET_HELLO_address_check_option (address,
430 GNUNET_HELLO_ADDRESS_INFO_INBOUND));
434 /* Call to manipulation to manipulate ATS information */
435 GNUNET_assert (NULL != GST_ats);
436 if ((NULL == ats) || (0 == ats_count))
438 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
439 "Updating metrics for peer `%s' address %s session %p\n",
440 GNUNET_i2s (&address->peer),
441 GST_plugins_a2s (address),
443 ats_new = GST_manipulation_manipulate_metrics (address,
447 GNUNET_ATS_address_update (ai->ar,
449 GNUNET_free_non_null (ats_new);
454 * Notify ATS about a new session now being in use (or not).
456 * @param address the address
457 * @param session the session
458 * @param in_use #GNUNET_YES or #GNUNET_NO
461 GST_ats_set_in_use (const struct GNUNET_HELLO_Address *address,
462 struct Session *session,
465 struct AddressInfo *ai;
467 ai = find_ai (address, session);
473 GNUNET_ATS_address_set_in_use (ai->ar, in_use);
478 * Notify ATS that the address has expired and thus cannot
479 * be used any longer. This function must only be called
480 * if the corresponding session is already gone.
482 * @param address the address
485 GST_ats_expire_address (const struct GNUNET_HELLO_Address *address)
487 struct AddressInfo *ai;
489 ai = find_ai (address, NULL);
495 GNUNET_assert (GNUNET_YES ==
496 GNUNET_CONTAINER_multipeermap_remove (p2a,
499 publish_p2a_stat_update ();
500 GNUNET_break (NULL == ai->session);
501 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
503 "Telling ATS to destroy address from peer %s\n",
504 GNUNET_i2s (&address->peer));
506 GNUNET_ATS_address_destroy (ai->ar);
507 GNUNET_HELLO_address_free (ai->address);
513 * Initialize ATS subsystem.
518 p2a = GNUNET_CONTAINER_multipeermap_create (4, GNUNET_YES);
523 * Release memory used by the given address data.
526 * @param key which peer is this about
527 * @param value the `struct AddressInfo`
528 * @return #GNUNET_OK (continue to iterate)
531 destroy_ai (void *cls,
532 const struct GNUNET_PeerIdentity *key,
535 struct AddressInfo *ai = value;
537 GNUNET_assert (GNUNET_YES ==
538 GNUNET_CONTAINER_multipeermap_remove (p2a,
541 GNUNET_HELLO_address_free (ai->address);
548 * Shutdown ATS subsystem.
553 GNUNET_CONTAINER_multipeermap_iterate (p2a,
556 publish_p2a_stat_update ();
557 GNUNET_CONTAINER_multipeermap_destroy (p2a);
561 /* end of gnunet-service-transport_ats.c */