src: for every AGPL3.0 file, add SPDX identifier.
[oweals/gnunet.git] / src / ats / gnunet-service-ats-new.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2011, 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/gnunet-service-ats-new.c
22  * @brief ats service
23  * @author Matthias Wachs
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_statistics_service.h"
29 #include "gnunet_ats_plugin_new.h"
30 #include "ats2.h"
31
32
33 /**
34  * What type of client is this client?
35  */
36 enum ClientType {
37   /**
38    * We don't know yet.
39    */
40   CT_NONE = 0,
41
42   /**
43    * Transport service.
44    */
45   CT_TRANSPORT,
46
47   /**
48    * Application.
49    */
50   CT_APPLICATION
51 };
52
53
54 /**
55  * Information we track per client.
56  */
57 struct Client;
58
59 /**
60  * Preferences expressed by a client are kept in a DLL per client.
61  */
62 struct ClientPreference
63 {
64   /**
65    * DLL pointer.
66    */
67   struct ClientPreference *next;
68
69   /**
70    * DLL pointer.
71    */
72   struct ClientPreference *prev;
73
74   /**
75    * Which client expressed the preference?
76    */
77   struct Client *client;
78
79   /**
80    * Plugin's representation of the preference.
81    */
82   struct GNUNET_ATS_PreferenceHandle *ph;
83
84   /**
85    * Details about the preference.
86    */
87   struct GNUNET_ATS_Preference pref;
88 };
89
90
91 /**
92  * Information about ongoing sessions of the transport client.
93  */
94 struct GNUNET_ATS_Session
95 {
96
97   /**
98    * Session data exposed to the plugin.
99    */
100   struct GNUNET_ATS_SessionData data;
101
102   /**
103    * The transport client that provided the session.
104    */
105   struct Client *client;
106
107   /**
108    * Session state in the plugin.
109    */
110   struct GNUNET_ATS_SessionHandle *sh;
111
112   /**
113    * Unique ID for the session when talking with the client.
114    */
115   uint32_t session_id;
116
117 };
118
119
120 /**
121  * Information we track per client.
122  */
123 struct Client
124 {
125   /**
126    * Type of the client, initially #CT_NONE.
127    */
128   enum ClientType type;
129
130   /**
131    * Service handle of the client.
132    */
133   struct GNUNET_SERVICE_Client *client;
134
135   /**
136    * Message queue to talk to the client.
137    */
138   struct GNUNET_MQ_Handle *mq;
139
140   /**
141    * Details depending on @e type.
142    */
143   union {
144
145     struct {
146
147       /**
148        * Head of DLL of preferences expressed by this client.
149        */
150       struct ClientPreference *cp_head;
151
152       /**
153        * Tail of DLL of preferences expressed by this client.
154        */
155       struct ClientPreference *cp_tail;
156
157     } application;
158
159     struct {
160
161       /**
162        * Map from session IDs to `struct GNUNET_ATS_Session` objects.
163        */
164       struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
165
166     } transport;
167
168   } details;
169
170 };
171
172
173 /**
174  * Handle for statistics.
175  */
176 static struct GNUNET_STATISTICS_Handle *stats;
177
178 /**
179  * Our solver.
180  */
181 static struct GNUNET_ATS_SolverFunctions *plugin;
182
183 /**
184  * Solver plugin name as string
185  */
186 static char *plugin_name;
187
188 /**
189  * The transport client (there can only be one at a time).
190  */
191 static struct Client *transport_client;
192
193
194 /**
195  * Function called by the solver to prompt the transport to
196  * try out a new address.
197  *
198  * @param cls closure, NULL
199  * @param pid peer this is about
200  * @param address address the transport should try
201  */
202 static void
203 suggest_cb (void *cls,
204             const struct GNUNET_PeerIdentity *pid,
205             const char *address)
206 {
207   struct GNUNET_MQ_Envelope *env;
208   size_t slen = strlen (address) + 1;
209   struct AddressSuggestionMessage *as;
210
211   if (NULL == transport_client)
212   {
213     // FIXME: stats!
214     return;
215   }
216   env = GNUNET_MQ_msg_extra (as,
217                              slen,
218                              GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
219   as->peer = *pid;
220   memcpy (&as[1],
221           address,
222           slen);
223   GNUNET_MQ_send (transport_client->mq,
224                   env);
225 }
226
227
228 /**
229  * Function called by the solver to tell the transpor to
230  * allocate bandwidth for the specified session.
231  *
232  * @param cls closure, NULL
233  * @param session session this is about
234  * @param peer peer this is about
235  * @param bw_in suggested bandwidth for receiving
236  * @param bw_out suggested bandwidth for transmission
237  */
238 static void
239 allocate_cb (void *cls,
240              struct GNUNET_ATS_Session *session,
241              const struct GNUNET_PeerIdentity *peer,
242              struct GNUNET_BANDWIDTH_Value32NBO bw_in,
243              struct GNUNET_BANDWIDTH_Value32NBO bw_out)
244 {
245   struct GNUNET_MQ_Envelope *env;
246   struct SessionAllocationMessage *sam;
247
248   (void) cls;
249   if ( (NULL == transport_client) ||
250        (session->client != transport_client) )
251   {
252     /* transport must have just died and solver is addressing the
253        losses of sessions (possibly of previous transport), ignore! */
254     return;
255   }
256   env = GNUNET_MQ_msg (sam,
257                        GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION);
258   sam->session_id = session->session_id;
259   sam->peer = *peer;
260   sam->bandwidth_in = bw_in;
261   sam->bandwidth_out = bw_out;
262   GNUNET_MQ_send (transport_client->mq,
263                   env);
264 }
265
266
267 /**
268  * Convert @a properties to @a prop
269  *
270  * @param properties in NBO
271  * @param prop[out] in HBO
272  */
273 static void
274 prop_ntoh (const struct PropertiesNBO *properties,
275            struct GNUNET_ATS_Properties *prop)
276 {
277   prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
278   prop->goodput_out = ntohl (properties->goodput_out);
279   prop->goodput_in = ntohl (properties->goodput_in);
280   prop->utilization_out = ntohl (properties->utilization_out);
281   prop->utilization_in = ntohl (properties->utilization_in);
282   prop->distance = ntohl (properties->distance);
283   prop->mtu = ntohl (properties->mtu);
284   prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
285   prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
286 }
287
288
289 /**
290  * We have received a `struct ExpressPreferenceMessage` from an application client.
291  *
292  * @param cls handle to the client
293  * @param msg the start message
294  */
295 static void
296 handle_suggest (void *cls,
297                 const struct ExpressPreferenceMessage *msg)
298 {
299   struct Client *c = cls;
300   struct ClientPreference *cp;
301
302   if (CT_NONE == c->type)
303     c->type = CT_APPLICATION;
304   if (CT_APPLICATION != c->type)
305   {
306     GNUNET_break (0);
307     GNUNET_SERVICE_client_drop (c->client);
308     return;
309   }
310   cp = GNUNET_new (struct ClientPreference);
311   cp->client = c;
312   cp->pref.peer = msg->peer;
313   cp->pref.bw = msg->bw;
314   cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
315   cp->ph = plugin->preference_add (plugin->cls,
316                                    &cp->pref);
317   GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
318                                c->details.application.cp_tail,
319                                cp);
320   GNUNET_SERVICE_client_continue (c->client);
321 }
322
323
324 /**
325  * We have received a `struct ExpressPreferenceMessage` from an application client.
326  *
327  * @param cls handle to the client
328  * @param msg the start message
329  */
330 static void
331 handle_suggest_cancel (void *cls,
332                        const struct ExpressPreferenceMessage *msg)
333 {
334   struct Client *c = cls;
335   struct ClientPreference *cp;
336
337   if (CT_NONE == c->type)
338     c->type = CT_APPLICATION;
339   if (CT_APPLICATION != c->type)
340   {
341     GNUNET_break (0);
342     GNUNET_SERVICE_client_drop (c->client);
343     return;
344   }
345   for (cp = c->details.application.cp_head;
346        NULL != cp;
347        cp = cp->next)
348     if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
349          (cp->pref.bw.value__ == msg->bw.value__) &&
350          (0 == memcmp (&cp->pref.peer,
351                        &msg->peer,
352                        sizeof (struct GNUNET_PeerIdentity))) )
353       break;
354   if (NULL == cp)
355   {
356     GNUNET_break (0);
357     GNUNET_SERVICE_client_drop (c->client);
358     return;
359   }
360   plugin->preference_del (plugin->cls,
361                           cp->ph,
362                           &cp->pref);
363   GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
364                                c->details.application.cp_tail,
365                                cp);
366   GNUNET_free (cp);
367   GNUNET_SERVICE_client_continue (c->client);
368 }
369
370
371 /**
372  * Handle 'start' messages from transport clients.
373  *
374  * @param cls client that sent the request
375  * @param message the request message
376  */
377 static void
378 handle_start (void *cls,
379               const struct GNUNET_MessageHeader *hdr)
380 {
381   struct Client *c = cls;
382
383   if (CT_NONE != c->type)
384   {
385     GNUNET_break (0);
386     GNUNET_SERVICE_client_drop (c->client);
387     return;
388   }
389   c->type = CT_TRANSPORT;
390   c->details.transport.sessions
391     = GNUNET_CONTAINER_multihashmap32_create (128);
392   if (NULL != transport_client)
393   {
394     GNUNET_SERVICE_client_drop (transport_client->client);
395     transport_client = NULL;
396   }
397   transport_client = c;
398   GNUNET_SERVICE_client_continue (c->client);
399 }
400
401
402 /**
403  * Check 'session_add' message is well-formed and comes from a
404  * transport client.
405  *
406  * @param cls client that sent the request
407  * @param message the request message
408  * @return #GNUNET_OK if @a message is well-formed
409  */
410 static int
411 check_session_add (void *cls,
412                    const struct SessionAddMessage *message)
413 {
414   struct Client *c = cls;
415
416   GNUNET_MQ_check_zero_termination (message);
417   if (CT_TRANSPORT != c->type)
418   {
419     GNUNET_break (0);
420     return GNUNET_SYSERR;
421   }
422   return GNUNET_OK;
423 }
424
425
426 /**
427  * Handle 'session add' messages from transport clients.
428  *
429  * @param cls client that sent the request
430  * @param message the request message
431  */
432 static void
433 handle_session_add (void *cls,
434                     const struct SessionAddMessage *message)
435 {
436   struct Client *c = cls;
437   const char *address = (const char *) &message[1];
438   struct GNUNET_ATS_Session *session;
439   int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
440                       ntohs (message->header.type));
441
442   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
443                                                  message->session_id);
444   if (NULL != session)
445   {
446     GNUNET_break (0);
447     GNUNET_SERVICE_client_drop (c->client);
448     return;
449   }
450   session = GNUNET_new (struct GNUNET_ATS_Session);
451   session->data.session = session;
452   session->client = c;
453   session->session_id = message->session_id;
454   session->data.peer = message->peer;
455   prop_ntoh (&message->properties,
456              &session->data.prop);
457   session->data.inbound_only = inbound_only;
458   GNUNET_assert (GNUNET_YES ==
459                  GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
460                                                       message->session_id,
461                                                       session,
462                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
463   session->sh = plugin->session_add (plugin->cls,
464                                      &session->data,
465                                      address);
466   GNUNET_SERVICE_client_continue (c->client);
467 }
468
469
470 /**
471  * Handle 'session update' messages from transport clients.
472  *
473  * @param cls client that sent the request
474  * @param msg the request message
475  */
476 static void
477 handle_session_update (void *cls,
478                        const struct SessionUpdateMessage *msg)
479 {
480   struct Client *c = cls;
481   struct GNUNET_ATS_Session *session;
482
483   if (CT_TRANSPORT != c->type)
484   {
485     GNUNET_break (0);
486     GNUNET_SERVICE_client_drop (c->client);
487     return;
488   }
489   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
490                                                  msg->session_id);
491   if (NULL == session)
492   {
493     GNUNET_break (0);
494     GNUNET_SERVICE_client_drop (c->client);
495     return;
496   }
497   prop_ntoh (&msg->properties,
498              &session->data.prop);
499   plugin->session_update (plugin->cls,
500                           session->sh,
501                           &session->data);
502   GNUNET_SERVICE_client_continue (c->client);
503 }
504
505
506 /**
507  * Handle 'session delete' messages from transport clients.
508  *
509  * @param cls client that sent the request
510  * @param message the request message
511  */
512 static void
513 handle_session_del (void *cls,
514                     const struct SessionDelMessage *message)
515 {
516   struct Client *c = cls;
517   struct GNUNET_ATS_Session *session;
518
519   if (CT_TRANSPORT != c->type)
520   {
521     GNUNET_break (0);
522     GNUNET_SERVICE_client_drop (c->client);
523     return;
524   }
525   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
526                                                  message->session_id);
527   if (NULL == session)
528   {
529     GNUNET_break (0);
530     GNUNET_SERVICE_client_drop (c->client);
531     return;
532   }
533   plugin->session_del (plugin->cls,
534                        session->sh,
535                        &session->data);
536   GNUNET_assert (GNUNET_YES ==
537                  GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
538                                                          session->session_id,
539                                                          session));
540   GNUNET_free (session);
541   GNUNET_SERVICE_client_continue (c->client);
542 }
543
544
545 /**
546  * A client connected to us. Setup the local client
547  * record.
548  *
549  * @param cls unused
550  * @param client handle of the client
551  * @param mq message queue to talk to @a client
552  * @return @a client
553  */
554 static void *
555 client_connect_cb (void *cls,
556                    struct GNUNET_SERVICE_Client *client,
557                    struct GNUNET_MQ_Handle *mq)
558 {
559   struct Client *c = GNUNET_new (struct Client);
560
561   c->client = client;
562   c->mq = mq;
563   return c;
564 }
565
566
567 /**
568  * Function called on each session to release associated state
569  * on transport disconnect.
570  *
571  * @param cls the `struct Client`
572  * @param key unused (session_id)
573  * @param value a `struct GNUNET_ATS_Session`
574  */
575 static int
576 free_session (void *cls,
577               uint32_t key,
578               void *value)
579 {
580   struct Client *c = cls;
581   struct GNUNET_ATS_Session *session = value;
582
583   (void) key;
584   GNUNET_assert (c == session->client);
585   plugin->session_del (plugin->cls,
586                        session->sh,
587                        &session->data);
588   GNUNET_free (session);
589   return GNUNET_OK;
590 }
591
592
593 /**
594  * A client disconnected from us.  Tear down the local client
595  * record.
596  *
597  * @param cls unused
598  * @param client handle of the client
599  * @param app_ctx our `struct Client`
600  */
601 static void
602 client_disconnect_cb (void *cls,
603                       struct GNUNET_SERVICE_Client *client,
604                       void *app_ctx)
605 {
606   struct Client *c = app_ctx;
607
608   (void) cls;
609   GNUNET_assert (c->client == client);
610   switch (c->type)
611   {
612   case CT_NONE:
613     break;
614   case CT_APPLICATION:
615     for (struct ClientPreference *cp = c->details.application.cp_head;
616          NULL != cp;
617          cp = c->details.application.cp_head)
618     {
619       plugin->preference_del (plugin->cls,
620                               cp->ph,
621                               &cp->pref);
622       GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
623                                    c->details.application.cp_tail,
624                                    cp);
625       GNUNET_free (cp);
626     }
627     break;
628   case CT_TRANSPORT:
629     if (transport_client == c)
630       transport_client = NULL;
631     GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
632                                              &free_session,
633                                              c);
634     GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
635     break;
636   }
637   GNUNET_free (c);
638 }
639
640
641 /**
642  * Task run at the end during shutdown.
643  *
644  * @param cls unused
645  */
646 static void
647 final_cleanup (void *cls)
648 {
649   (void) cls;
650   if (NULL != stats)
651   {
652     GNUNET_STATISTICS_destroy (stats,
653                                GNUNET_NO);
654     stats = NULL;
655   }
656   if (NULL != plugin)
657   {
658     GNUNET_PLUGIN_unload (plugin_name,
659                           plugin);
660     plugin = NULL;
661   }
662   if (NULL != plugin_name)
663   {
664     GNUNET_free (plugin_name);
665     plugin_name = NULL;
666   }
667 }
668
669
670 /**
671  * Task run during shutdown.
672  *
673  * @param cls unused
674  */
675 static void
676 cleanup_task (void *cls)
677 {
678   (void) cls;
679   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
680               "ATS shutdown initiated\n");
681   GNUNET_SCHEDULER_add_now (&final_cleanup,
682                             NULL);
683 }
684
685
686 /**
687  * Process template requests.
688  *
689  * @param cls closure
690  * @param cfg configuration to use
691  * @param service the initialized service
692  */
693 static void
694 run (void *cls,
695      const struct GNUNET_CONFIGURATION_Handle *cfg,
696      struct GNUNET_SERVICE_Handle *service)
697 {
698   static struct GNUNET_ATS_PluginEnvironment env;
699   char *solver;
700
701   stats = GNUNET_STATISTICS_create ("ats",
702                                     cfg);
703   if (GNUNET_SYSERR ==
704       GNUNET_CONFIGURATION_get_value_string (cfg,
705                                              "ats",
706                                              "SOLVER",
707                                              &solver))
708   {
709     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
710                 "No ATS solver configured, using 'simple' approach\n");
711     solver = GNUNET_strdup ("simple");
712   }
713   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
714                                  NULL);
715   env.cls = NULL;
716   env.cfg = cfg;
717   env.stats = stats;
718   env.suggest_cb = &suggest_cb;
719   env.allocate_cb = &allocate_cb;
720   GNUNET_asprintf (&plugin_name,
721                    "libgnunet_plugin_ats2_%s",
722                    solver);
723   GNUNET_free (solver);
724   if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
725                                             &env)))
726   {
727     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
728                 _("Failed to initialize solver `%s'!\n"),
729                 plugin_name);
730     GNUNET_SCHEDULER_shutdown ();
731     return;
732   }
733 }
734
735
736 /**
737  * Define "main" method using service macro.
738  */
739 GNUNET_SERVICE_MAIN
740 ("ats",
741  GNUNET_SERVICE_OPTION_NONE,
742  &run,
743  &client_connect_cb,
744  &client_disconnect_cb,
745  NULL,
746  GNUNET_MQ_hd_fixed_size (suggest,
747                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
748                           struct ExpressPreferenceMessage,
749                           NULL),
750  GNUNET_MQ_hd_fixed_size (suggest_cancel,
751                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
752                           struct ExpressPreferenceMessage,
753                           NULL),
754  GNUNET_MQ_hd_fixed_size (start,
755                           GNUNET_MESSAGE_TYPE_ATS_START,
756                           struct GNUNET_MessageHeader,
757                           NULL),
758  GNUNET_MQ_hd_var_size (session_add,
759                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
760                         struct SessionAddMessage,
761                         NULL),
762  GNUNET_MQ_hd_var_size (session_add,
763                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
764                         struct SessionAddMessage,
765                         NULL),
766  GNUNET_MQ_hd_fixed_size (session_update,
767                           GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
768                           struct SessionUpdateMessage,
769                           NULL),
770  GNUNET_MQ_hd_fixed_size (session_del,
771                           GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
772                           struct SessionDelMessage,
773                           NULL),
774  GNUNET_MQ_handler_end ());
775
776
777 /* end of gnunet-service-ats.c */