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 messages ATS -> transport
26 * - implement loading / unloading of ATS plugins
27 * - expose plugin the API to send messages ATS -> transport
30 #include "gnunet_util_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_ats_plugin_new.h"
37 * What type of client is this client?
58 * Information we track per client.
63 * Preferences expressed by a client are kept in a DLL per client.
65 struct ClientPreference
70 struct ClientPreference *next;
75 struct ClientPreference *prev;
78 * Which client expressed the preference?
80 struct Client *client;
83 * Plugin's representation of the preference.
85 struct GNUNET_ATS_PreferenceHandle *ph;
88 * Details about the preference.
90 struct GNUNET_ATS_Preference pref;
95 * Information about ongoing sessions of the transport client.
97 struct GNUNET_ATS_Session
101 * Session data exposed to the plugin.
103 struct GNUNET_ATS_SessionData data;
106 * The transport client that provided the session.
108 struct Client *client;
111 * Session state in the plugin.
113 struct GNUNET_ATS_SessionHandle *sh;
116 * Unique ID for the session when talking with the client.
124 * Information we track per client.
129 * Type of the client, initially #CT_NONE.
131 enum ClientType type;
134 * Service handle of the client.
136 struct GNUNET_SERVICE_Client *client;
139 * Message queue to talk to the client.
141 struct GNUNET_MQ_Handle *mq;
144 * Details depending on @e type.
151 * Head of DLL of preferences expressed by this client.
153 struct ClientPreference *cp_head;
156 * Tail of DLL of preferences expressed by this client.
158 struct ClientPreference *cp_tail;
165 * Map from session IDs to `struct GNUNET_ATS_Session` objects.
167 struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
177 * Handle for statistics.
179 static struct GNUNET_STATISTICS_Handle *GSA_stats;
184 static struct GNUNET_ATS_SolverFunctions *plugin;
187 * The transport client (there can only be one at a time).
189 static struct Client *transport_client;
193 * Convert @a properties to @a prop
195 * @param properties in NBO
196 * @param prop[out] in HBO
199 prop_ntoh (const struct PropertiesNBO *properties,
200 struct GNUNET_ATS_Properties *prop)
202 prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
203 prop->goodput_out = ntohl (properties->goodput_out);
204 prop->goodput_in = ntohl (properties->goodput_in);
205 prop->utilization_out = ntohl (properties->utilization_out);
206 prop->utilization_in = ntohl (properties->utilization_in);
207 prop->distance = ntohl (properties->distance);
208 prop->mtu = ntohl (properties->mtu);
209 prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
210 prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
215 * We have received a `struct ExpressPreferenceMessage` from an application client.
217 * @param cls handle to the client
218 * @param msg the start message
221 handle_suggest (void *cls,
222 const struct ExpressPreferenceMessage *msg)
224 struct Client *c = cls;
225 struct ClientPreference *cp;
227 if (CT_NONE == c->type)
228 c->type = CT_APPLICATION;
229 if (CT_APPLICATION != c->type)
232 GNUNET_SERVICE_client_drop (c->client);
235 cp = GNUNET_new (struct ClientPreference);
237 cp->pref.peer = msg->peer;
238 cp->pref.bw = msg->bw;
239 cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
240 cp->ph = plugin->preference_add (plugin->cls,
242 GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
243 c->details.application.cp_tail,
245 GNUNET_SERVICE_client_continue (c->client);
250 * We have received a `struct ExpressPreferenceMessage` from an application client.
252 * @param cls handle to the client
253 * @param msg the start message
256 handle_suggest_cancel (void *cls,
257 const struct ExpressPreferenceMessage *msg)
259 struct Client *c = cls;
260 struct ClientPreference *cp;
262 if (CT_NONE == c->type)
263 c->type = CT_APPLICATION;
264 if (CT_APPLICATION != c->type)
267 GNUNET_SERVICE_client_drop (c->client);
270 for (cp = c->details.application.cp_head;
273 if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
274 (cp->pref.bw.value__ == msg->bw.value__) &&
275 (0 == memcmp (&cp->pref.peer,
277 sizeof (struct GNUNET_PeerIdentity))) )
282 GNUNET_SERVICE_client_drop (c->client);
285 plugin->preference_del (plugin->cls,
288 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
289 c->details.application.cp_tail,
292 GNUNET_SERVICE_client_continue (c->client);
297 * Handle 'start' messages from transport clients.
299 * @param cls client that sent the request
300 * @param message the request message
303 handle_start (void *cls,
304 const struct GNUNET_MessageHeader *hdr)
306 struct Client *c = cls;
308 if (CT_NONE != c->type)
311 GNUNET_SERVICE_client_drop (c->client);
314 c->type = CT_TRANSPORT;
315 c->details.transport.sessions
316 = GNUNET_CONTAINER_multihashmap32_create (128);
317 if (NULL != transport_client)
319 GNUNET_SERVICE_client_drop (transport_client->client);
320 transport_client = NULL;
322 transport_client = c;
323 GNUNET_SERVICE_client_continue (c->client);
328 * Check 'session_add' message is well-formed and comes from a
331 * @param cls client that sent the request
332 * @param message the request message
333 * @return #GNUNET_OK if @a message is well-formed
336 check_session_add (void *cls,
337 const struct SessionAddMessage *message)
339 struct Client *c = cls;
341 GNUNET_MQ_check_zero_termination (message);
342 if (CT_TRANSPORT != c->type)
345 return GNUNET_SYSERR;
352 * Handle 'session add' messages from transport clients.
354 * @param cls client that sent the request
355 * @param message the request message
358 handle_session_add (void *cls,
359 const struct SessionAddMessage *message)
361 struct Client *c = cls;
362 struct GNUNET_ATS_Session *session;
363 int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
364 ntohs (message->header.type));
366 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
367 message->session_id);
371 GNUNET_SERVICE_client_drop (c->client);
374 session = GNUNET_new (struct GNUNET_ATS_Session);
375 session->data.session = session;
377 session->session_id = message->session_id;
378 session->data.peer = message->peer;
379 prop_ntoh (&message->properties,
380 &session->data.prop);
381 session->data.inbound_only = inbound_only;
382 GNUNET_assert (GNUNET_YES ==
383 GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
386 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
387 session->sh = plugin->session_add (plugin->cls,
389 GNUNET_SERVICE_client_continue (c->client);
395 * Handle 'session update' messages from transport clients.
397 * @param cls client that sent the request
398 * @param msg the request message
401 handle_session_update (void *cls,
402 const struct SessionUpdateMessage *msg)
404 struct Client *c = cls;
405 struct GNUNET_ATS_Session *session;
407 if (CT_TRANSPORT != c->type)
410 GNUNET_SERVICE_client_drop (c->client);
413 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
418 GNUNET_SERVICE_client_drop (c->client);
421 prop_ntoh (&msg->properties,
422 &session->data.prop);
423 plugin->session_update (plugin->cls,
426 GNUNET_SERVICE_client_continue (c->client);
431 * Handle 'session delete' messages from transport clients.
433 * @param cls client that sent the request
434 * @param message the request message
437 handle_session_del (void *cls,
438 const struct SessionDelMessage *message)
440 struct Client *c = cls;
441 struct GNUNET_ATS_Session *session;
443 if (CT_TRANSPORT != c->type)
446 GNUNET_SERVICE_client_drop (c->client);
449 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
450 message->session_id);
454 GNUNET_SERVICE_client_drop (c->client);
457 plugin->session_del (plugin->cls,
460 GNUNET_assert (GNUNET_YES ==
461 GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
464 GNUNET_free (session);
465 GNUNET_SERVICE_client_continue (c->client);
470 * A client connected to us. Setup the local client
474 * @param client handle of the client
475 * @param mq message queue to talk to @a client
479 client_connect_cb (void *cls,
480 struct GNUNET_SERVICE_Client *client,
481 struct GNUNET_MQ_Handle *mq)
483 struct Client *c = GNUNET_new (struct Client);
492 * Function called on each session to release associated state
493 * on transport disconnect.
495 * @param cls the `struct Client`
496 * @param key unused (session_id)
497 * @param value a `struct GNUNET_ATS_Session`
500 free_session (void *cls,
504 struct Client *c = cls;
505 struct GNUNET_ATS_Session *session = value;
508 GNUNET_assert (c == session->client);
509 plugin->session_del (plugin->cls,
512 GNUNET_free (session);
518 * A client disconnected from us. Tear down the local client
522 * @param client handle of the client
523 * @param app_ctx our `struct Client`
526 client_disconnect_cb (void *cls,
527 struct GNUNET_SERVICE_Client *client,
530 struct Client *c = app_ctx;
533 GNUNET_assert (c->client == client);
539 for (struct ClientPreference *cp = c->details.application.cp_head;
541 cp = c->details.application.cp_head)
543 plugin->preference_del (plugin->cls,
546 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
547 c->details.application.cp_tail,
553 if (transport_client == c)
554 transport_client = NULL;
555 GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
558 GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
566 * Task run during shutdown.
571 cleanup_task (void *cls)
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "ATS shutdown initiated\n");
575 if (NULL != GSA_stats)
577 GNUNET_STATISTICS_destroy (GSA_stats,
585 * Process template requests.
588 * @param cfg configuration to use
589 * @param service the initialized service
593 const struct GNUNET_CONFIGURATION_Handle *cfg,
594 struct GNUNET_SERVICE_Handle *service)
596 GSA_stats = GNUNET_STATISTICS_create ("ats",
598 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
602 GAS_plugin_init (cfg))
605 GNUNET_SCHEDULER_shutdown ();
613 * Define "main" method using service macro.
617 GNUNET_SERVICE_OPTION_NONE,
620 &client_disconnect_cb,
622 GNUNET_MQ_hd_fixed_size (suggest,
623 GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
624 struct ExpressPreferenceMessage,
626 GNUNET_MQ_hd_fixed_size (suggest_cancel,
627 GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
628 struct ExpressPreferenceMessage,
630 GNUNET_MQ_hd_fixed_size (start,
631 GNUNET_MESSAGE_TYPE_ATS_START,
632 struct GNUNET_MessageHeader,
634 GNUNET_MQ_hd_var_size (session_add,
635 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
636 struct SessionAddMessage,
638 GNUNET_MQ_hd_var_size (session_add,
639 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
640 struct SessionAddMessage,
642 GNUNET_MQ_hd_fixed_size (session_update,
643 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
644 struct SessionUpdateMessage,
646 GNUNET_MQ_hd_fixed_size (session_del,
647 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
648 struct SessionDelMessage,
650 GNUNET_MQ_handler_end ());
653 /* end of gnunet-service-ats.c */