high-level new ATS service implementation
[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 /**
19  * @file ats/gnunet-service-ats-new.c
20  * @brief ats service
21  * @author Matthias Wachs
22  * @author Christian Grothoff
23  *
24  * TODO:
25  * - implement messages ATS -> transport
26  * - implement loading / unloading of ATS plugins
27  * - expose plugin the API to send messages ATS -> transport
28  */
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_ats_plugin_new.h"
33 #include "ats2.h"
34
35
36 /**
37  * What type of client is this client?
38  */
39 enum ClientType {
40   /**
41    * We don't know yet.
42    */
43   CT_NONE = 0,
44
45   /**
46    * Transport service.
47    */
48   CT_TRANSPORT,
49
50   /**
51    * Application.
52    */
53   CT_APPLICATION
54 };
55
56
57 /**
58  * Information we track per client.
59  */
60 struct Client;
61
62 /**
63  * Preferences expressed by a client are kept in a DLL per client.
64  */
65 struct ClientPreference
66 {
67   /**
68    * DLL pointer.
69    */
70   struct ClientPreference *next;
71
72   /**
73    * DLL pointer.
74    */
75   struct ClientPreference *prev;
76
77   /**
78    * Which client expressed the preference?
79    */
80   struct Client *client;
81
82   /**
83    * Plugin's representation of the preference.
84    */
85   struct GNUNET_ATS_PreferenceHandle *ph;
86   
87   /**
88    * Details about the preference.
89    */
90   struct GNUNET_ATS_Preference pref;
91 };
92
93
94 /**
95  * Information about ongoing sessions of the transport client.
96  */
97 struct GNUNET_ATS_Session
98 {
99
100   /**
101    * Session data exposed to the plugin. 
102    */
103   struct GNUNET_ATS_SessionData data;
104
105   /**
106    * The transport client that provided the session.
107    */
108   struct Client *client;
109
110   /**
111    * Session state in the plugin.
112    */
113   struct GNUNET_ATS_SessionHandle *sh;
114   
115   /**
116    * Unique ID for the session when talking with the client.
117    */ 
118   uint32_t session_id;
119   
120 };
121
122
123 /**
124  * Information we track per client.
125  */
126 struct Client
127 {
128   /**
129    * Type of the client, initially #CT_NONE.
130    */
131   enum ClientType type;
132
133   /**
134    * Service handle of the client.
135    */
136   struct GNUNET_SERVICE_Client *client;
137
138   /**
139    * Message queue to talk to the client.
140    */
141   struct GNUNET_MQ_Handle *mq;
142
143   /**
144    * Details depending on @e type.
145    */
146   union {
147
148     struct {
149
150       /**
151        * Head of DLL of preferences expressed by this client.
152        */
153       struct ClientPreference *cp_head;
154       
155       /**
156        * Tail of DLL of preferences expressed by this client.
157        */
158       struct ClientPreference *cp_tail;
159       
160     } application;
161
162     struct {
163
164       /**
165        * Map from session IDs to `struct GNUNET_ATS_Session` objects.
166        */
167       struct GNUNET_CONTAINER_MultiHashMap32 *sessions;
168       
169     } transport;
170     
171   } details;
172
173 };
174
175
176 /**
177  * Handle for statistics.
178  */
179 static struct GNUNET_STATISTICS_Handle *GSA_stats;
180
181 /**
182  * Our solver.
183  */
184 static struct GNUNET_ATS_SolverFunctions *plugin;
185
186 /**
187  * The transport client (there can only be one at a time).
188  */
189 static struct Client *transport_client;
190
191
192 /**
193  * Convert @a properties to @a prop
194  *
195  * @param properties in NBO
196  * @param prop[out] in HBO
197  */
198 static void
199 prop_ntoh (const struct PropertiesNBO *properties,
200            struct GNUNET_ATS_Properties *prop)
201 {
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);
211 }
212
213
214 /**
215  * We have received a `struct ExpressPreferenceMessage` from an application client.  
216  *
217  * @param cls handle to the client
218  * @param msg the start message
219  */
220 static void
221 handle_suggest (void *cls,
222                 const struct ExpressPreferenceMessage *msg)
223 {
224   struct Client *c = cls;
225   struct ClientPreference *cp;
226
227   if (CT_NONE == c->type)
228     c->type = CT_APPLICATION;
229   if (CT_APPLICATION != c->type)
230   {
231     GNUNET_break (0);
232     GNUNET_SERVICE_client_drop (c->client);
233     return;
234   }
235   cp = GNUNET_new (struct ClientPreference);
236   cp->client = c;
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,
241                                    &cp->pref);
242   GNUNET_CONTAINER_DLL_insert (c->details.application.cp_head,
243                                c->details.application.cp_tail,
244                                cp);
245   GNUNET_SERVICE_client_continue (c->client);
246 }
247
248
249 /**
250  * We have received a `struct ExpressPreferenceMessage` from an application client.  
251  *
252  * @param cls handle to the client
253  * @param msg the start message
254  */
255 static void
256 handle_suggest_cancel (void *cls,
257                        const struct ExpressPreferenceMessage *msg)
258 {
259   struct Client *c = cls;
260   struct ClientPreference *cp;
261   
262   if (CT_NONE == c->type)
263     c->type = CT_APPLICATION;
264   if (CT_APPLICATION != c->type)
265   {
266     GNUNET_break (0);
267     GNUNET_SERVICE_client_drop (c->client);
268     return;
269   }
270   for (cp = c->details.application.cp_head;
271        NULL != cp;
272        cp = cp->next)
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,
276                        &msg->peer,
277                        sizeof (struct GNUNET_PeerIdentity))) )
278       break;
279   if (NULL == cp)
280   {
281     GNUNET_break (0);
282     GNUNET_SERVICE_client_drop (c->client);
283     return;
284   }
285   plugin->preference_del (plugin->cls,
286                           cp->ph,
287                           &cp->pref);
288   GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
289                                c->details.application.cp_tail,
290                                cp);
291   GNUNET_free (cp);
292   GNUNET_SERVICE_client_continue (c->client);
293 }
294
295
296 /**
297  * Handle 'start' messages from transport clients.
298  *
299  * @param cls client that sent the request
300  * @param message the request message
301  */
302 static void
303 handle_start (void *cls,
304               const struct GNUNET_MessageHeader *hdr)
305 {
306   struct Client *c = cls;
307
308   if (CT_NONE != c->type)
309   {
310     GNUNET_break (0);
311     GNUNET_SERVICE_client_drop (c->client);
312     return;
313   }
314   c->type = CT_TRANSPORT;
315   c->details.transport.sessions
316     = GNUNET_CONTAINER_multihashmap32_create (128);
317   if (NULL != transport_client)
318   {
319     GNUNET_SERVICE_client_drop (transport_client->client);
320     transport_client = NULL;
321   }
322   transport_client = c;
323   GNUNET_SERVICE_client_continue (c->client);
324 }
325
326
327 /**
328  * Check 'session_add' message is well-formed and comes from a 
329  * transport client.
330  *
331  * @param cls client that sent the request
332  * @param message the request message
333  * @return #GNUNET_OK if @a message is well-formed
334  */
335 static int
336 check_session_add (void *cls,
337                    const struct SessionAddMessage *message)
338 {
339   struct Client *c = cls;
340
341   GNUNET_MQ_check_zero_termination (message);
342   if (CT_TRANSPORT != c->type)
343   {
344     GNUNET_break (0);
345     return GNUNET_SYSERR;
346   }
347   return GNUNET_OK;
348 }
349
350
351 /**
352  * Handle 'session add' messages from transport clients.
353  *
354  * @param cls client that sent the request
355  * @param message the request message
356  */
357 static void
358 handle_session_add (void *cls,
359                     const struct SessionAddMessage *message)
360 {
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));
365
366   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
367                                                  message->session_id);
368   if (NULL != session)
369   {
370     GNUNET_break (0);
371     GNUNET_SERVICE_client_drop (c->client);
372     return;
373   }
374   session = GNUNET_new (struct GNUNET_ATS_Session);
375   session->data.session = session;
376   session->client = c;
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,
384                                                       message->session_id,
385                                                       session,
386                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
387   session->sh = plugin->session_add (plugin->cls,
388                                      &session->data);
389   GNUNET_SERVICE_client_continue (c->client);
390 }
391
392
393
394 /**
395  * Handle 'session update' messages from transport clients.
396  *
397  * @param cls client that sent the request
398  * @param msg the request message
399  */
400 static void
401 handle_session_update (void *cls,
402                        const struct SessionUpdateMessage *msg)
403 {
404   struct Client *c = cls;
405   struct GNUNET_ATS_Session *session;
406   
407   if (CT_TRANSPORT != c->type)
408   {
409     GNUNET_break (0);
410     GNUNET_SERVICE_client_drop (c->client);
411     return;
412   }
413   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
414                                                  msg->session_id);
415   if (NULL == session)
416   {
417     GNUNET_break (0);
418     GNUNET_SERVICE_client_drop (c->client);
419     return;
420   }
421   prop_ntoh (&msg->properties,
422              &session->data.prop);
423   plugin->session_update (plugin->cls,
424                           session->sh,
425                           &session->data);
426   GNUNET_SERVICE_client_continue (c->client);
427 }
428
429
430 /**
431  * Handle 'session delete' messages from transport clients.
432  *
433  * @param cls client that sent the request
434  * @param message the request message
435  */
436 static void
437 handle_session_del (void *cls,
438                     const struct SessionDelMessage *message)
439 {
440   struct Client *c = cls;
441   struct GNUNET_ATS_Session *session;
442
443   if (CT_TRANSPORT != c->type)
444   {
445     GNUNET_break (0);
446     GNUNET_SERVICE_client_drop (c->client);
447     return;
448   }
449   session = GNUNET_CONTAINER_multihashmap32_get (c->details.transport.sessions,
450                                                  message->session_id);
451   if (NULL == session)
452   {
453     GNUNET_break (0);
454     GNUNET_SERVICE_client_drop (c->client);
455     return;
456   } 
457   plugin->session_del (plugin->cls,
458                        session->sh,
459                        &session->data);
460   GNUNET_assert (GNUNET_YES ==
461                  GNUNET_CONTAINER_multihashmap32_remove (c->details.transport.sessions,
462                                                          session->session_id,
463                                                          session));
464   GNUNET_free (session);
465   GNUNET_SERVICE_client_continue (c->client);
466 }
467
468
469 /**
470  * A client connected to us. Setup the local client
471  * record.
472  *
473  * @param cls unused
474  * @param client handle of the client
475  * @param mq message queue to talk to @a client
476  * @return @a client
477  */
478 static void *
479 client_connect_cb (void *cls,
480                    struct GNUNET_SERVICE_Client *client,
481                    struct GNUNET_MQ_Handle *mq)
482 {
483   struct Client *c = GNUNET_new (struct Client);
484
485   c->client = client;
486   c->mq = mq;
487   return c;
488 }
489
490
491 /**
492  * Function called on each session to release associated state
493  * on transport disconnect.
494  *
495  * @param cls the `struct Client`
496  * @param key unused (session_id)
497  * @param value a `struct GNUNET_ATS_Session`
498  */
499 static int
500 free_session (void *cls,
501               uint32_t key,
502               void *value)
503 {
504   struct Client *c = cls;
505   struct GNUNET_ATS_Session *session = value;
506
507   (void) key;
508   GNUNET_assert (c == session->client);
509   plugin->session_del (plugin->cls,
510                        session->sh,
511                        &session->data);
512   GNUNET_free (session);
513   return GNUNET_OK;
514 }
515
516
517 /**
518  * A client disconnected from us.  Tear down the local client
519  * record.
520  *
521  * @param cls unused
522  * @param client handle of the client
523  * @param app_ctx our `struct Client`
524  */
525 static void
526 client_disconnect_cb (void *cls,
527                       struct GNUNET_SERVICE_Client *client,
528                       void *app_ctx)
529 {
530   struct Client *c = app_ctx;
531
532   (void) cls;
533   GNUNET_assert (c->client == client);
534   switch (c->type)
535   {
536   case CT_NONE:
537     break;
538   case CT_APPLICATION:
539     for (struct ClientPreference *cp = c->details.application.cp_head;
540          NULL != cp;
541          cp = c->details.application.cp_head)
542     {
543       plugin->preference_del (plugin->cls,
544                               cp->ph,
545                               &cp->pref);
546       GNUNET_CONTAINER_DLL_remove (c->details.application.cp_head,
547                                    c->details.application.cp_tail,
548                                    cp);
549       GNUNET_free (cp);
550     }
551     break;
552   case CT_TRANSPORT:
553     if (transport_client == c)
554       transport_client = NULL;
555     GNUNET_CONTAINER_multihashmap32_iterate (c->details.transport.sessions,
556                                              &free_session,
557                                              c);
558     GNUNET_CONTAINER_multihashmap32_destroy (c->details.transport.sessions);
559     break;
560   }
561   GNUNET_free (c);
562 }
563
564
565 /**
566  * Task run during shutdown.
567  *
568  * @param cls unused
569  */
570 static void
571 cleanup_task (void *cls)
572 {
573   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574               "ATS shutdown initiated\n");
575   if (NULL != GSA_stats)
576   {
577     GNUNET_STATISTICS_destroy (GSA_stats,
578                                GNUNET_NO);
579     GSA_stats = NULL;
580   }
581 }
582
583
584 /**
585  * Process template requests.
586  *
587  * @param cls closure
588  * @param cfg configuration to use
589  * @param service the initialized service
590  */
591 static void
592 run (void *cls,
593      const struct GNUNET_CONFIGURATION_Handle *cfg,
594      struct GNUNET_SERVICE_Handle *service)
595 {
596   GSA_stats = GNUNET_STATISTICS_create ("ats",
597                                         cfg);
598   GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
599                                  NULL);
600 #if 0
601   if (GNUNET_OK !=
602       GAS_plugin_init (cfg))
603   {
604     GNUNET_break (0);
605     GNUNET_SCHEDULER_shutdown ();
606     return;
607   }
608 #endif
609 }
610
611
612 /**
613  * Define "main" method using service macro.
614  */
615 GNUNET_SERVICE_MAIN
616 ("ats",
617  GNUNET_SERVICE_OPTION_NONE,
618  &run,
619  &client_connect_cb,
620  &client_disconnect_cb,
621  NULL,
622  GNUNET_MQ_hd_fixed_size (suggest,
623                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST,
624                           struct ExpressPreferenceMessage,
625                           NULL),
626  GNUNET_MQ_hd_fixed_size (suggest_cancel,
627                           GNUNET_MESSAGE_TYPE_ATS_SUGGEST_CANCEL,
628                           struct ExpressPreferenceMessage,
629                           NULL),
630  GNUNET_MQ_hd_fixed_size (start,
631                           GNUNET_MESSAGE_TYPE_ATS_START,
632                           struct GNUNET_MessageHeader,
633                           NULL),
634  GNUNET_MQ_hd_var_size (session_add,
635                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD,
636                         struct SessionAddMessage,
637                         NULL),
638  GNUNET_MQ_hd_var_size (session_add,
639                         GNUNET_MESSAGE_TYPE_ATS_SESSION_ADD_INBOUND_ONLY,
640                         struct SessionAddMessage,
641                         NULL),
642  GNUNET_MQ_hd_fixed_size (session_update, 
643                           GNUNET_MESSAGE_TYPE_ATS_SESSION_UPDATE,
644                           struct SessionUpdateMessage,
645                           NULL),
646  GNUNET_MQ_hd_fixed_size (session_del, 
647                           GNUNET_MESSAGE_TYPE_ATS_SESSION_DEL,
648                           struct SessionDelMessage,
649                           NULL),
650  GNUNET_MQ_handler_end ());
651
652
653 /* end of gnunet-service-ats.c */