-fix ril delete address handling
[oweals/gnunet.git] / src / ats / gnunet-service-ats_plugins.c
1 /*
2  This file is part of GNUnet.
3  (C) 2011-2014 Christian Grothoff (and other contributing authors)
4
5  GNUnet is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published
7  by the Free Software Foundation; either version 3, or (at your
8  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  General Public License for more details.
14
15  You should have received a copy of the GNU General Public License
16  along with GNUnet; see the file COPYING.  If not, write to the
17  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  Boston, MA 02111-1307, USA.
19  */
20
21 /**
22  * @file ats/gnunet-service-ats_plugins.c
23  * @brief ats service plugin management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet_ats_plugin.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_preferences.h"
31 #include "gnunet-service-ats_plugins.h"
32 #include "gnunet-service-ats_scheduling.h"
33 #include "gnunet-service-ats_normalization.h"
34
35
36 /**
37  * Configured ATS solver
38  */
39 static int ats_mode;
40
41 /**
42  * Solver handle.
43  */
44 static struct GNUNET_ATS_SolverFunctions *sf;
45
46 /**
47  * Solver environment.
48  */
49 static struct GNUNET_ATS_PluginEnvironment env;
50
51 /**
52  * Solver plugin name as string
53  */
54 static char *plugin;
55
56
57 /**
58  * The preference changed for a peer, update solver.
59  *
60  * @param peer the peer
61  * @param kind the ATS kind
62  * @param pref_rel the new relative preference value
63  */
64 void
65 GAS_normalized_preference_changed (const struct GNUNET_PeerIdentity *peer,
66                                    enum GNUNET_ATS_PreferenceKind kind,
67                                    double pref_rel)
68 {
69   /* Tell solver about update */
70   sf->s_pref (sf->cls, peer, kind, pref_rel);
71 }
72
73
74 /**
75  * The relative value for a property changed
76  *
77  * @param address the peer
78  * @param type the ATS type
79  * @param prop_rel the new relative preference value
80  */
81 void
82 GAS_normalized_property_changed (struct ATS_Address *address,
83                                  uint32_t type,
84                                  double prop_rel)
85 {
86   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
87               "Normalized property %s for peer `%s' changed to %.3f \n",
88               GNUNET_ATS_print_property_type (type),
89               GNUNET_i2s (&address->peer),
90               prop_rel);
91   sf->s_address_update_property (sf->cls,
92                                  address,
93                                  type,
94                                  0,
95                                  prop_rel);
96 }
97
98
99 /**
100  * Solver information callback
101  *
102  * @param cls the closure
103  * @param op the operation
104  * @param status operation status
105  * @param add additional information
106  */
107 static void
108 solver_info_cb (void *cls,
109                 enum GAS_Solver_Operation op,
110                 enum GAS_Solver_Status status,
111                 enum GAS_Solver_Additional_Information add)
112 {
113   char *add_info;
114
115   switch (add) {
116     case GAS_INFO_NONE:
117       add_info = "GAS_INFO_NONE";
118       break;
119     case GAS_INFO_FULL:
120       add_info = "GAS_INFO_MLP_FULL";
121       break;
122     case GAS_INFO_UPDATED:
123       add_info = "GAS_INFO_MLP_UPDATED";
124       break;
125     case GAS_INFO_PROP_ALL:
126       add_info = "GAS_INFO_PROP_ALL";
127       break;
128     case GAS_INFO_PROP_SINGLE:
129       add_info = "GAS_INFO_PROP_SINGLE";
130       break;
131     default:
132       add_info = "INVALID";
133       break;
134   }
135   switch (op)
136   {
137     case GAS_OP_SOLVE_START:
138       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
139           "Solver notifies `%s' with result `%s' `%s'\n", "GAS_OP_SOLVE_START",
140           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL", add_info);
141       return;
142     case GAS_OP_SOLVE_STOP:
143       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
144           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_STOP",
145           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL", add_info);
146       return;
147
148     case GAS_OP_SOLVE_SETUP_START:
149       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
150           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_START",
151           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
152       return;
153
154     case GAS_OP_SOLVE_SETUP_STOP:
155       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
156           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_SETUP_STOP",
157           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
158       return;
159
160     case GAS_OP_SOLVE_MLP_LP_START:
161       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
162           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_START",
163           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
164       return;
165     case GAS_OP_SOLVE_MLP_LP_STOP:
166       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
167           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_LP_STOP",
168           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
169       return;
170
171     case GAS_OP_SOLVE_MLP_MLP_START:
172       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
173           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_START",
174           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
175       return;
176     case GAS_OP_SOLVE_MLP_MLP_STOP:
177       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
178           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_MLP_STOP",
179           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
180       return;
181     case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START:
182       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
183           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_START",
184           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
185       return;
186     case GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP:
187       GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
188           "Solver notifies `%s' with result `%s'\n", "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP",
189           (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
190       return;
191     default:
192       break;
193     }
194 }
195
196
197 /**
198  * Callback for solver to notify about assignment changes
199  *
200  * @param cls NULL
201  * @param address the address with changes
202  */
203 static void
204 bandwidth_changed_cb (void *cls,
205                       struct ATS_Address *address)
206 {
207   uint32_t diff_out;
208   uint32_t diff_in;
209
210   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
211               "Bandwidth assignment changed for peer %s \n",
212               GNUNET_i2s (&address->peer));
213
214   /* Notify performance clients about changes to address */
215   GAS_performance_notify_all_clients (&address->peer,
216                                       address->plugin,
217                                       address->addr,
218                                       address->addr_len,
219                                       address->active,
220                                       address->atsi,
221                                       address->atsi_count,
222                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
223                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
224
225   if ( (0 == address->assigned_bw_in) &&
226        (0 == address->assigned_bw_out) )
227   {
228     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
229                "Telling transport to disconnect peer `%s'\n",
230                 GNUNET_i2s (&address->peer));
231
232     /* Notify scheduling clients about suggestion */
233     GAS_scheduling_transmit_address_suggestion (&address->peer,
234                                                 address->session_id,
235                                                 GNUNET_BANDWIDTH_ZERO,
236                                                 GNUNET_BANDWIDTH_ZERO);
237     return;
238   }
239
240   /* Do bandwidth stability check */
241   diff_out = abs (address->assigned_bw_out - address->last_notified_bw_out);
242   diff_in = abs (address->assigned_bw_in - address->last_notified_bw_in);
243
244   if ( (diff_out < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) &&
245        (diff_in < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) )
246     return;
247
248   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
249       "Sending bandwidth update for peer `%s': %u %u\n",
250       GNUNET_i2s (&address->peer), address->assigned_bw_out,
251       address->assigned_bw_out);
252
253   /* *Notify scheduling clients about suggestion */
254   GAS_scheduling_transmit_address_suggestion (&address->peer,
255                                               address->session_id,
256                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
257                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
258
259   address->last_notified_bw_out = address->assigned_bw_out;
260   address->last_notified_bw_in = address->assigned_bw_in;
261 }
262
263
264 /**
265  * Load quotas for networks from configuration
266  *
267  * @param cfg configuration handle
268  * @param out_dest where to write outbound quotas
269  * @param in_dest where to write inbound quotas
270  * @param dest_length length of inbound and outbound arrays
271  * @return number of networks loaded
272  */
273 static unsigned int
274 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg,
275     unsigned long long *out_dest, unsigned long long *in_dest, int dest_length)
276 {
277   char * entry_in = NULL;
278   char * entry_out = NULL;
279   char * quota_out_str;
280   char * quota_in_str;
281   int c;
282   int res;
283
284   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
285   {
286     in_dest[c] = 0;
287     out_dest[c] = 0;
288     GNUNET_asprintf (&entry_out,
289                      "%s_QUOTA_OUT",
290                      GNUNET_ATS_print_network_type (c));
291     GNUNET_asprintf (&entry_in,
292                      "%s_QUOTA_IN",
293                      GNUNET_ATS_print_network_type (c));
294
295     /* quota out */
296     if (GNUNET_OK
297         == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", entry_out,
298             &quota_out_str))
299     {
300       res = GNUNET_NO;
301       if (0 == strcmp (quota_out_str, GNUNET_ATS_MaxBandwidthString))
302       {
303         out_dest[c] = GNUNET_ATS_MaxBandwidth;
304         res = GNUNET_YES;
305       }
306       if ((GNUNET_NO == res)
307           && (GNUNET_OK
308               == GNUNET_STRINGS_fancy_size_to_bytes (quota_out_str,
309                   &out_dest[c])))
310         res = GNUNET_YES;
311       if ((GNUNET_NO == res)
312           && (GNUNET_OK
313               == GNUNET_CONFIGURATION_get_value_number (cfg, "ats", entry_out,
314                   &out_dest[c])))
315         res = GNUNET_YES;
316
317       if (GNUNET_NO == res)
318       {
319         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
320                    _("Could not load quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
321                    GNUNET_ATS_print_network_type (c),
322                    quota_out_str,
323                    GNUNET_ATS_DefaultBandwidth);
324         out_dest[c] = GNUNET_ATS_DefaultBandwidth;
325       }
326       else
327       {
328         GNUNET_log(GNUNET_ERROR_TYPE_INFO,
329                    _("Outbound quota configure for network `%s' is %llu\n"),
330                    GNUNET_ATS_print_network_type (c),
331                    out_dest[c]);
332       }
333       GNUNET_free(quota_out_str);
334     }
335     else
336     {
337       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
338                  _("No outbound quota configured for network `%s', assigning default bandwidth %llu\n"),
339                  GNUNET_ATS_print_network_type (c),
340                  GNUNET_ATS_DefaultBandwidth);
341       out_dest[c] = GNUNET_ATS_DefaultBandwidth;
342     }
343
344     /* quota in */
345     if (GNUNET_OK
346         == GNUNET_CONFIGURATION_get_value_string (cfg, "ats", entry_in,
347             &quota_in_str))
348     {
349       res = GNUNET_NO;
350       if (0 == strcmp (quota_in_str, GNUNET_ATS_MaxBandwidthString))
351       {
352         in_dest[c] = GNUNET_ATS_MaxBandwidth;
353         res = GNUNET_YES;
354       }
355       if ((GNUNET_NO == res)
356           && (GNUNET_OK
357               == GNUNET_STRINGS_fancy_size_to_bytes (quota_in_str, &in_dest[c])))
358         res = GNUNET_YES;
359       if ((GNUNET_NO == res)
360           && (GNUNET_OK
361               == GNUNET_CONFIGURATION_get_value_number (cfg, "ats", entry_in,
362                   &in_dest[c])))
363         res = GNUNET_YES;
364
365       if (GNUNET_NO == res)
366       {
367         GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
368                    _("Could not load quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
369                    GNUNET_ATS_print_network_type (c),
370                    quota_in_str,
371                    GNUNET_ATS_DefaultBandwidth);
372         in_dest[c] = GNUNET_ATS_DefaultBandwidth;
373       }
374       else
375       {
376         GNUNET_log(GNUNET_ERROR_TYPE_INFO,
377                    _("Inbound quota configured for network `%s' is %llu\n"),
378                    GNUNET_ATS_print_network_type (c),
379                    in_dest[c]);
380       }
381       GNUNET_free(quota_in_str);
382     }
383     else
384     {
385       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
386                  _("No outbound quota configure for network `%s', assigning default bandwidth %llu\n"),
387                  GNUNET_ATS_print_network_type (c),
388                  GNUNET_ATS_DefaultBandwidth);
389       in_dest[c] = GNUNET_ATS_DefaultBandwidth;
390     }
391     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
392                "Loaded quota for network `%s' (in/out): %llu %llu\n",
393                GNUNET_ATS_print_network_type (c),
394                in_dest[c],
395                out_dest[c]);
396     GNUNET_free(entry_out);
397     GNUNET_free(entry_in);
398   }
399   return GNUNET_ATS_NetworkTypeCount;
400 }
401
402
403 /**
404  * Initialize plugins subsystem.
405  *
406  * @param cfg configuration to use
407  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (failed to load
408  *         solver plugin)
409  */
410 int
411 GAS_plugins_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
412 {
413   unsigned long long quotas_in[GNUNET_ATS_NetworkTypeCount];
414   unsigned long long quotas_out[GNUNET_ATS_NetworkTypeCount];
415   char *mode_str;
416   char *plugin_short;
417   int c;
418
419   /* Figure out configured solution method */
420   if (GNUNET_SYSERR ==
421       GNUNET_CONFIGURATION_get_value_string (cfg, "ats", "MODE", &mode_str))
422   {
423     GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
424                "No resource assignment method configured, using proportional approach\n");
425     ats_mode = MODE_PROPORTIONAL;
426   }
427   else
428   {
429     for (c = 0; c < strlen (mode_str); c++)
430       mode_str[c] = toupper (mode_str[c]);
431     if (0 == strcmp (mode_str, "PROPORTIONAL"))
432       ats_mode = MODE_PROPORTIONAL;
433     else if (0 == strcmp (mode_str, "MLP"))
434     {
435       ats_mode = MODE_MLP;
436 #if !HAVE_LIBGLPK
437       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
438                  "Assignment method `%s' configured, but GLPK is not available, please install \n",
439                  mode_str);
440       ats_mode = MODE_PROPORTIONAL;
441 #endif
442     }
443     else if (0 == strcmp (mode_str, "RIL"))
444       ats_mode = MODE_RIL;
445     else
446     {
447       GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
448                  "Invalid resource assignment method `%s' configured, using proportional approach\n",
449                  mode_str);
450       ats_mode = MODE_PROPORTIONAL;
451     }
452     GNUNET_free(mode_str);
453   }
454
455   load_quotas (cfg,
456                quotas_out,
457                quotas_in,
458                GNUNET_ATS_NetworkTypeCount);
459   env.cls = NULL;
460   env.info_cb = &solver_info_cb;
461   env.bandwidth_changed_cb = &bandwidth_changed_cb;
462   env.get_preferences = &GAS_normalization_get_preferences_by_peer;
463   env.get_property = &GAS_normalization_get_properties;
464   env.cfg = cfg;
465   env.stats = GSA_stats;
466   env.addresses = GSA_addresses;
467   env.network_count = GNUNET_ATS_NetworkTypeCount;
468   for (c = 0; c < GNUNET_ATS_NetworkTypeCount; c++)
469   {
470     env.out_quota[c] = quotas_out[c];
471     env.in_quota[c] = quotas_in[c];
472   }
473
474   switch (ats_mode) {
475     case MODE_PROPORTIONAL:
476       plugin_short = "proportional";
477       break;
478     case MODE_MLP:
479       plugin_short = "mlp";
480       break;
481     case MODE_RIL:
482       plugin_short = "ril";
483       break;
484     default:
485       plugin_short = NULL;
486       break;
487   }
488   GNUNET_asprintf (&plugin,
489                    "libgnunet_plugin_ats_%s",
490                    plugin_short);
491   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
492               "Initializing solver `%s '`%s'\n",
493               plugin_short,
494               plugin);
495   if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env)))
496   {
497     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498                 _("Failed to initialize solver `%s'!\n"),
499                 plugin);
500     return GNUNET_SYSERR;
501   }
502   return GNUNET_OK;
503 }
504
505
506 /**
507  * Shutdown address subsystem.
508  */
509 void
510 GAS_plugins_done ()
511 {
512   GNUNET_PLUGIN_unload (plugin,
513                         sf);
514   sf = NULL;
515   GNUNET_free (plugin);
516   plugin = NULL;
517 }
518
519
520 void
521 GAS_plugin_new_address (struct ATS_Address *new_address,
522                         enum GNUNET_ATS_Network_Type addr_net,
523                         const struct GNUNET_ATS_Information *atsi,
524                         uint32_t atsi_count)
525 {
526   sf->s_add (sf->cls, new_address, addr_net);
527   sf->s_bulk_start (sf->cls);
528   GAS_normalization_normalize_property (new_address,
529                                         atsi,
530                                         atsi_count);
531   sf->s_bulk_stop (sf->cls);
532 }
533
534
535 void
536 GAS_plugin_update_address (struct ATS_Address *address,
537                            const struct GNUNET_ATS_Information *atsi,
538                            uint32_t atsi_count)
539 {
540   sf->s_bulk_start (sf->cls);
541   GAS_normalization_normalize_property (address,
542                                         atsi,
543                                         atsi_count);
544   sf->s_bulk_stop (sf->cls);
545 }
546
547
548 void
549 GAS_plugin_delete_address (struct ATS_Address *address)
550 {
551   sf->s_del (sf->cls, address, GNUNET_NO);
552 }
553
554
555 void
556 GAS_plugin_update_preferences (void *client,
557                                const struct GNUNET_PeerIdentity *peer,
558                                enum GNUNET_ATS_PreferenceKind kind,
559                                float score_abs)
560 {
561   sf->s_bulk_start (sf->cls);
562   /* Tell normalization about change, normalization will call callback if preference changed */
563   GAS_normalization_normalize_preference (client, peer, kind, score_abs);
564   sf->s_bulk_stop (sf->cls);
565 }
566
567
568 void
569 GAS_plugin_preference_feedback (void *application,
570                                 const struct GNUNET_PeerIdentity *peer,
571                                 const struct GNUNET_TIME_Relative scope,
572                                 enum GNUNET_ATS_PreferenceKind kind,
573                                 float score_abs)
574 {
575   sf->s_feedback (sf->cls,
576                      application,
577                      peer,
578                      scope,
579                      kind,
580                      score_abs);
581 }
582
583
584 void
585 GAS_plugin_solver_lock ()
586 {
587   sf->s_bulk_start (sf->cls);
588 }
589
590
591 void
592 GAS_plugin_solver_unlock ()
593 {
594   sf->s_bulk_start (sf->cls);
595 }
596
597
598 void
599 GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid)
600 {
601   const struct ATS_Address *aa;
602
603   aa = sf->s_get (sf->cls, pid);
604   if (NULL == aa)
605   {
606     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
607                 "Cannot suggest address for peer `%s'\n",
608                 GNUNET_i2s (pid));
609     return;
610   }
611   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
612              "Suggesting address %p for peer `%s'\n",
613              aa,
614              GNUNET_i2s (pid));
615
616   GAS_scheduling_transmit_address_suggestion (pid,
617                                               aa->session_id,
618                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
619                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
620
621   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
622              "Address %p ready for suggestion\n",
623              aa);
624 }
625
626
627 void
628 GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid)
629 {
630   sf->s_get_stop (sf->cls, pid);
631 }
632
633
634 /* end of gnunet-service-ats_plugins.c */