2 This file is part of GNUnet.
3 Copyright (C) 2010-2015, 2018 GNUnet e.V.
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
21 * @file ats/ats_api2_transport.c
22 * @brief address suggestions and bandwidth allocation
23 * @author Christian Grothoff
24 * @author Matthias Wachs
27 #include "gnunet_ats_transport_service.h"
30 #define LOG(kind,...) GNUNET_log_from(kind, "ats-transport-api", __VA_ARGS__)
34 * Information we track per session, incoming or outgoing. It also
35 * doesn't matter if we have a session, any session that ATS is
36 * allowed to suggest right now should be tracked.
38 struct GNUNET_ATS_SessionRecord
42 * Transport handle this session record belongs to.
44 struct GNUNET_ATS_TransportHandle *ath;
52 * Session handle, NULL if inbound-only (also implies we cannot
53 * actually control inbound traffic via transport!). So if
54 * @e session is NULL, the @e properties are informative for
55 * ATS (connection exists, utilization) but ATS cannot directly
56 * influence it (and should thus not call the
57 * #GNUNET_ATS_AllocationCallback for this @e session, which is
58 * obvious as NULL is not a meaningful session to allocation
61 struct GNUNET_ATS_Session *session;
64 * Identity of the peer reached at @e address.
66 struct GNUNET_PeerIdentity pid;
69 * Performance data about the @e session.
71 struct GNUNET_ATS_Properties properties;
74 * Unique ID to identify this session at this @a pid in IPC
83 * Handle to the ATS subsystem for bandwidth/transport transport information.
85 struct GNUNET_ATS_TransportHandle
91 const struct GNUNET_CONFIGURATION_Handle *cfg;
94 * Callback to invoke on suggestions.
96 GNUNET_ATS_SuggestionCallback suggest_cb;
99 * Closure for @e suggest_cb.
101 void *suggest_cb_cls;
104 * Callback to invoke on allocations.
106 GNUNET_ATS_AllocationCallback alloc_cb;
109 * Closure for @e alloc_cb.
114 * Message queue for sending requests to the ATS service.
116 struct GNUNET_MQ_Handle *mq;
119 * Task to trigger reconnect.
121 struct GNUNET_SCHEDULER_Task *task;
124 * Hash map mapping PIDs to session records.
126 struct GNUNET_CONTAINER_MultiPeerMap *records;
129 * Reconnect backoff delay.
131 struct GNUNET_TIME_Relative backoff;
138 * Convert ATS properties from host to network byte order.
140 * @param nbo[OUT] value written
141 * @param hbo value read
144 properties_hton (struct PropertiesNBO *nbo,
145 const struct GNUNET_ATS_Properties *hbo)
147 nbo->delay = GNUNET_TIME_relative_hton (hbo->delay);
148 nbo->goodput_out = htonl (hbo->goodput_out);
149 nbo->goodput_in = htonl (hbo->goodput_in);
150 nbo->utilization_out = htonl (hbo->utilization_out);
151 nbo->utilization_in = htonl (hbo->utilization_in);
152 nbo->distance = htonl (hbo->distance);
153 nbo->mtu = htonl (hbo->mtu);
154 nbo->nt = htonl ((uint32_t) hbo->nt);
155 nbo->cc = htonl ((uint32_t) hbo->cc);
160 * Re-establish the connection to the ATS service.
162 * @param sh handle to use to re-connect.
165 reconnect (struct GNUNET_ATS_TransportHandle *ath);
169 * Re-establish the connection to the ATS service.
171 * @param cls handle to use to re-connect.
174 reconnect_task (void *cls)
176 struct GNUNET_ATS_TransportHandle *ath = cls;
184 * Disconnect from ATS and then reconnect.
186 * @param ath our handle
189 force_reconnect (struct GNUNET_ATS_TransportHandle *ath)
193 GNUNET_MQ_destroy (ath->mq);
196 /* FIXME: do we tell transport service about disconnect events? CON:
197 initially ATS will have a really screwed picture of the world and
198 the rapid change would be bad. PRO: if we don't, ATS and
199 transport may disagree about the allocation for a while...
200 For now: lazy: do nothing. */
201 ath->backoff = GNUNET_TIME_STD_BACKOFF (ath->backoff);
202 ath->task = GNUNET_SCHEDULER_add_delayed (ath->backoff,
209 * Check format of address suggestion message from the service.
211 * @param cls the `struct GNUNET_ATS_TransportHandle`
212 * @param m message received
215 check_ats_address_suggestion (void *cls,
216 const struct AddressSuggestionMessage *m)
219 GNUNET_MQ_check_zero_termination (m);
220 return GNUNET_SYSERR;
225 * We received an address suggestion message from the service.
227 * @param cls the `struct GNUNET_ATS_TransportHandle`
228 * @param m message received
231 handle_ats_address_suggestion (void *cls,
232 const struct AddressSuggestionMessage *m)
234 struct GNUNET_ATS_TransportHandle *ath = cls;
235 const char *address = (const char *) &m[1];
237 ath->suggest_cb (ath->suggest_cb_cls,
244 * Closure for #match_session_cb.
254 * Where to store the result.
256 struct GNUNET_ATS_SessionRecord *sr;
261 * Finds matching session record.
263 * @param cls a `struct FindContext`
264 * @param pid peer identity (unused)
265 * @param value a `struct GNUNET_ATS_SessionRecord`
266 * @return #GNUNET_NO if match found, #GNUNET_YES to continue searching
269 match_session_cb (void *cls,
270 const struct GNUNET_PeerIdentity *pid,
273 struct FindContext *fc = cls;
274 struct GNUNET_ATS_SessionRecord *sr = value;
277 if (fc->session_id == sr->slot)
288 * Find session record for peer @a pid and session @a session_id
290 * @param ath transport handle to search
291 * @param session_id session ID to match
292 * @param pid peer to search under
293 * @return NULL if no such record exists
295 static struct GNUNET_ATS_SessionRecord *
296 find_session (struct GNUNET_ATS_TransportHandle *ath,
298 const struct GNUNET_PeerIdentity *pid)
300 struct FindContext fc = {
301 .session_id = session_id,
304 GNUNET_CONTAINER_multipeermap_get_multiple (ath->records,
313 * We received a session allocation message from the service.
315 * @param cls the `struct GNUNET_ATS_TransportHandle`
316 * @param m message received
319 handle_ats_session_allocation (void *cls,
320 const struct SessionAllocationMessage *m)
322 struct GNUNET_ATS_TransportHandle *ath = cls;
323 struct GNUNET_ATS_SessionRecord *ar;
326 session_id = ntohl (m->session_id);
327 ar = find_session (ath,
332 /* this can (rarely) happen if ATS changes an sessiones allocation
333 just when the transport service deleted it */
334 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
335 "Allocation ignored, session unknown\n");
338 ath->backoff = GNUNET_TIME_UNIT_ZERO;
339 LOG (GNUNET_ERROR_TYPE_DEBUG,
340 "ATS allocates bandwidth for peer `%s' using address %s\n",
341 GNUNET_i2s (&ar->pid),
343 ath->alloc_cb (ath->alloc_cb_cls,
351 * We encountered an error handling the MQ to the ATS service.
354 * @param cls the `struct GNUNET_ATS_TransportHandle`
355 * @param error details about the error
358 error_handler (void *cls,
359 enum GNUNET_MQ_Error error)
361 struct GNUNET_ATS_TransportHandle *ath = cls;
363 LOG (GNUNET_ERROR_TYPE_DEBUG,
364 "ATS connection died (code %d), reconnecting\n",
366 force_reconnect (ath);
371 * Generate and transmit the `struct SessionAddMessage` for the given
374 * @param ar the session to inform the ATS service about
377 send_add_session_message (const struct GNUNET_ATS_SessionRecord *ar)
379 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
380 struct GNUNET_MQ_Envelope *ev;
381 struct SessionAddMessage *m;
385 return; /* disconnected, skip for now */
386 alen = strlen (ar->address) + 1;
387 ev = GNUNET_MQ_msg_extra (m,
389 (NULL == ar->session)
390 ? GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY
391 : GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD);
393 m->session_id = htonl (ar->slot);
394 properties_hton (&m->properties,
396 GNUNET_memcpy (&m[1],
400 LOG (GNUNET_ERROR_TYPE_DEBUG,
401 "Adding address `%s' for peer `%s'\n",
403 GNUNET_i2s (&ar->pid));
404 GNUNET_MQ_send (ath->mq,
410 * Send ATS information about the session record.
412 * @param cls our `struct GNUNET_ATS_TransportHandle *`, unused
414 * @param value the `struct GNUNET_ATS_SessionRecord *` to add
418 send_add_session_cb (void *cls,
419 const struct GNUNET_PeerIdentity *pid,
422 struct GNUNET_ATS_SessionRecord *ar = value;
426 send_add_session_message (ar);
432 * Re-establish the connection to the ATS service.
434 * @param ath handle to use to re-connect.
437 reconnect (struct GNUNET_ATS_TransportHandle *ath)
439 struct GNUNET_MQ_MessageHandler handlers[] = {
440 GNUNET_MQ_hd_var_size (ats_address_suggestion,
441 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
442 struct AddressSuggestionMessage,
444 GNUNET_MQ_hd_fixed_size (ats_session_allocation,
445 GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION,
446 struct SessionAllocationMessage,
448 GNUNET_MQ_handler_end ()
450 struct GNUNET_MQ_Envelope *ev;
451 struct GNUNET_MessageHeader *init;
453 GNUNET_assert (NULL == ath->mq);
454 ath->mq = GNUNET_CLIENT_connect (ath->cfg,
462 force_reconnect (ath);
465 ev = GNUNET_MQ_msg (init,
466 GNUNET_MESSAGE_TYPE_ATS_START);
467 GNUNET_MQ_send (ath->mq,
471 GNUNET_CONTAINER_multipeermap_iterate (ath->records,
472 &send_add_session_cb,
478 * Initialize the ATS subsystem.
480 * @param cfg configuration to use
481 * @param alloc_cb notification to call whenever the allocation changed
482 * @param alloc_cb_cls closure for @a alloc_cb
483 * @param suggest_cb notification to call whenever the suggestation is made
484 * @param suggest_cb_cls closure for @a suggest_cb
485 * @return ats context
487 struct GNUNET_ATS_TransportHandle *
488 GNUNET_ATS_transport_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
489 GNUNET_ATS_AllocationCallback alloc_cb,
491 GNUNET_ATS_SuggestionCallback suggest_cb,
492 void *suggest_cb_cls)
494 struct GNUNET_ATS_TransportHandle *ath;
496 ath = GNUNET_new (struct GNUNET_ATS_TransportHandle);
498 ath->suggest_cb = suggest_cb;
499 ath->suggest_cb_cls = suggest_cb_cls;
500 ath->alloc_cb = alloc_cb;
501 ath->alloc_cb_cls = alloc_cb_cls;
502 ath->records = GNUNET_CONTAINER_multipeermap_create (128,
510 * Release memory associated with the session record.
514 * @param value a `struct GNUNET_ATS_SessionRecord`
518 free_record (void *cls,
519 const struct GNUNET_PeerIdentity *pid,
522 struct GNUNET_ATS_SessionRecord *ar = value;
532 * Client is done with ATS transport, release resources.
534 * @param ath handle to release
537 GNUNET_ATS_transport_done (struct GNUNET_ATS_TransportHandle *ath)
541 GNUNET_MQ_destroy (ath->mq);
544 if (NULL != ath->task)
546 GNUNET_SCHEDULER_cancel (ath->task);
549 GNUNET_CONTAINER_multipeermap_iterate (ath->records,
552 GNUNET_CONTAINER_multipeermap_destroy (ath->records);
558 * We have a new session ATS should know. Sessiones have to be added
559 * with this function before they can be: updated, set in use and
563 * @param pid peer we connected to
564 * @param address the address (human readable version)
565 * @param session transport-internal handle for the session/queue, NULL if
566 * the session is inbound-only
567 * @param prop performance data for the session
568 * @return handle to the session representation inside ATS, NULL
569 * on error (i.e. ATS knows this exact session already)
571 struct GNUNET_ATS_SessionRecord *
572 GNUNET_ATS_session_add (struct GNUNET_ATS_TransportHandle *ath,
573 const struct GNUNET_PeerIdentity *pid,
575 struct GNUNET_ATS_Session *session,
576 const struct GNUNET_ATS_Properties *prop)
578 struct GNUNET_ATS_SessionRecord *ar;
584 /* we need a valid address */
588 alen = strlen (address) + 1;
589 if ( (alen + sizeof (struct SessionAddMessage) >= GNUNET_MAX_MESSAGE_SIZE) ||
590 (alen >= GNUNET_MAX_MESSAGE_SIZE) )
592 /* address too large for us, this should not happen */
597 /* Spin 's' until we find an unused session ID for this pid */
598 for (s = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
600 NULL != find_session (ath,
605 alen = strlen (address) + 1;
606 ar = GNUNET_malloc (sizeof (struct GNUNET_ATS_SessionRecord) + alen);
609 ar->session = session;
610 ar->address = (const char *) &ar[1];
612 ar->properties = *prop;
616 (void) GNUNET_CONTAINER_multipeermap_put (ath->records,
619 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
620 send_add_session_message (ar);
626 * We have updated performance statistics for a given session. Note
627 * that this function can be called for sessiones that are currently
628 * in use as well as sessiones that are valid but not actively in use.
629 * Furthermore, the peer may not even be connected to us right now (in
630 * which case the call may be ignored or the information may be stored
631 * for later use). Update bandwidth assignments.
633 * @param ar session record to update information for
634 * @param prop performance data for the session
637 GNUNET_ATS_session_update (struct GNUNET_ATS_SessionRecord *ar,
638 const struct GNUNET_ATS_Properties *prop)
640 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
641 struct GNUNET_MQ_Envelope *ev;
642 struct SessionUpdateMessage *m;
644 LOG (GNUNET_ERROR_TYPE_DEBUG,
645 "Updating address `%s' for peer `%s'\n",
647 GNUNET_i2s (&ar->pid));
648 ar->properties = *prop;
650 return; /* disconnected, skip for now */
651 ev = GNUNET_MQ_msg (m,
652 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE);
653 m->session_id = htonl (ar->slot);
655 properties_hton (&m->properties,
657 GNUNET_MQ_send (ath->mq,
663 * A session was destroyed, ATS should now schedule and
664 * allocate under the assumption that this @a ar is no
667 * @param ar session record to drop
670 GNUNET_ATS_session_del (struct GNUNET_ATS_SessionRecord *ar)
672 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
673 struct GNUNET_MQ_Envelope *ev;
674 struct SessionDelMessage *m;
676 LOG (GNUNET_ERROR_TYPE_DEBUG,
677 "Deleting address `%s' for peer `%s'\n",
679 GNUNET_i2s (&ar->pid));
682 ev = GNUNET_MQ_msg (m,
683 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL);
684 m->session_id = htonl (ar->slot);
686 GNUNET_MQ_send (ath->mq,
691 /* end of ats_api2_transport.c */