REST: nothing triggers rest
[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   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
217               "Suggesting address `%s' of peer `%s'\n",
218               address,
219               GNUNET_i2s (pid));
220   env = GNUNET_MQ_msg_extra (as,
221                              slen,
222                              GNUNET_MESSAGE_TYPE_ATS_ADDRESS_SUGGESTION);
223   as->peer = *pid;
224   memcpy (&as[1],
225           address,
226           slen);
227   GNUNET_MQ_send (transport_client->mq,
228                   env);
229 }
230
231
232 /**
233  * Function called by the solver to tell the transpor to
234  * allocate bandwidth for the specified session.
235  *
236  * @param cls closure, NULL
237  * @param session session this is about
238  * @param peer peer this is about
239  * @param bw_in suggested bandwidth for receiving
240  * @param bw_out suggested bandwidth for transmission
241  */
242 static void
243 allocate_cb (void *cls,
244              struct GNUNET_ATS_Session *session,
245              const struct GNUNET_PeerIdentity *peer,
246              struct GNUNET_BANDWIDTH_Value32NBO bw_in,
247              struct GNUNET_BANDWIDTH_Value32NBO bw_out)
248 {
249   struct GNUNET_MQ_Envelope *env;
250   struct SessionAllocationMessage *sam;
251
252   (void) cls;
253   if ( (NULL == transport_client) ||
254        (session->client != transport_client) )
255   {
256     /* transport must have just died and solver is addressing the
257        losses of sessions (possibly of previous transport), ignore! */
258     return;
259   }
260   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
261               "Allocating %u/%u bytes for %p of peer `%s'\n",
262               ntohl (bw_in.value__),
263               ntohl (bw_out.value__),
264               session,
265               GNUNET_i2s (peer));
266   env = GNUNET_MQ_msg (sam,
267                        GNUNET_MESSAGE_TYPE_ATS_SESSION_ALLOCATION);
268   sam->session_id = session->session_id;
269   sam->peer = *peer;
270   sam->bandwidth_in = bw_in;
271   sam->bandwidth_out = bw_out;
272   GNUNET_MQ_send (transport_client->mq,
273                   env);
274 }
275
276
277 /**
278  * Convert @a properties to @a prop
279  *
280  * @param properties in NBO
281  * @param prop[out] in HBO
282  */
283 static void
284 prop_ntoh (const struct PropertiesNBO *properties,
285            struct GNUNET_ATS_Properties *prop)
286 {
287   prop->delay = GNUNET_TIME_relative_ntoh (properties->delay);
288   prop->goodput_out = ntohl (properties->goodput_out);
289   prop->goodput_in = ntohl (properties->goodput_in);
290   prop->utilization_out = ntohl (properties->utilization_out);
291   prop->utilization_in = ntohl (properties->utilization_in);
292   prop->distance = ntohl (properties->distance);
293   prop->mtu = ntohl (properties->mtu);
294   prop->nt = (enum GNUNET_NetworkType) ntohl (properties->nt);
295   prop->cc = (enum GNUNET_TRANSPORT_CommunicatorCharacteristics) ntohl (properties->cc);
296 }
297
298
299 /**
300  * We have received a `struct ExpressPreferenceMessage` from an application client.
301  *
302  * @param cls handle to the client
303  * @param msg the start message
304  */
305 static void
306 handle_suggest (void *cls,
307                 const struct ExpressPreferenceMessage *msg)
308 {
309   struct Client *c = cls;
310   struct ClientPreference *cp;
311
312   if (CT_NONE == c->type)
313     c->type = CT_APPLICATION;
314   if (CT_APPLICATION != c->type)
315   {
316     GNUNET_break (0);
317     GNUNET_SERVICE_client_drop (c->client);
318     return;
319   }
320   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
321               "Client suggested we talk to %s with preference %d at rate %u\n",
322               GNUNET_i2s (&msg->peer),
323               (int) ntohl (msg->pk),
324               (int) ntohl (msg->bw.value__));
325   cp = GNUNET_new (struct ClientPreference);
326   cp->client = c;
327   cp->pref.peer = msg->peer;
328   cp->pref.bw = msg->bw;
329   cp->pref.pk = (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk);
330   cp->ph = plugin->preference_add (plugin->cls,
331                                    &cp->pref);
332   GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
333                                c->details.application.cp_tail,
334                                cp);
335   GNUNET_SERVICE_client_continue (c->client);
336 }
337
338
339 /**
340  * We have received a `struct ExpressPreferenceMessage` from an application client.
341  *
342  * @param cls handle to the client
343  * @param msg the start message
344  */
345 static void
346 handle_suggest_cancel (void *cls,
347                        const struct ExpressPreferenceMessage *msg)
348 {
349   struct Client *c = cls;
350   struct ClientPreference *cp;
351
352   if (CT_NONE == c->type)
353     c->type = CT_APPLICATION;
354   if (CT_APPLICATION != c->type)
355   {
356     GNUNET_break (0);
357     GNUNET_SERVICE_client_drop (c->client);
358     return;
359   }
360   for (cp = c->details.application.cp_head;
361        NULL != cp;
362        cp = cp->next)
363     if ( (cp->pref.pk == (enum GNUNET_MQ_PreferenceKind) ntohl (msg->pk)) &&
364          (cp->pref.bw.value__ == msg->bw.value__) &&
365          (0 == GNUNET_memcmp (&cp->pref.peer,
366                        &msg->peer)) )
367       break;
368   if (NULL == cp)
369   {
370     GNUNET_break (0);
371     GNUNET_SERVICE_client_drop (c->client);
372     return;
373   }
374   plugin->preference_del (plugin->cls,
375                           cp->ph,
376                           &cp->pref);
377   GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
378                                c->details.application.cp_tail,
379                                cp);
380   GNUNET_free (cp);
381   GNUNET_SERVICE_client_continue (c->client);
382 }
383
384
385 /**
386  * Handle 'start' messages from transport clients.
387  *
388  * @param cls client that sent the request
389  * @param message the request message
390  */
391 static void
392 handle_start (void *cls,
393               const struct GNUNET_MessageHeader *hdr)
394 {
395   struct Client *c = cls;
396
397   if (CT_NONE != c->type)
398   {
399     GNUNET_break (0);
400     GNUNET_SERVICE_client_drop (c->client);
401     return;
402   }
403   c->type = CT_TRANSPORT;
404   c->details.transport.sessions
405     = GNUNET_CONTAINER_multihashmap32_create (128);
406   if (NULL != transport_client)
407   {
408     GNUNET_SERVICE_client_drop (transport_client->client);
409     transport_client = NULL;
410   }
411   transport_client = c;
412   GNUNET_SERVICE_client_continue (c->client);
413 }
414
415
416 /**
417  * Check 'session_add' message is well-formed and comes from a
418  * transport client.
419  *
420  * @param cls client that sent the request
421  * @param message the request message
422  * @return #GNUNET_OK if @a message is well-formed
423  */
424 static int
425 check_session_add (void *cls,
426                    const struct SessionAddMessage *message)
427 {
428   struct Client *c = cls;
429
430   GNUNET_MQ_check_zero_termination (message);
431   if (CT_TRANSPORT != c->type)
432   {
433     GNUNET_break (0);
434     return GNUNET_SYSERR;
435   }
436   return GNUNET_OK;
437 }
438
439
440 /**
441  * Handle 'session add' messages from transport clients.
442  *
443  * @param cls client that sent the request
444  * @param message the request message
445  */
446 static void
447 handle_session_add (void *cls,
448                     const struct SessionAddMessage *message)
449 {
450   struct Client *c = cls;
451   const char *address = (const char *) &message[1];
452   struct GNUNET_ATS_Session *session;
453   int inbound_only = (GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY ==
454                       ntohs (message->header.type));
455
456   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
457                                                  message->session_id);
458   if (NULL != session)
459   {
460     GNUNET_break (0);
461     GNUNET_SERVICE_client_drop (c->client);
462     return;
463   }
464   session = GNUNET_new (struct GNUNET_ATS_Session);
465   session->data.session = session;
466   session->client = c;
467   session->session_id = message->session_id;
468   session->data.peer = message->peer;
469   prop_ntoh (&message->properties,
470              &session->data.prop);
471   session->data.inbound_only = inbound_only;
472   GNUNET_assert (GNUNET_YES ==
473                  GNUNET_CONTAINER_multihashmap32_put (c->details.transport.sessions,
474                                                       message->session_id,
475                                                       session,
476                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
477   session->sh = plugin->session_add (plugin->cls,
478                                      &session->data,
479                                      address);
480   GNUNET_assert (NULL != session->sh);
481   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
482               "Transport has new session %p to %s\n",
483               session,
484               GNUNET_i2s (&message->peer));
485   GNUNET_SERVICE_client_continue (c->client);
486 }
487
488
489 /**
490  * Handle 'session update' messages from transport clients.
491  *
492  * @param cls client that sent the request
493  * @param msg the request message
494  */
495 static void
496 handle_session_update (void *cls,
497                        const struct SessionUpdateMessage *msg)
498 {
499   struct Client *c = cls;
500   struct GNUNET_ATS_Session *session;
501
502   if (CT_TRANSPORT != c->type)
503   {
504     GNUNET_break (0);
505     GNUNET_SERVICE_client_drop (c->client);
506     return;
507   }
508   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
509                                                  msg->session_id);
510   if (NULL == session)
511   {
512     GNUNET_break (0);
513     GNUNET_SERVICE_client_drop (c->client);
514     return;
515   }
516   prop_ntoh (&msg->properties,
517              &session->data.prop);
518   plugin->session_update (plugin->cls,
519                           session->sh,
520                           &session->data);
521   GNUNET_SERVICE_client_continue (c->client);
522 }
523
524
525 /**
526  * Handle 'session delete' messages from transport clients.
527  *
528  * @param cls client that sent the request
529  * @param message the request message
530  */
531 static void
532 handle_session_del (void *cls,
533                     const struct SessionDelMessage *message)
534 {
535   struct Client *c = cls;
536   struct GNUNET_ATS_Session *session;
537
538   if (CT_TRANSPORT != c->type)
539   {
540     GNUNET_break (0);
541     GNUNET_SERVICE_client_drop (c->client);
542     return;
543   }
544   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
545                                                  message->session_id);
546   if (NULL == session)
547   {
548     GNUNET_break (0);
549     GNUNET_SERVICE_client_drop (c->client);
550     return;
551   }
552   GNUNET_assert (NULL != session->sh);
553   plugin->session_del (plugin->cls,
554                        session->sh,
555                        &session->data);
556   session->sh = NULL;
557   GNUNET_assert (GNUNET_YES ==
558                  GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
559                                                          session->session_id,
560                                                          session));
561   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
562               "Transport lost session %p to %s\n",
563               session,
564               GNUNET_i2s (&session->data.peer));
565   GNUNET_free (session);
566   GNUNET_SERVICE_client_continue (c->client);
567 }
568
569
570 /**
571  * A client connected to us. Setup the local client
572  * record.
573  *
574  * @param cls unused
575  * @param client handle of the client
576  * @param mq message queue to talk to @a client
577  * @return @a client
578  */
579 static void *
580 client_connect_cb (void *cls,
581                    struct GNUNET_SERVICE_Client *client,
582                    struct GNUNET_MQ_Handle *mq)
583 {
584   struct Client *c = GNUNET_new (struct Client);
585
586   c->client = client;
587   c->mq = mq;
588   return c;
589 }
590
591
592 /**
593  * Function called on each session to release associated state
594  * on transport disconnect.
595  *
596  * @param cls the `struct Client`
597  * @param key unused (session_id)
598  * @param value a `struct GNUNET_ATS_Session`
599  */
600 static int
601 free_session (void *cls,
602               uint32_t key,
603               void *value)
604 {
605   struct Client *c = cls;
606   struct GNUNET_ATS_Session *session = value;
607
608   (void) key;
609   GNUNET_assert (c == session->client);
610   GNUNET_assert (NULL != session->sh);
611   plugin->session_del (plugin->cls,
612                        session->sh,
613                        &session->data);
614   session->sh = NULL;
615   GNUNET_free (session);
616   return GNUNET_OK;
617 }
618
619
620 /**
621  * A client disconnected from us.  Tear down the local client
622  * record.
623  *
624  * @param cls unused
625  * @param client handle of the client
626  * @param app_ctx our `struct Client`
627  */
628 static void
629 client_disconnect_cb (void *cls,
630                       struct GNUNET_SERVICE_Client *client,
631                       void *app_ctx)
632 {
633   struct Client *c = app_ctx;
634
635   (void) cls;
636   GNUNET_assert (c->client == client);
637   switch (c->type)
638   {
639   case CT_NONE:
640     break;
641   case CT_APPLICATION:
642     for (struct ClientPreference *cp = c->details.application.cp_head;
643          NULL != cp;
644          cp = c->details.application.cp_head)
645     {
646       plugin->preference_del (plugin->cls,
647                               cp->ph,
648                               &cp->pref);
649       GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
650                                    c->details.application.cp_tail,
651                                    cp);
652       GNUNET_free (cp);
653     }
654     break;
655   case CT_TRANSPORT:
656     if (transport_client == c)
657       transport_client = NULL;
658     GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
659                                              &free_session,
660                                              c);
661     GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
662     break;
663   }
664   GNUNET_free (c);
665 }
666
667
668 /**
669  * Task run at the end during shutdown.
670  *
671  * @param cls unused
672  */
673 static void
674 final_cleanup (void *cls)
675 {
676   (void) cls;
677   if (NULL != stats)
678   {
679     GNUNET_STATISTICS_destroy (stats,
680                                GNUNET_NO);
681     stats = NULL;
682   }
683   if (NULL != plugin)
684   {
685     GNUNET_PLUGIN_unload (plugin_name,
686                           plugin);
687     plugin = NULL;
688   }
689   if (NULL != plugin_name)
690   {
691     GNUNET_free (plugin_name);
692     plugin_name = NULL;
693   }
694 }
695
696
697 /**
698  * Task run during shutdown.
699  *
700  * @param cls unused
701  */
702 static void
703 cleanup_task (void *cls)
704 {
705   (void) cls;
706   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
707               "ATS shutdown initiated\n");
708   GNUNET_SCHEDULER_add_now (&final_cleanup,
709                             NULL);
710 }
711
712
713 /**
714  * Process template requests.
715  *
716  * @param cls closure
717  * @param cfg configuration to use
718  * @param service the initialized service
719  */
720 static void
721 run (void *cls,
722      const struct GNUNET_CONFIGURATION_Handle *cfg,
723      struct GNUNET_SERVICE_Handle *service)
724 {
725   static struct GNUNET_ATS_PluginEnvironment env;
726   char *solver;
727
728   stats = GNUNET_STATISTICS_create ("ats",
729                                     cfg);
730   if (GNUNET_SYSERR ==
731       GNUNET_CONFIGURATION_get_value_string (cfg,
732                                              "ats",
733                                              "SOLVER",
734                                              &solver))
735   {
736     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
737                 "No ATS solver configured, using 'simple' approach\n");
738     solver = GNUNET_strdup ("simple");
739   }
740   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
741                                  NULL);
742   env.cls = NULL;
743   env.cfg = cfg;
744   env.stats = stats;
745   env.suggest_cb = &suggest_cb;
746   env.allocate_cb = &allocate_cb;
747   GNUNET_asprintf (&plugin_name,
748                    "libgnunet_plugin_ats2_%s",
749                    solver);
750   GNUNET_free (solver);
751   if (NULL == (plugin = GNUNET_PLUGIN_load (plugin_name,
752                                             &env)))
753   {
754     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
755                 _("Failed to initialize solver `%s'!\n"),
756                 plugin_name);
757     GNUNET_SCHEDULER_shutdown ();
758     return;
759   }
760 }
761
762
763 /**
764  * Define "main" method using service macro.
765  */
766 GNUNET_SERVICE_MAIN
767 ("ats",
768  GNUNET_SERVICE_OPTION_NONE,
769  &run,
770  &client_connect_cb,
771  &client_disconnect_cb,
772  NULL,
773  GNUNET_MQ_hd_fixed_size (suggest,
774                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
775                           struct ExpressPreferenceMessage,
776                           NULL),
777  GNUNET_MQ_hd_fixed_size (suggest_cancel,
778                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
779                           struct ExpressPreferenceMessage,
780                           NULL),
781  GNUNET_MQ_hd_fixed_size (start,
782                           GNUNET_MESSAGE_TYPE_ATS_START,
783                           struct GNUNET_MessageHeader,
784                           NULL),
785  GNUNET_MQ_hd_var_size (session_add,
786                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
787                         struct SessionAddMessage,
788                         NULL),
789  GNUNET_MQ_hd_var_size (session_add,
790                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
791                         struct SessionAddMessage,
792                         NULL),
793  GNUNET_MQ_hd_fixed_size (session_update,
794                           GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
795                           struct SessionUpdateMessage,
796                           NULL),
797  GNUNET_MQ_hd_fixed_size (session_del,
798                           GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
799                           struct SessionDelMessage,
800                           NULL),
801  GNUNET_MQ_handler_end ());
802
803
804 /* end of gnunet-service-ats.c */