aboutsummaryrefslogtreecommitdiff
path: root/src/ats/ats_api2_transport.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ats/ats_api2_transport.c')
-rw-r--r--src/ats/ats_api2_transport.c689
1 files changed, 0 insertions, 689 deletions
diff --git a/src/ats/ats_api2_transport.c b/src/ats/ats_api2_transport.c
deleted file mode 100644
index da02ca592..000000000
--- a/src/ats/ats_api2_transport.c
+++ /dev/null
@@ -1,689 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2010-2015, 2018 GNUnet e.V.
4
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.
9
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.
14
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/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file ats/ats_api2_transport.c
22 * @brief address suggestions and bandwidth allocation
23 * @author Christian Grothoff
24 * @author Matthias Wachs
25 */
26#include "platform.h"
27#include "gnunet_ats_transport_service.h"
28#include "ats2.h"
29
30#define LOG(kind, ...) GNUNET_log_from (kind, "ats-transport-api", __VA_ARGS__)
31
32
33/**
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.
37 */
38struct GNUNET_ATS_SessionRecord
39{
40 /**
41 * Transport handle this session record belongs to.
42 */
43 struct GNUNET_ATS_TransportHandle *ath;
44
45 /**
46 * Address data.
47 */
48 const char *address;
49
50 /**
51 * Session handle, NULL if inbound-only (also implies we cannot
52 * actually control inbound traffic via transport!). So if
53 * @e session is NULL, the @e properties are informative for
54 * ATS (connection exists, utilization) but ATS cannot directly
55 * influence it (and should thus not call the
56 * #GNUNET_ATS_AllocationCallback for this @e session, which is
57 * obvious as NULL is not a meaningful session to allocation
58 * resources to).
59 */
60 struct GNUNET_ATS_Session *session;
61
62 /**
63 * Identity of the peer reached at @e address.
64 */
65 struct GNUNET_PeerIdentity pid;
66
67 /**
68 * Performance data about the @e session.
69 */
70 struct GNUNET_ATS_Properties properties;
71
72 /**
73 * Unique ID to identify this session at this @a pid in IPC
74 * messages.
75 */
76 uint32_t slot;
77};
78
79
80/**
81 * Handle to the ATS subsystem for bandwidth/transport transport information.
82 */
83struct GNUNET_ATS_TransportHandle
84{
85 /**
86 * Our configuration.
87 */
88 const struct GNUNET_CONFIGURATION_Handle *cfg;
89
90 /**
91 * Callback to invoke on suggestions.
92 */
93 GNUNET_ATS_SuggestionCallback suggest_cb;
94
95 /**
96 * Closure for @e suggest_cb.
97 */
98 void *suggest_cb_cls;
99
100 /**
101 * Callback to invoke on allocations.
102 */
103 GNUNET_ATS_AllocationCallback alloc_cb;
104
105 /**
106 * Closure for @e alloc_cb.
107 */
108 void *alloc_cb_cls;
109
110 /**
111 * Message queue for sending requests to the ATS service.
112 */
113 struct GNUNET_MQ_Handle *mq;
114
115 /**
116 * Task to trigger reconnect.
117 */
118 struct GNUNET_SCHEDULER_Task *task;
119
120 /**
121 * Hash map mapping PIDs to session records.
122 */
123 struct GNUNET_CONTAINER_MultiPeerMap *records;
124
125 /**
126 * Reconnect backoff delay.
127 */
128 struct GNUNET_TIME_Relative backoff;
129};
130
131
132
133/**
134 * Convert ATS properties from host to network byte order.
135 *
136 * @param nbo[OUT] value written
137 * @param hbo value read
138 */
139static void
140properties_hton (struct PropertiesNBO *nbo,
141 const struct GNUNET_ATS_Properties *hbo)
142{
143 nbo->delay = GNUNET_TIME_relative_hton (hbo->delay);
144 nbo->goodput_out = htonl (hbo->goodput_out);
145 nbo->goodput_in = htonl (hbo->goodput_in);
146 nbo->utilization_out = htonl (hbo->utilization_out);
147 nbo->utilization_in = htonl (hbo->utilization_in);
148 nbo->distance = htonl (hbo->distance);
149 nbo->mtu = htonl (hbo->mtu);
150 nbo->nt = htonl ((uint32_t) hbo->nt);
151 nbo->cc = htonl ((uint32_t) hbo->cc);
152}
153
154
155/**
156 * Re-establish the connection to the ATS service.
157 *
158 * @param sh handle to use to re-connect.
159 */
160static void
161reconnect (struct GNUNET_ATS_TransportHandle *ath);
162
163
164/**
165 * Re-establish the connection to the ATS service.
166 *
167 * @param cls handle to use to re-connect.
168 */
169static void
170reconnect_task (void *cls)
171{
172 struct GNUNET_ATS_TransportHandle *ath = cls;
173
174 ath->task = NULL;
175 reconnect (ath);
176}
177
178
179/**
180 * Disconnect from ATS and then reconnect.
181 *
182 * @param ath our handle
183 */
184static void
185force_reconnect (struct GNUNET_ATS_TransportHandle *ath)
186{
187 if (NULL != ath->mq)
188 {
189 GNUNET_MQ_destroy (ath->mq);
190 ath->mq = NULL;
191 }
192 /* FIXME: do we tell transport service about disconnect events? CON:
193 initially ATS will have a really screwed picture of the world and
194 the rapid change would be bad. PRO: if we don't, ATS and
195 transport may disagree about the allocation for a while...
196 For now: lazy: do nothing. */
197 ath->backoff = GNUNET_TIME_STD_BACKOFF (ath->backoff);
198 ath->task = GNUNET_SCHEDULER_add_delayed (ath->backoff,
199 &reconnect_task,
200 ath);
201}
202
203
204/**
205 * Check format of address suggestion message from the service.
206 *
207 * @param cls the `struct GNUNET_ATS_TransportHandle`
208 * @param m message received
209 */
210static int
211check_ats_address_suggestion (void *cls,
212 const struct AddressSuggestionMessage *m)
213{
214 (void) cls;
215 GNUNET_MQ_check_zero_termination (m);
216 return GNUNET_SYSERR;
217}
218
219
220/**
221 * We received an address suggestion message from the service.
222 *
223 * @param cls the `struct GNUNET_ATS_TransportHandle`
224 * @param m message received
225 */
226static void
227handle_ats_address_suggestion (void *cls,
228 const struct AddressSuggestionMessage *m)
229{
230 struct GNUNET_ATS_TransportHandle *ath = cls;
231 const char *address = (const char *) &m[1];
232
233 ath->suggest_cb (ath->suggest_cb_cls,
234 &m->peer,
235 address);
236}
237
238
239/**
240 * Closure for #match_session_cb.
241 */
242struct FindContext
243{
244 /**
245 * Key to look for.
246 */
247 uint32_t session_id;
248
249 /**
250 * Where to store the result.
251 */
252 struct GNUNET_ATS_SessionRecord *sr;
253};
254
255
256/**
257 * Finds matching session record.
258 *
259 * @param cls a `struct FindContext`
260 * @param pid peer identity (unused)
261 * @param value a `struct GNUNET_ATS_SessionRecord`
262 * @return #GNUNET_NO if match found, #GNUNET_YES to continue searching
263 */
264static int
265match_session_cb (void *cls,
266 const struct GNUNET_PeerIdentity *pid,
267 void *value)
268{
269 struct FindContext *fc = cls;
270 struct GNUNET_ATS_SessionRecord *sr = value;
271
272 (void) pid;
273 if (fc->session_id == sr->slot)
274 {
275 fc->sr = sr;
276 return GNUNET_NO;
277 }
278 return GNUNET_YES;
279}
280
281
282
283/**
284 * Find session record for peer @a pid and session @a session_id
285 *
286 * @param ath transport handle to search
287 * @param session_id session ID to match
288 * @param pid peer to search under
289 * @return NULL if no such record exists
290 */
291static struct GNUNET_ATS_SessionRecord *
292find_session (struct GNUNET_ATS_TransportHandle *ath,
293 uint32_t session_id,
294 const struct GNUNET_PeerIdentity *pid)
295{
296 struct FindContext fc = {
297 .session_id = session_id,
298 .sr = NULL
299 };
300
301 GNUNET_CONTAINER_multipeermap_get_multiple (ath->records,
302 pid,
303 &match_session_cb,
304 &fc);
305 return fc.sr;
306}
307
308
309/**
310 * We received a session allocation message from the service.
311 *
312 * @param cls the `struct GNUNET_ATS_TransportHandle`
313 * @param m message received
314 */
315static void
316handle_ats_session_allocation (void *cls,
317 const struct SessionAllocationMessage *m)
318{
319 struct GNUNET_ATS_TransportHandle *ath = cls;
320 struct GNUNET_ATS_SessionRecord *ar;
321 uint32_t session_id;
322
323 session_id = ntohl (m->session_id);
324 ar = find_session (ath,
325 session_id,
326 &m->peer);
327 if (NULL == ar)
328 {
329 /* this can (rarely) happen if ATS changes an sessiones allocation
330 just when the transport service deleted it */
331 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
332 "Allocation ignored, session unknown\n");
333 return;
334 }
335 ath->backoff = GNUNET_TIME_UNIT_ZERO;
336 LOG (GNUNET_ERROR_TYPE_DEBUG,
337 "ATS allocates bandwidth for peer `%s' using address %s\n",
338 GNUNET_i2s (&ar->pid),
339 ar->address);
340 ath->alloc_cb (ath->alloc_cb_cls,
341 ar->session,
342 m->bandwidth_out,
343 m->bandwidth_in);
344}
345
346
347/**
348 * We encountered an error handling the MQ to the ATS service.
349 * Reconnect.
350 *
351 * @param cls the `struct GNUNET_ATS_TransportHandle`
352 * @param error details about the error
353 */
354static void
355error_handler (void *cls,
356 enum GNUNET_MQ_Error error)
357{
358 struct GNUNET_ATS_TransportHandle *ath = cls;
359
360 LOG (GNUNET_ERROR_TYPE_DEBUG,
361 "ATS connection died (code %d), reconnecting\n",
362 (int) error);
363 force_reconnect (ath);
364}
365
366
367/**
368 * Generate and transmit the `struct SessionAddMessage` for the given
369 * session record.
370 *
371 * @param ar the session to inform the ATS service about
372 */
373static void
374send_add_session_message (const struct GNUNET_ATS_SessionRecord *ar)
375{
376 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
377 struct GNUNET_MQ_Envelope *ev;
378 struct SessionAddMessage *m;
379 size_t alen;
380
381 if (NULL == ath->mq)
382 return; /* disconnected, skip for now */
383 alen = strlen (ar->address) + 1;
384 ev = GNUNET_MQ_msg_extra (m,
385 alen,
386 (NULL == ar->session)
387 ? GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY
388 : GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD);
389 m->peer = ar->pid;
390 m->session_id = htonl (ar->slot);
391 properties_hton (&m->properties,
392 &ar->properties);
393 GNUNET_memcpy (&m[1],
394 ar->address,
395 alen);
396
397 LOG (GNUNET_ERROR_TYPE_DEBUG,
398 "Adding address `%s' for peer `%s'\n",
399 ar->address,
400 GNUNET_i2s (&ar->pid));
401 GNUNET_MQ_send (ath->mq,
402 ev);
403}
404
405
406/**
407 * Send ATS information about the session record.
408 *
409 * @param cls our `struct GNUNET_ATS_TransportHandle *`, unused
410 * @param pid unused
411 * @param value the `struct GNUNET_ATS_SessionRecord *` to add
412 * @return #GNUNET_OK
413 */
414static int
415send_add_session_cb (void *cls,
416 const struct GNUNET_PeerIdentity *pid,
417 void *value)
418{
419 struct GNUNET_ATS_SessionRecord *ar = value;
420
421 (void) cls;
422 (void) pid;
423 send_add_session_message (ar);
424 return GNUNET_OK;
425}
426
427
428/**
429 * Re-establish the connection to the ATS service.
430 *
431 * @param ath handle to use to re-connect.
432 */
433static void
434reconnect (struct GNUNET_ATS_TransportHandle *ath)
435{
436 struct GNUNET_MQ_MessageHandler handlers[] = {
437 GNUNET_MQ_hd_var_size (ats_address_suggestion,
438 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION,
439 struct AddressSuggestionMessage,
440 ath),
441 GNUNET_MQ_hd_fixed_size (ats_session_allocation,
442 GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION,
443 struct SessionAllocationMessage,
444 ath),
445 GNUNET_MQ_handler_end ()
446 };
447 struct GNUNET_MQ_Envelope *ev;
448 struct GNUNET_MessageHeader *init;
449
450 GNUNET_assert (NULL == ath->mq);
451 ath->mq = GNUNET_CLIENT_connect (ath->cfg,
452 "ats",
453 handlers,
454 &error_handler,
455 ath);
456 if (NULL == ath->mq)
457 {
458 GNUNET_break (0);
459 force_reconnect (ath);
460 return;
461 }
462 ev = GNUNET_MQ_msg (init,
463 GNUNET_MESSAGE_TYPE_ATS_START);
464 GNUNET_MQ_send (ath->mq,
465 ev);
466 if (NULL == ath->mq)
467 return;
468 GNUNET_CONTAINER_multipeermap_iterate (ath->records,
469 &send_add_session_cb,
470 ath);
471}
472
473
474/**
475 * Initialize the ATS subsystem.
476 *
477 * @param cfg configuration to use
478 * @param alloc_cb notification to call whenever the allocation changed
479 * @param alloc_cb_cls closure for @a alloc_cb
480 * @param suggest_cb notification to call whenever the suggestation is made
481 * @param suggest_cb_cls closure for @a suggest_cb
482 * @return ats context
483 */
484struct GNUNET_ATS_TransportHandle *
485GNUNET_ATS_transport_init (const struct GNUNET_CONFIGURATION_Handle *cfg,
486 GNUNET_ATS_AllocationCallback alloc_cb,
487 void *alloc_cb_cls,
488 GNUNET_ATS_SuggestionCallback suggest_cb,
489 void *suggest_cb_cls)
490{
491 struct GNUNET_ATS_TransportHandle *ath;
492
493 ath = GNUNET_new (struct GNUNET_ATS_TransportHandle);
494 ath->cfg = cfg;
495 ath->suggest_cb = suggest_cb;
496 ath->suggest_cb_cls = suggest_cb_cls;
497 ath->alloc_cb = alloc_cb;
498 ath->alloc_cb_cls = alloc_cb_cls;
499 ath->records = GNUNET_CONTAINER_multipeermap_create (128,
500 GNUNET_YES);
501 reconnect (ath);
502 return ath;
503}
504
505
506/**
507 * Release memory associated with the session record.
508 *
509 * @param cls NULL
510 * @param pid unused
511 * @param value a `struct GNUNET_ATS_SessionRecord`
512 * @return #GNUNET_OK
513 */
514static int
515free_record (void *cls,
516 const struct GNUNET_PeerIdentity *pid,
517 void *value)
518{
519 struct GNUNET_ATS_SessionRecord *ar = value;
520
521 (void) cls;
522 (void) pid;
523 GNUNET_free (ar);
524 return GNUNET_OK;
525}
526
527
528/**
529 * Client is done with ATS transport, release resources.
530 *
531 * @param ath handle to release
532 */
533void
534GNUNET_ATS_transport_done (struct GNUNET_ATS_TransportHandle *ath)
535{
536 if (NULL != ath->mq)
537 {
538 GNUNET_MQ_destroy (ath->mq);
539 ath->mq = NULL;
540 }
541 if (NULL != ath->task)
542 {
543 GNUNET_SCHEDULER_cancel (ath->task);
544 ath->task = NULL;
545 }
546 GNUNET_CONTAINER_multipeermap_iterate (ath->records,
547 &free_record,
548 NULL);
549 GNUNET_CONTAINER_multipeermap_destroy (ath->records);
550 GNUNET_free (ath);
551}
552
553
554/**
555 * We have a new session ATS should know. Sessiones have to be added
556 * with this function before they can be: updated, set in use and
557 * destroyed.
558 *
559 * @param ath handle
560 * @param pid peer we connected to
561 * @param address the address (human readable version)
562 * @param session transport-internal handle for the session/queue, NULL if
563 * the session is inbound-only
564 * @param prop performance data for the session
565 * @return handle to the session representation inside ATS, NULL
566 * on error (i.e. ATS knows this exact session already)
567 */
568struct GNUNET_ATS_SessionRecord *
569GNUNET_ATS_session_add (struct GNUNET_ATS_TransportHandle *ath,
570 const struct GNUNET_PeerIdentity *pid,
571 const char *address,
572 struct GNUNET_ATS_Session *session,
573 const struct GNUNET_ATS_Properties *prop)
574{
575 struct GNUNET_ATS_SessionRecord *ar;
576 uint32_t s;
577 size_t alen;
578
579 if (NULL == address)
580 {
581 /* we need a valid address */
582 GNUNET_break (0);
583 return NULL;
584 }
585 alen = strlen (address) + 1;
586 if ((alen + sizeof(struct SessionAddMessage) >= GNUNET_MAX_MESSAGE_SIZE) ||
587 (alen >= GNUNET_MAX_MESSAGE_SIZE))
588 {
589 /* address too large for us, this should not happen */
590 GNUNET_break (0);
591 return NULL;
592 }
593
594 /* Spin 's' until we find an unused session ID for this pid */
595 for (s = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
596 UINT32_MAX);
597 NULL != find_session (ath,
598 s,
599 pid);
600 s++)
601 ;
602
603 alen = strlen (address) + 1;
604 ar = GNUNET_malloc (sizeof(struct GNUNET_ATS_SessionRecord) + alen);
605 ar->ath = ath;
606 ar->slot = s;
607 ar->session = session;
608 ar->address = (const char *) &ar[1];
609 ar->pid = *pid;
610 ar->properties = *prop;
611 memcpy (&ar[1],
612 address,
613 alen);
614 (void) GNUNET_CONTAINER_multipeermap_put (ath->records,
615 &ar->pid,
616 ar,
617 GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
618 send_add_session_message (ar);
619 return ar;
620}
621
622
623/**
624 * We have updated performance statistics for a given session. Note
625 * that this function can be called for sessiones that are currently
626 * in use as well as sessiones that are valid but not actively in use.
627 * Furthermore, the peer may not even be connected to us right now (in
628 * which case the call may be ignored or the information may be stored
629 * for later use). Update bandwidth assignments.
630 *
631 * @param ar session record to update information for
632 * @param prop performance data for the session
633 */
634void
635GNUNET_ATS_session_update (struct GNUNET_ATS_SessionRecord *ar,
636 const struct GNUNET_ATS_Properties *prop)
637{
638 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
639 struct GNUNET_MQ_Envelope *ev;
640 struct SessionUpdateMessage *m;
641
642 LOG (GNUNET_ERROR_TYPE_DEBUG,
643 "Updating address `%s' for peer `%s'\n",
644 ar->address,
645 GNUNET_i2s (&ar->pid));
646 ar->properties = *prop;
647 if (NULL == ath->mq)
648 return; /* disconnected, skip for now */
649 ev = GNUNET_MQ_msg (m,
650 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE);
651 m->session_id = htonl (ar->slot);
652 m->peer = ar->pid;
653 properties_hton (&m->properties,
654 &ar->properties);
655 GNUNET_MQ_send (ath->mq,
656 ev);
657}
658
659
660/**
661 * A session was destroyed, ATS should now schedule and
662 * allocate under the assumption that this @a ar is no
663 * longer in use.
664 *
665 * @param ar session record to drop
666 */
667void
668GNUNET_ATS_session_del (struct GNUNET_ATS_SessionRecord *ar)
669{
670 struct GNUNET_ATS_TransportHandle *ath = ar->ath;
671 struct GNUNET_MQ_Envelope *ev;
672 struct SessionDelMessage *m;
673
674 LOG (GNUNET_ERROR_TYPE_DEBUG,
675 "Deleting address `%s' for peer `%s'\n",
676 ar->address,
677 GNUNET_i2s (&ar->pid));
678 if (NULL == ath->mq)
679 return;
680 ev = GNUNET_MQ_msg (m,
681 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL);
682 m->session_id = htonl (ar->slot);
683 m->peer = ar->pid;
684 GNUNET_MQ_send (ath->mq,
685 ev);
686}
687
688
689/* end of ats_api2_transport.c */