2 This file is part of GNUnet.
3 Copyright (C) 2011, 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/gnunet-service-ats-new.c
23 * @author Matthias Wachs
24 * @author Christian Grothoff
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_ats_plugin_new.h"
34 * What type of client is this client?
55 * Information we track per client.
60 * Preferences expressed by a client are kept in a DLL per client.
62 struct ClientPreference
67 struct ClientPreference *next;
72 struct ClientPreference *prev;
75 * Which client expressed the preference?
77 struct Client *client;
80 * Plugin's representation of the preference.
82 struct GNUNET_ATS_PreferenceHandle *ph;
85 * Details about the preference.
87 struct GNUNET_ATS_Preference pref;
92 * Information about ongoing sessions of the transport client.
94 struct GNUNET_ATS_Session
98 * Session data exposed to the plugin.
100 struct GNUNET_ATS_SessionData data;
103 * The transport client that provided the session.
105 struct Client *client;
108 * Session state in the plugin.
110 struct GNUNET_ATS_SessionHandle *sh;
113 * Unique ID for the session when talking with the client.
121 * Information we track per client.
126 * Type of the client, initially #CT_NONE.
128 enum ClientType type;
131 * Service handle of the client.
133 struct GNUNET_SERVICE_Client *client;
136 * Message queue to talk to the client.
138 struct GNUNET_MQ_Handle *mq;
141 * Details depending on @e type.
148 * Head of DLL of preferences expressed by this client.
150 struct ClientPreference *cp_head;
153 * Tail of DLL of preferences expressed by this client.
155 struct ClientPreference *cp_tail;
162 * Map from session IDs to `struct GNUNET_ATS_Session` objects.
164 struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
174 * Handle for statistics.
176 static struct GNUNET_STATISTICS_Handle *stats;
181 static struct GNUNET_ATS_SolverFunctions *plugin;
184 * Solver plugin name as string
186 static char *plugin_name;
189 * The transport client (there can only be one at a time).
191 static struct Client *transport_client;
195 * Function called by the solver to prompt the transport to
196 * try out a new address.
198 * @param cls closure, NULL
199 * @param pid peer this is about
200 * @param address address the transport should try
203 suggest_cb (void *cls,
204 const struct GNUNET_PeerIdentity *pid,
207 struct GNUNET_MQ_Envelope *env;
208 size_t slen = strlen (address) + 1;
209 struct AddressSuggestionMessage *as;
211 if (NULL == transport_client)
216 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217 "Suggesting address `%s' of peer `%s'\n",
220 env = GNUNET_MQ_msg_extra (as,
222 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
227 GNUNET_MQ_send (transport_client->mq,
233 * Function called by the solver to tell the transpor to
234 * allocate bandwidth for the specified session.
236 * @param cls closure, NULL
237 * @param session session this is about
238 * @param peer peer this is about
239 * @param bw_in suggested bandwidth for receiving
240 * @param bw_out suggested bandwidth for transmission
243 allocate_cb (void *cls,
244 struct GNUNET_ATS_Session *session,
245 const struct GNUNET_PeerIdentity *peer,
246 struct GNUNET_BANDWIDTH_Value32NBO bw_in,
247 struct GNUNET_BANDWIDTH_Value32NBO bw_out)
249 struct GNUNET_MQ_Envelope *env;
250 struct SessionAllocationMessage *sam;
253 if ( (NULL == transport_client) ||
254 (session->client != transport_client) )
256 /* transport must have just died and solver is addressing the
257 losses of sessions (possibly of previous transport), ignore! */
260 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261 "Allocating %u/%u bytes for %p of peer `%s'\n",
262 ntohl (bw_in.value__),
263 ntohl (bw_out.value__),
266 env = GNUNET_MQ_msg (sam,
267 GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION);
268 sam->session_id = session->session_id;
270 sam->bandwidth_in = bw_in;
271 sam->bandwidth_out = bw_out;
272 GNUNET_MQ_send (transport_client->mq,
278 * Convert @a properties to @a prop
280 * @param properties in NBO
281 * @param prop[out] in HBO
284 prop_ntoh (const struct PropertiesNBO *properties,
285 struct GNUNET_ATS_Properties *prop)
287 prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
288 prop->goodput_out = ntohl (properties->goodput_out);
289 prop->goodput_in = ntohl (properties->goodput_in);
290 prop->utilization_out = ntohl (properties->utilization_out);
291 prop->utilization_in = ntohl (properties->utilization_in);
292 prop->distance = ntohl (properties->distance);
293 prop->mtu = ntohl (properties->mtu);
294 prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
295 prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
300 * We have received a `struct ExpressPreferenceMessage` from an application client.
302 * @param cls handle to the client
303 * @param msg the start message
306 handle_suggest (void *cls,
307 const struct ExpressPreferenceMessage *msg)
309 struct Client *c = cls;
310 struct ClientPreference *cp;
312 if (CT_NONE == c->type)
313 c->type = CT_APPLICATION;
314 if (CT_APPLICATION != c->type)
317 GNUNET_SERVICE_client_drop (c->client);
320 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321 "Client suggested we talk to %s with preference %d at rate %u\n",
322 GNUNET_i2s (&msg->peer),
323 (int) ntohl (msg->pk),
324 (int) ntohl (msg->bw.value__));
325 cp = GNUNET_new (struct ClientPreference);
327 cp->pref.peer = msg->peer;
328 cp->pref.bw = msg->bw;
329 cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
330 cp->ph = plugin->preference_add (plugin->cls,
332 GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
333 c->details.application.cp_tail,
335 GNUNET_SERVICE_client_continue (c->client);
340 * We have received a `struct ExpressPreferenceMessage` from an application client.
342 * @param cls handle to the client
343 * @param msg the start message
346 handle_suggest_cancel (void *cls,
347 const struct ExpressPreferenceMessage *msg)
349 struct Client *c = cls;
350 struct ClientPreference *cp;
352 if (CT_NONE == c->type)
353 c->type = CT_APPLICATION;
354 if (CT_APPLICATION != c->type)
357 GNUNET_SERVICE_client_drop (c->client);
360 for (cp = c->details.application.cp_head;
363 if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
364 (cp->pref.bw.value__ == msg->bw.value__) &&
365 (0 == memcmp (&cp->pref.peer,
367 sizeof (struct GNUNET_PeerIdentity))) )
372 GNUNET_SERVICE_client_drop (c->client);
375 plugin->preference_del (plugin->cls,
378 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
379 c->details.application.cp_tail,
382 GNUNET_SERVICE_client_continue (c->client);
387 * Handle 'start' messages from transport clients.
389 * @param cls client that sent the request
390 * @param message the request message
393 handle_start (void *cls,
394 const struct GNUNET_MessageHeader *hdr)
396 struct Client *c = cls;
398 if (CT_NONE != c->type)
401 GNUNET_SERVICE_client_drop (c->client);
404 c->type = CT_TRANSPORT;
405 c->details.transport.sessions
406 = GNUNET_CONTAINER_multihashmap32_create (128);
407 if (NULL != transport_client)
409 GNUNET_SERVICE_client_drop (transport_client->client);
410 transport_client = NULL;
412 transport_client = c;
413 GNUNET_SERVICE_client_continue (c->client);
418 * Check 'session_add' message is well-formed and comes from a
421 * @param cls client that sent the request
422 * @param message the request message
423 * @return #GNUNET_OK if @a message is well-formed
426 check_session_add (void *cls,
427 const struct SessionAddMessage *message)
429 struct Client *c = cls;
431 GNUNET_MQ_check_zero_termination (message);
432 if (CT_TRANSPORT != c->type)
435 return GNUNET_SYSERR;
442 * Handle 'session add' messages from transport clients.
444 * @param cls client that sent the request
445 * @param message the request message
448 handle_session_add (void *cls,
449 const struct SessionAddMessage *message)
451 struct Client *c = cls;
452 const char *address = (const char *) &message[1];
453 struct GNUNET_ATS_Session *session;
454 int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
455 ntohs (message->header.type));
457 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
458 message->session_id);
462 GNUNET_SERVICE_client_drop (c->client);
465 session = GNUNET_new (struct GNUNET_ATS_Session);
466 session->data.session = session;
468 session->session_id = message->session_id;
469 session->data.peer = message->peer;
470 prop_ntoh (&message->properties,
471 &session->data.prop);
472 session->data.inbound_only = inbound_only;
473 GNUNET_assert (GNUNET_YES ==
474 GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
477 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
478 session->sh = plugin->session_add (plugin->cls,
481 GNUNET_assert (NULL != session->sh);
482 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
483 "Transport has new session %p to %s\n",
485 GNUNET_i2s (&message->peer));
486 GNUNET_SERVICE_client_continue (c->client);
491 * Handle 'session update' messages from transport clients.
493 * @param cls client that sent the request
494 * @param msg the request message
497 handle_session_update (void *cls,
498 const struct SessionUpdateMessage *msg)
500 struct Client *c = cls;
501 struct GNUNET_ATS_Session *session;
503 if (CT_TRANSPORT != c->type)
506 GNUNET_SERVICE_client_drop (c->client);
509 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
514 GNUNET_SERVICE_client_drop (c->client);
517 prop_ntoh (&msg->properties,
518 &session->data.prop);
519 plugin->session_update (plugin->cls,
522 GNUNET_SERVICE_client_continue (c->client);
527 * Handle 'session delete' messages from transport clients.
529 * @param cls client that sent the request
530 * @param message the request message
533 handle_session_del (void *cls,
534 const struct SessionDelMessage *message)
536 struct Client *c = cls;
537 struct GNUNET_ATS_Session *session;
539 if (CT_TRANSPORT != c->type)
542 GNUNET_SERVICE_client_drop (c->client);
545 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
546 message->session_id);
550 GNUNET_SERVICE_client_drop (c->client);
553 GNUNET_assert (NULL != session->sh);
554 plugin->session_del (plugin->cls,
558 GNUNET_assert (GNUNET_YES ==
559 GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
562 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
563 "Transport lost session %p to %s\n",
565 GNUNET_i2s (&session->data.peer));
566 GNUNET_free (session);
567 GNUNET_SERVICE_client_continue (c->client);
572 * A client connected to us. Setup the local client
576 * @param client handle of the client
577 * @param mq message queue to talk to @a client
581 client_connect_cb (void *cls,
582 struct GNUNET_SERVICE_Client *client,
583 struct GNUNET_MQ_Handle *mq)
585 struct Client *c = GNUNET_new (struct Client);
594 * Function called on each session to release associated state
595 * on transport disconnect.
597 * @param cls the `struct Client`
598 * @param key unused (session_id)
599 * @param value a `struct GNUNET_ATS_Session`
602 free_session (void *cls,
606 struct Client *c = cls;
607 struct GNUNET_ATS_Session *session = value;
610 GNUNET_assert (c == session->client);
611 GNUNET_assert (NULL != session->sh);
612 plugin->session_del (plugin->cls,
616 GNUNET_free (session);
622 * A client disconnected from us. Tear down the local client
626 * @param client handle of the client
627 * @param app_ctx our `struct Client`
630 client_disconnect_cb (void *cls,
631 struct GNUNET_SERVICE_Client *client,
634 struct Client *c = app_ctx;
637 GNUNET_assert (c->client == client);
643 for (struct ClientPreference *cp = c->details.application.cp_head;
645 cp = c->details.application.cp_head)
647 plugin->preference_del (plugin->cls,
650 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
651 c->details.application.cp_tail,
657 if (transport_client == c)
658 transport_client = NULL;
659 GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
662 GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
670 * Task run at the end during shutdown.
675 final_cleanup (void *cls)
680 GNUNET_STATISTICS_destroy (stats,
686 GNUNET_PLUGIN_unload (plugin_name,
690 if (NULL != plugin_name)
692 GNUNET_free (plugin_name);
699 * Task run during shutdown.
704 cleanup_task (void *cls)
707 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
708 "ATS shutdown initiated\n");
709 GNUNET_SCHEDULER_add_now (&final_cleanup,
715 * Process template requests.
718 * @param cfg configuration to use
719 * @param service the initialized service
723 const struct GNUNET_CONFIGURATION_Handle *cfg,
724 struct GNUNET_SERVICE_Handle *service)
726 static struct GNUNET_ATS_PluginEnvironment env;
729 stats = GNUNET_STATISTICS_create ("ats",
732 GNUNET_CONFIGURATION_get_value_string (cfg,
737 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
738 "No ATS solver configured, using 'simple' approach\n");
739 solver = GNUNET_strdup ("simple");
741 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
746 env.suggest_cb = &suggest_cb;
747 env.allocate_cb = &allocate_cb;
748 GNUNET_asprintf (&plugin_name,
749 "libgnunet_plugin_ats2_%s",
751 GNUNET_free (solver);
752 if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
755 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
756 _("Failed to initialize solver `%s'!\n"),
758 GNUNET_SCHEDULER_shutdown ();
765 * Define "main" method using service macro.
769 GNUNET_SERVICE_OPTION_NONE,
772 &client_disconnect_cb,
774 GNUNET_MQ_hd_fixed_size (suggest,
775 GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
776 struct ExpressPreferenceMessage,
778 GNUNET_MQ_hd_fixed_size (suggest_cancel,
779 GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
780 struct ExpressPreferenceMessage,
782 GNUNET_MQ_hd_fixed_size (start,
783 GNUNET_MESSAGE_TYPE_ATS_START,
784 struct GNUNET_MessageHeader,
786 GNUNET_MQ_hd_var_size (session_add,
787 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
788 struct SessionAddMessage,
790 GNUNET_MQ_hd_var_size (session_add,
791 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
792 struct SessionAddMessage,
794 GNUNET_MQ_hd_fixed_size (session_update,
795 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
796 struct SessionUpdateMessage,
798 GNUNET_MQ_hd_fixed_size (session_del,
799 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
800 struct SessionDelMessage,
802 GNUNET_MQ_handler_end ());
805 /* end of gnunet-service-ats.c */