fixing misc ats simple issues
[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_assert (NULL != session->sh);
467   GNUNET_SERVICE_client_continue (c->client);
468 }
469
470
471 /**
472  * Handle 'session update' messages from transport clients.
473  *
474  * @param cls client that sent the request
475  * @param msg the request message
476  */
477 static void
478 handle_session_update (void *cls,
479                        const struct SessionUpdateMessage *msg)
480 {
481   struct Client *c = cls;
482   struct GNUNET_ATS_Session *session;
483
484   if (CT_TRANSPORT != c->type)
485   {
486     GNUNET_break (0);
487     GNUNET_SERVICE_client_drop (c->client);
488     return;
489   }
490   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
491                                                  msg->session_id);
492   if (NULL == session)
493   {
494     GNUNET_break (0);
495     GNUNET_SERVICE_client_drop (c->client);
496     return;
497   }
498   prop_ntoh (&msg->properties,
499              &session->data.prop);
500   plugin->session_update (plugin->cls,
501                           session->sh,
502                           &session->data);
503   GNUNET_SERVICE_client_continue (c->client);
504 }
505
506
507 /**
508  * Handle 'session delete' messages from transport clients.
509  *
510  * @param cls client that sent the request
511  * @param message the request message
512  */
513 static void
514 handle_session_del (void *cls,
515                     const struct SessionDelMessage *message)
516 {
517   struct Client *c = cls;
518   struct GNUNET_ATS_Session *session;
519
520   if (CT_TRANSPORT != c->type)
521   {
522     GNUNET_break (0);
523     GNUNET_SERVICE_client_drop (c->client);
524     return;
525   }
526   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
527                                                  message->session_id);
528   if (NULL == session)
529   {
530     GNUNET_break (0);
531     GNUNET_SERVICE_client_drop (c->client);
532     return;
533   }
534   GNUNET_assert (NULL != session->sh);
535   plugin->session_del (plugin->cls,
536                        session->sh,
537                        &session->data);
538   session->sh = NULL;
539   GNUNET_assert (GNUNET_YES ==
540                  GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
541                                                          session->session_id,
542                                                          session));
543   GNUNET_free (session);
544   GNUNET_SERVICE_client_continue (c->client);
545 }
546
547
548 /**
549  * A client connected to us. Setup the local client
550  * record.
551  *
552  * @param cls unused
553  * @param client handle of the client
554  * @param mq message queue to talk to @a client
555  * @return @a client
556  */
557 static void *
558 client_connect_cb (void *cls,
559                    struct GNUNET_SERVICE_Client *client,
560                    struct GNUNET_MQ_Handle *mq)
561 {
562   struct Client *c = GNUNET_new (struct Client);
563
564   c->client = client;
565   c->mq = mq;
566   return c;
567 }
568
569
570 /**
571  * Function called on each session to release associated state
572  * on transport disconnect.
573  *
574  * @param cls the `struct Client`
575  * @param key unused (session_id)
576  * @param value a `struct GNUNET_ATS_Session`
577  */
578 static int
579 free_session (void *cls,
580               uint32_t key,
581               void *value)
582 {
583   struct Client *c = cls;
584   struct GNUNET_ATS_Session *session = value;
585
586   (void) key;
587   GNUNET_assert (c == session->client);
588   GNUNET_assert (NULL != session->sh);
589   plugin->session_del (plugin->cls,
590                        session->sh,
591                        &session->data);
592   session->sh = NULL;
593   GNUNET_free (session);
594   return GNUNET_OK;
595 }
596
597
598 /**
599  * A client disconnected from us.  Tear down the local client
600  * record.
601  *
602  * @param cls unused
603  * @param client handle of the client
604  * @param app_ctx our `struct Client`
605  */
606 static void
607 client_disconnect_cb (void *cls,
608                       struct GNUNET_SERVICE_Client *client,
609                       void *app_ctx)
610 {
611   struct Client *c = app_ctx;
612
613   (void) cls;
614   GNUNET_assert (c->client == client);
615   switch (c->type)
616   {
617   case CT_NONE:
618     break;
619   case CT_APPLICATION:
620     for (struct ClientPreference *cp = c->details.application.cp_head;
621          NULL != cp;
622          cp = c->details.application.cp_head)
623     {
624       plugin->preference_del (plugin->cls,
625                               cp->ph,
626                               &cp->pref);
627       GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
628                                    c->details.application.cp_tail,
629                                    cp);
630       GNUNET_free (cp);
631     }
632     break;
633   case CT_TRANSPORT:
634     if (transport_client == c)
635       transport_client = NULL;
636     GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
637                                              &free_session,
638                                              c);
639     GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
640     break;
641   }
642   GNUNET_free (c);
643 }
644
645
646 /**
647  * Task run at the end during shutdown.
648  *
649  * @param cls unused
650  */
651 static void
652 final_cleanup (void *cls)
653 {
654   (void) cls;
655   if (NULL != stats)
656   {
657     GNUNET_STATISTICS_destroy (stats,
658                                GNUNET_NO);
659     stats = NULL;
660   }
661   if (NULL != plugin)
662   {
663     GNUNET_PLUGIN_unload (plugin_name,
664                           plugin);
665     plugin = NULL;
666   }
667   if (NULL != plugin_name)
668   {
669     GNUNET_free (plugin_name);
670     plugin_name = NULL;
671   }
672 }
673
674
675 /**
676  * Task run during shutdown.
677  *
678  * @param cls unused
679  */
680 static void
681 cleanup_task (void *cls)
682 {
683   (void) cls;
684   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
685               "ATS shutdown initiated\n");
686   GNUNET_SCHEDULER_add_now (&final_cleanup,
687                             NULL);
688 }
689
690
691 /**
692  * Process template requests.
693  *
694  * @param cls closure
695  * @param cfg configuration to use
696  * @param service the initialized service
697  */
698 static void
699 run (void *cls,
700      const struct GNUNET_CONFIGURATION_Handle *cfg,
701      struct GNUNET_SERVICE_Handle *service)
702 {
703   static struct GNUNET_ATS_PluginEnvironment env;
704   char *solver;
705
706   stats = GNUNET_STATISTICS_create ("ats",
707                                     cfg);
708   if (GNUNET_SYSERR ==
709       GNUNET_CONFIGURATION_get_value_string (cfg,
710                                              "ats",
711                                              "SOLVER",
712                                              &solver))
713   {
714     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
715                 "No ATS solver configured, using 'simple' approach\n");
716     solver = GNUNET_strdup ("simple");
717   }
718   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
719                                  NULL);
720   env.cls = NULL;
721   env.cfg = cfg;
722   env.stats = stats;
723   env.suggest_cb = &suggest_cb;
724   env.allocate_cb = &allocate_cb;
725   GNUNET_asprintf (&plugin_name,
726                    "libgnunet_plugin_ats2_%s",
727                    solver);
728   GNUNET_free (solver);
729   if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
730                                             &env)))
731   {
732     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
733                 _("Failed to initialize solver `%s'!\n"),
734                 plugin_name);
735     GNUNET_SCHEDULER_shutdown ();
736     return;
737   }
738 }
739
740
741 /**
742  * Define "main" method using service macro.
743  */
744 GNUNET_SERVICE_MAIN
745 ("ats",
746  GNUNET_SERVICE_OPTION_NONE,
747  &run,
748  &client_connect_cb,
749  &client_disconnect_cb,
750  NULL,
751  GNUNET_MQ_hd_fixed_size (suggest,
752                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
753                           struct ExpressPreferenceMessage,
754                           NULL),
755  GNUNET_MQ_hd_fixed_size (suggest_cancel,
756                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
757                           struct ExpressPreferenceMessage,
758                           NULL),
759  GNUNET_MQ_hd_fixed_size (start,
760                           GNUNET_MESSAGE_TYPE_ATS_START,
761                           struct GNUNET_MessageHeader,
762                           NULL),
763  GNUNET_MQ_hd_var_size (session_add,
764                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
765                         struct SessionAddMessage,
766                         NULL),
767  GNUNET_MQ_hd_var_size (session_add,
768                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
769                         struct SessionAddMessage,
770                         NULL),
771  GNUNET_MQ_hd_fixed_size (session_update,
772                           GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
773                           struct SessionUpdateMessage,
774                           NULL),
775  GNUNET_MQ_hd_fixed_size (session_del,
776                           GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
777                           struct SessionDelMessage,
778                           NULL),
779  GNUNET_MQ_handler_end ());
780
781
782 /* end of gnunet-service-ats.c */