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 #include "gnunet_util_lib.h"
26 #include "gnunet_statistics_service.h"
27 #include "gnunet_ats_plugin_new.h"
32 * What type of client is this client?
53 * Information we track per client.
58 * Preferences expressed by a client are kept in a DLL per client.
60 struct ClientPreference
65 struct ClientPreference *next;
70 struct ClientPreference *prev;
73 * Which client expressed the preference?
75 struct Client *client;
78 * Plugin's representation of the preference.
80 struct GNUNET_ATS_PreferenceHandle *ph;
83 * Details about the preference.
85 struct GNUNET_ATS_Preference pref;
90 * Information about ongoing sessions of the transport client.
92 struct GNUNET_ATS_Session
96 * Session data exposed to the plugin.
98 struct GNUNET_ATS_SessionData data;
101 * The transport client that provided the session.
103 struct Client *client;
106 * Session state in the plugin.
108 struct GNUNET_ATS_SessionHandle *sh;
111 * Unique ID for the session when talking with the client.
119 * Information we track per client.
124 * Type of the client, initially #CT_NONE.
126 enum ClientType type;
129 * Service handle of the client.
131 struct GNUNET_SERVICE_Client *client;
134 * Message queue to talk to the client.
136 struct GNUNET_MQ_Handle *mq;
139 * Details depending on @e type.
146 * Head of DLL of preferences expressed by this client.
148 struct ClientPreference *cp_head;
151 * Tail of DLL of preferences expressed by this client.
153 struct ClientPreference *cp_tail;
160 * Map from session IDs to `struct GNUNET_ATS_Session` objects.
162 struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
172 * Handle for statistics.
174 static struct GNUNET_STATISTICS_Handle *stats;
179 static struct GNUNET_ATS_SolverFunctions *plugin;
182 * Solver plugin name as string
184 static char *plugin_name;
187 * The transport client (there can only be one at a time).
189 static struct Client *transport_client;
193 * Function called by the solver to prompt the transport to
194 * try out a new address.
196 * @param cls closure, NULL
197 * @param pid peer this is about
198 * @param address address the transport should try
201 suggest_cb (void *cls,
202 const struct GNUNET_PeerIdentity *pid,
205 struct GNUNET_MQ_Envelope *env;
206 size_t slen = strlen (address) + 1;
207 struct AddressSuggestionMessage *as;
209 if (NULL == transport_client)
214 env = GNUNET_MQ_msg_extra (as,
216 GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
221 GNUNET_MQ_send (transport_client->mq,
227 * Function called by the solver to tell the transpor to
228 * allocate bandwidth for the specified session.
230 * @param cls closure, NULL
231 * @param session session this is about
232 * @param peer peer this is about
233 * @param bw_in suggested bandwidth for receiving
234 * @param bw_out suggested bandwidth for transmission
237 allocate_cb (void *cls,
238 struct GNUNET_ATS_Session *session,
239 const struct GNUNET_PeerIdentity *peer,
240 struct GNUNET_BANDWIDTH_Value32NBO bw_in,
241 struct GNUNET_BANDWIDTH_Value32NBO bw_out)
243 struct GNUNET_MQ_Envelope *env;
244 struct SessionAllocationMessage *sam;
247 if ( (NULL == transport_client) ||
248 (session->client != transport_client) )
250 /* transport must have just died and solver is addressing the
251 losses of sessions (possibly of previous transport), ignore! */
254 env = GNUNET_MQ_msg (sam,
255 GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION);
256 sam->session_id = session->session_id;
258 sam->bandwidth_in = bw_in;
259 sam->bandwidth_out = bw_out;
260 GNUNET_MQ_send (transport_client->mq,
266 * Convert @a properties to @a prop
268 * @param properties in NBO
269 * @param prop[out] in HBO
272 prop_ntoh (const struct PropertiesNBO *properties,
273 struct GNUNET_ATS_Properties *prop)
275 prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
276 prop->goodput_out = ntohl (properties->goodput_out);
277 prop->goodput_in = ntohl (properties->goodput_in);
278 prop->utilization_out = ntohl (properties->utilization_out);
279 prop->utilization_in = ntohl (properties->utilization_in);
280 prop->distance = ntohl (properties->distance);
281 prop->mtu = ntohl (properties->mtu);
282 prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
283 prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
288 * We have received a `struct ExpressPreferenceMessage` from an application client.
290 * @param cls handle to the client
291 * @param msg the start message
294 handle_suggest (void *cls,
295 const struct ExpressPreferenceMessage *msg)
297 struct Client *c = cls;
298 struct ClientPreference *cp;
300 if (CT_NONE == c->type)
301 c->type = CT_APPLICATION;
302 if (CT_APPLICATION != c->type)
305 GNUNET_SERVICE_client_drop (c->client);
308 cp = GNUNET_new (struct ClientPreference);
310 cp->pref.peer = msg->peer;
311 cp->pref.bw = msg->bw;
312 cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
313 cp->ph = plugin->preference_add (plugin->cls,
315 GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
316 c->details.application.cp_tail,
318 GNUNET_SERVICE_client_continue (c->client);
323 * We have received a `struct ExpressPreferenceMessage` from an application client.
325 * @param cls handle to the client
326 * @param msg the start message
329 handle_suggest_cancel (void *cls,
330 const struct ExpressPreferenceMessage *msg)
332 struct Client *c = cls;
333 struct ClientPreference *cp;
335 if (CT_NONE == c->type)
336 c->type = CT_APPLICATION;
337 if (CT_APPLICATION != c->type)
340 GNUNET_SERVICE_client_drop (c->client);
343 for (cp = c->details.application.cp_head;
346 if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
347 (cp->pref.bw.value__ == msg->bw.value__) &&
348 (0 == memcmp (&cp->pref.peer,
350 sizeof (struct GNUNET_PeerIdentity))) )
355 GNUNET_SERVICE_client_drop (c->client);
358 plugin->preference_del (plugin->cls,
361 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
362 c->details.application.cp_tail,
365 GNUNET_SERVICE_client_continue (c->client);
370 * Handle 'start' messages from transport clients.
372 * @param cls client that sent the request
373 * @param message the request message
376 handle_start (void *cls,
377 const struct GNUNET_MessageHeader *hdr)
379 struct Client *c = cls;
381 if (CT_NONE != c->type)
384 GNUNET_SERVICE_client_drop (c->client);
387 c->type = CT_TRANSPORT;
388 c->details.transport.sessions
389 = GNUNET_CONTAINER_multihashmap32_create (128);
390 if (NULL != transport_client)
392 GNUNET_SERVICE_client_drop (transport_client->client);
393 transport_client = NULL;
395 transport_client = c;
396 GNUNET_SERVICE_client_continue (c->client);
401 * Check 'session_add' message is well-formed and comes from a
404 * @param cls client that sent the request
405 * @param message the request message
406 * @return #GNUNET_OK if @a message is well-formed
409 check_session_add (void *cls,
410 const struct SessionAddMessage *message)
412 struct Client *c = cls;
414 GNUNET_MQ_check_zero_termination (message);
415 if (CT_TRANSPORT != c->type)
418 return GNUNET_SYSERR;
425 * Handle 'session add' messages from transport clients.
427 * @param cls client that sent the request
428 * @param message the request message
431 handle_session_add (void *cls,
432 const struct SessionAddMessage *message)
434 struct Client *c = cls;
435 const char *address = (const char *) &message[1];
436 struct GNUNET_ATS_Session *session;
437 int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
438 ntohs (message->header.type));
440 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
441 message->session_id);
445 GNUNET_SERVICE_client_drop (c->client);
448 session = GNUNET_new (struct GNUNET_ATS_Session);
449 session->data.session = session;
451 session->session_id = message->session_id;
452 session->data.peer = message->peer;
453 prop_ntoh (&message->properties,
454 &session->data.prop);
455 session->data.inbound_only = inbound_only;
456 GNUNET_assert (GNUNET_YES ==
457 GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
460 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
461 session->sh = plugin->session_add (plugin->cls,
464 GNUNET_SERVICE_client_continue (c->client);
469 * Handle 'session update' messages from transport clients.
471 * @param cls client that sent the request
472 * @param msg the request message
475 handle_session_update (void *cls,
476 const struct SessionUpdateMessage *msg)
478 struct Client *c = cls;
479 struct GNUNET_ATS_Session *session;
481 if (CT_TRANSPORT != c->type)
484 GNUNET_SERVICE_client_drop (c->client);
487 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
492 GNUNET_SERVICE_client_drop (c->client);
495 prop_ntoh (&msg->properties,
496 &session->data.prop);
497 plugin->session_update (plugin->cls,
500 GNUNET_SERVICE_client_continue (c->client);
505 * Handle 'session delete' messages from transport clients.
507 * @param cls client that sent the request
508 * @param message the request message
511 handle_session_del (void *cls,
512 const struct SessionDelMessage *message)
514 struct Client *c = cls;
515 struct GNUNET_ATS_Session *session;
517 if (CT_TRANSPORT != c->type)
520 GNUNET_SERVICE_client_drop (c->client);
523 session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
524 message->session_id);
528 GNUNET_SERVICE_client_drop (c->client);
531 plugin->session_del (plugin->cls,
534 GNUNET_assert (GNUNET_YES ==
535 GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
538 GNUNET_free (session);
539 GNUNET_SERVICE_client_continue (c->client);
544 * A client connected to us. Setup the local client
548 * @param client handle of the client
549 * @param mq message queue to talk to @a client
553 client_connect_cb (void *cls,
554 struct GNUNET_SERVICE_Client *client,
555 struct GNUNET_MQ_Handle *mq)
557 struct Client *c = GNUNET_new (struct Client);
566 * Function called on each session to release associated state
567 * on transport disconnect.
569 * @param cls the `struct Client`
570 * @param key unused (session_id)
571 * @param value a `struct GNUNET_ATS_Session`
574 free_session (void *cls,
578 struct Client *c = cls;
579 struct GNUNET_ATS_Session *session = value;
582 GNUNET_assert (c == session->client);
583 plugin->session_del (plugin->cls,
586 GNUNET_free (session);
592 * A client disconnected from us. Tear down the local client
596 * @param client handle of the client
597 * @param app_ctx our `struct Client`
600 client_disconnect_cb (void *cls,
601 struct GNUNET_SERVICE_Client *client,
604 struct Client *c = app_ctx;
607 GNUNET_assert (c->client == client);
613 for (struct ClientPreference *cp = c->details.application.cp_head;
615 cp = c->details.application.cp_head)
617 plugin->preference_del (plugin->cls,
620 GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
621 c->details.application.cp_tail,
627 if (transport_client == c)
628 transport_client = NULL;
629 GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
632 GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
640 * Task run during shutdown.
645 cleanup_task (void *cls)
647 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
648 "ATS shutdown initiated\n");
651 GNUNET_STATISTICS_destroy (stats,
657 GNUNET_PLUGIN_unload (plugin_name,
661 if (NULL != plugin_name)
663 GNUNET_free (plugin_name);
670 * Process template requests.
673 * @param cfg configuration to use
674 * @param service the initialized service
678 const struct GNUNET_CONFIGURATION_Handle *cfg,
679 struct GNUNET_SERVICE_Handle *service)
681 static struct GNUNET_ATS_PluginEnvironment env;
684 stats = GNUNET_STATISTICS_create ("ats",
687 GNUNET_CONFIGURATION_get_value_string (cfg,
692 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
693 "No ATS solver configured, using 'simple' approach\n");
694 solver = GNUNET_strdup ("simple");
696 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
701 env.suggest_cb = &suggest_cb;
702 env.allocate_cb = &allocate_cb;
703 GNUNET_asprintf (&plugin_name,
704 "libgnunet_plugin_ats2_%s",
706 GNUNET_free (solver);
707 if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
710 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
711 _("Failed to initialize solver `%s'!\n"),
713 GNUNET_SCHEDULER_shutdown ();
720 * Define "main" method using service macro.
724 GNUNET_SERVICE_OPTION_NONE,
727 &client_disconnect_cb,
729 GNUNET_MQ_hd_fixed_size (suggest,
730 GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
731 struct ExpressPreferenceMessage,
733 GNUNET_MQ_hd_fixed_size (suggest_cancel,
734 GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
735 struct ExpressPreferenceMessage,
737 GNUNET_MQ_hd_fixed_size (start,
738 GNUNET_MESSAGE_TYPE_ATS_START,
739 struct GNUNET_MessageHeader,
741 GNUNET_MQ_hd_var_size (session_add,
742 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
743 struct SessionAddMessage,
745 GNUNET_MQ_hd_var_size (session_add,
746 GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
747 struct SessionAddMessage,
749 GNUNET_MQ_hd_fixed_size (session_update,
750 GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
751 struct SessionUpdateMessage,
753 GNUNET_MQ_hd_fixed_size (session_del,
754 GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
755 struct SessionDelMessage,
757 GNUNET_MQ_handler_end ());
760 /* end of gnunet-service-ats.c */