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/>.
19 * @file ats/gnunet-service-ats-new.c
21 * @author Matthias Wachs
22 * @author Christian Grothoff
25 * - implement unloading of ATS plugins
28 #include "gnunet_util_lib.h"
29 #include "gnunet_statistics_service.h"
30 #include "gnunet_ats_plugin_new.h"
35 * What type of client is this client?
56 * Information we track per client.
61 * Preferences expressed by a client are kept in a DLL per client.
63 struct ClientPreference
68 struct ClientPreference *next;
73 struct ClientPreference *prev;
76 * Which client expressed the preference?
78 struct Client *client;
81 * Plugin's representation of the preference.
83 struct GNUNET_ATS_PreferenceHandle *ph;
86 * Details about the preference.
88 struct GNUNET_ATS_Preference pref;
93 * Information about ongoing sessions of the transport client.
95 struct GNUNET_ATS_Session
99 * Session data exposed to the plugin.
101 struct GNUNET_ATS_SessionData data;
104 * The transport client that provided the session.
106 struct Client *client;
109 * Session state in the plugin.
111 struct GNUNET_ATS_SessionHandle *sh;
114 * Unique ID for the session when talking with the client.
122 * Information we track per client.
127 * Type of the client, initially #CT_NONE.
129 enum ClientType type;
132 * Service handle of the client.
134 struct GNUNET_SERVICE_Client *client;
137 * Message queue to talk to the client.
139 struct GNUNET_MQ_Handle *mq;
142 * Details depending on @e type.
149 * Head of DLL of preferences expressed by this client.
151 struct ClientPreference *cp_head;
154 * Tail of DLL of preferences expressed by this client.
156 struct ClientPreference *cp_tail;
163 * Map from session IDs to `struct GNUNET_ATS_Session` objects.
165 struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
175 * Handle for statistics.
177 static struct GNUNET_STATISTICS_Handle *stats;
182 static struct GNUNET_ATS_SolverFunctions *plugin;
185 * Solver plugin name as string
187 static char *plugin_name;
190 * The transport client (there can only be one at a time).
192 static struct Client *transport_client;
196 * Function called by the solver to prompt the transport to
197 * try out a new address.
199 * @param cls closure, NULL
200 * @param pid peer this is about
201 * @param address address the transport should try
204 suggest_cb (void *cls,
205 const struct GNUNET_PeerIdentity *pid,
208 struct GNUNET_MQ_Envelope *env;
209 size_t slen = strlen (address) + 1;
210 struct AddressSuggestionMessage *as;
212 if (NULL == transport_client)
217 env = GNUNET_MQ_msg_extra (as,
219 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
224 GNUNET_MQ_send (transport_client->mq,
230 * Function called by the solver to tell the transpor to
231 * allocate bandwidth for the specified session.
233 * @param cls closure, NULL
234 * @param session session this is about
235 * @param peer peer this is about
236 * @param bw_in suggested bandwidth for receiving
237 * @param bw_out suggested bandwidth for transmission
240 allocate_cb (void *cls,
241 struct GNUNET_ATS_Session *session,
242 const struct GNUNET_PeerIdentity *peer,
243 struct GNUNET_BANDWIDTH_Value32NBO bw_in,
244 struct GNUNET_BANDWIDTH_Value32NBO bw_out)
246 struct GNUNET_MQ_Envelope *env;
247 struct SessionAllocationMessage *sam;
250 if ( (NULL == transport_client) ||
251 (session->client != transport_client) )
253 /* transport must have just died and solver is addressing the
254 losses of sessions (possibly of previous transport), ignore! */
257 env = GNUNET_MQ_msg (sam,
258 GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION);
259 sam->session_id = session->session_id;
261 sam->bandwidth_in = bw_in;
262 sam->bandwidth_out = bw_out;
263 GNUNET_MQ_send (transport_client->mq,
269 * Convert @a properties to @a prop
271 * @param properties in NBO
272 * @param prop[out] in HBO
275 prop_ntoh (const struct PropertiesNBO *properties,
276 struct GNUNET_ATS_Properties *prop)
278 prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
279 prop->goodput_out = ntohl (properties->goodput_out);
280 prop->goodput_in = ntohl (properties->goodput_in);
281 prop->utilization_out = ntohl (properties->utilization_out);
282 prop->utilization_in = ntohl (properties->utilization_in);
283 prop->distance = ntohl (properties->distance);
284 prop->mtu = ntohl (properties->mtu);
285 prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
286 prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
291 * We have received a `struct ExpressPreferenceMessage` from an application client.
293 * @param cls handle to the client
294 * @param msg the start message
297 handle_suggest (void *cls,
298 const struct ExpressPreferenceMessage *msg)
300 struct Client *c = cls;
301 struct ClientPreference *cp;
303 if (CT_NONE == c->type)
304 c->type = CT_APPLICATION;
305 if (CT_APPLICATION != c->type)
308 GNUNET_SERVICE_client_drop (c->client);
311 cp = GNUNET_new (struct ClientPreference);
313 cp->pref.peer = msg->peer;
314 cp->pref.bw = msg->bw;
315 cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
316 cp->ph = plugin->preference_add (plugin->cls,
318 GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
319 c->details.application.cp_tail,
321 GNUNET_SERVICE_client_continue (c->client);
326 * We have received a `struct ExpressPreferenceMessage` from an application client.
328 * @param cls handle to the client
329 * @param msg the start message
332 handle_suggest_cancel (void *cls,
333 const struct ExpressPreferenceMessage *msg)
335 struct Client *c = cls;
336 struct ClientPreference *cp;
338 if (CT_NONE == c->type)
339 c->type = CT_APPLICATION;
340 if (CT_APPLICATION != c->type)
343 GNUNET_SERVICE_client_drop (c->client);
346 for (cp = c->details.application.cp_head;
349 if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
350 (cp->pref.bw.value__ == msg->bw.value__) &&
351 (0 == memcmp (&cp->pref.peer,
353 sizeof (struct GNUNET_PeerIdentity))) )
358 GNUNET_SERVICE_client_drop (c->client);
361 plugin->preference_del (plugin->cls,
364 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
365 c->details.application.cp_tail,
368 GNUNET_SERVICE_client_continue (c->client);
373 * Handle 'start' messages from transport clients.
375 * @param cls client that sent the request
376 * @param message the request message
379 handle_start (void *cls,
380 const struct GNUNET_MessageHeader *hdr)
382 struct Client *c = cls;
384 if (CT_NONE != c->type)
387 GNUNET_SERVICE_client_drop (c->client);
390 c->type = CT_TRANSPORT;
391 c->details.transport.sessions
392 = GNUNET_CONTAINER_multihashmap32_create (128);
393 if (NULL != transport_client)
395 GNUNET_SERVICE_client_drop (transport_client->client);
396 transport_client = NULL;
398 transport_client = c;
399 GNUNET_SERVICE_client_continue (c->client);
404 * Check 'session_add' message is well-formed and comes from a
407 * @param cls client that sent the request
408 * @param message the request message
409 * @return #GNUNET_OK if @a message is well-formed
412 check_session_add (void *cls,
413 const struct SessionAddMessage *message)
415 struct Client *c = cls;
417 GNUNET_MQ_check_zero_termination (message);
418 if (CT_TRANSPORT != c->type)
421 return GNUNET_SYSERR;
428 * Handle 'session add' messages from transport clients.
430 * @param cls client that sent the request
431 * @param message the request message
434 handle_session_add (void *cls,
435 const struct SessionAddMessage *message)
437 struct Client *c = cls;
438 const char *address = (const char *) &message[1];
439 struct GNUNET_ATS_Session *session;
440 int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
441 ntohs (message->header.type));
443 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
444 message->session_id);
448 GNUNET_SERVICE_client_drop (c->client);
451 session = GNUNET_new (struct GNUNET_ATS_Session);
452 session->data.session = session;
454 session->session_id = message->session_id;
455 session->data.peer = message->peer;
456 prop_ntoh (&message->properties,
457 &session->data.prop);
458 session->data.inbound_only = inbound_only;
459 GNUNET_assert (GNUNET_YES ==
460 GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
463 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
464 session->sh = plugin->session_add (plugin->cls,
467 GNUNET_SERVICE_client_continue (c->client);
472 * Handle 'session update' messages from transport clients.
474 * @param cls client that sent the request
475 * @param msg the request message
478 handle_session_update (void *cls,
479 const struct SessionUpdateMessage *msg)
481 struct Client *c = cls;
482 struct GNUNET_ATS_Session *session;
484 if (CT_TRANSPORT != c->type)
487 GNUNET_SERVICE_client_drop (c->client);
490 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
495 GNUNET_SERVICE_client_drop (c->client);
498 prop_ntoh (&msg->properties,
499 &session->data.prop);
500 plugin->session_update (plugin->cls,
503 GNUNET_SERVICE_client_continue (c->client);
508 * Handle 'session delete' messages from transport clients.
510 * @param cls client that sent the request
511 * @param message the request message
514 handle_session_del (void *cls,
515 const struct SessionDelMessage *message)
517 struct Client *c = cls;
518 struct GNUNET_ATS_Session *session;
520 if (CT_TRANSPORT != c->type)
523 GNUNET_SERVICE_client_drop (c->client);
526 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
527 message->session_id);
531 GNUNET_SERVICE_client_drop (c->client);
534 plugin->session_del (plugin->cls,
537 GNUNET_assert (GNUNET_YES ==
538 GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
541 GNUNET_free (session);
542 GNUNET_SERVICE_client_continue (c->client);
547 * A client connected to us. Setup the local client
551 * @param client handle of the client
552 * @param mq message queue to talk to @a client
556 client_connect_cb (void *cls,
557 struct GNUNET_SERVICE_Client *client,
558 struct GNUNET_MQ_Handle *mq)
560 struct Client *c = GNUNET_new (struct Client);
569 * Function called on each session to release associated state
570 * on transport disconnect.
572 * @param cls the `struct Client`
573 * @param key unused (session_id)
574 * @param value a `struct GNUNET_ATS_Session`
577 free_session (void *cls,
581 struct Client *c = cls;
582 struct GNUNET_ATS_Session *session = value;
585 GNUNET_assert (c == session->client);
586 plugin->session_del (plugin->cls,
589 GNUNET_free (session);
595 * A client disconnected from us. Tear down the local client
599 * @param client handle of the client
600 * @param app_ctx our `struct Client`
603 client_disconnect_cb (void *cls,
604 struct GNUNET_SERVICE_Client *client,
607 struct Client *c = app_ctx;
610 GNUNET_assert (c->client == client);
616 for (struct ClientPreference *cp = c->details.application.cp_head;
618 cp = c->details.application.cp_head)
620 plugin->preference_del (plugin->cls,
623 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
624 c->details.application.cp_tail,
630 if (transport_client == c)
631 transport_client = NULL;
632 GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
635 GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
643 * Task run during shutdown.
648 cleanup_task (void *cls)
650 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
651 "ATS shutdown initiated\n");
654 GNUNET_STATISTICS_destroy (stats,
658 if (NULL != plugin_name)
660 GNUNET_free (plugin_name);
667 * Process template requests.
670 * @param cfg configuration to use
671 * @param service the initialized service
675 const struct GNUNET_CONFIGURATION_Handle *cfg,
676 struct GNUNET_SERVICE_Handle *service)
678 static struct GNUNET_ATS_PluginEnvironment env;
681 stats = GNUNET_STATISTICS_create ("ats",
684 GNUNET_CONFIGURATION_get_value_string (cfg,
689 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
690 "No ATS solver configured, using 'simple' approach\n");
691 solver = GNUNET_strdup ("simple");
693 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
698 env.suggest_cb = &suggest_cb;
699 env.allocate_cb = &allocate_cb;
700 GNUNET_asprintf (&plugin_name,
701 "libgnunet_plugin_ats2_%s",
703 GNUNET_free (solver);
704 if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
707 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
708 _("Failed to initialize solver `%s'!\n"),
710 GNUNET_SCHEDULER_shutdown ();
717 * Define "main" method using service macro.
721 GNUNET_SERVICE_OPTION_NONE,
724 &client_disconnect_cb,
726 GNUNET_MQ_hd_fixed_size (suggest,
727 GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
728 struct ExpressPreferenceMessage,
730 GNUNET_MQ_hd_fixed_size (suggest_cancel,
731 GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
732 struct ExpressPreferenceMessage,
734 GNUNET_MQ_hd_fixed_size (start,
735 GNUNET_MESSAGE_TYPE_ATS_START,
736 struct GNUNET_MessageHeader,
738 GNUNET_MQ_hd_var_size (session_add,
739 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
740 struct SessionAddMessage,
742 GNUNET_MQ_hd_var_size (session_add,
743 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
744 struct SessionAddMessage,
746 GNUNET_MQ_hd_fixed_size (session_update,
747 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
748 struct SessionUpdateMessage,
750 GNUNET_MQ_hd_fixed_size (session_del,
751 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
752 struct SessionDelMessage,
754 GNUNET_MQ_handler_end ());
757 /* end of gnunet-service-ats.c */