-fix config, shutdown issue
[oweals/gnunet.git] / src / ats / gnunet-service-ats_plugins.c
1 /*
2  This file is part of GNUnet.
3  Copyright (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_connectivity.h"
30 #include "gnunet-service-ats_performance.h"
31 #include "gnunet-service-ats_preferences.h"
32 #include "gnunet-service-ats_plugins.h"
33 #include "gnunet-service-ats_scheduling.h"
34 #include "gnunet-service-ats_normalization.h"
35
36
37 /**
38  * Solver handle.
39  */
40 static struct GNUNET_ATS_SolverFunctions *sf;
41
42 /**
43  * Solver environment.
44  */
45 static struct GNUNET_ATS_PluginEnvironment env;
46
47 /**
48  * Solver plugin name as string
49  */
50 static char *plugin;
51
52
53 /**
54  * The preference changed for a peer, update solver.
55  *
56  * @param peer the peer
57  * @param kind the ATS kind
58  * @param pref_rel the new relative preference value
59  */
60 void
61 GAS_plugin_notify_preference_changed (const struct GNUNET_PeerIdentity *peer,
62                                       enum GNUNET_ATS_PreferenceKind kind,
63                                       double pref_rel)
64 {
65   sf->s_pref (sf->cls,
66               peer,
67               kind,
68               pref_rel);
69 }
70
71
72 /**
73  * The relative value for a property changed
74  *
75  * @param address the peer
76  * @param type the ATS type
77  * @param prop_rel the new relative preference value
78  */
79 void
80 GAS_plugin_notify_property_changed (struct ATS_Address *address,
81                                     enum GNUNET_ATS_Property type,
82                                     double prop_rel)
83 {
84   sf->s_address_update_property (sf->cls,
85                                  address,
86                                  type,
87                                  0,
88                                  prop_rel);
89 }
90
91
92 /**
93  * Solver information callback
94  *
95  * @param cls the closure
96  * @param op the operation
97  * @param status operation status
98  * @param add additional information
99  */
100 static void
101 solver_info_cb (void *cls,
102                 enum GAS_Solver_Operation op,
103                 enum GAS_Solver_Status status,
104                 enum GAS_Solver_Additional_Information add)
105 {
106   const char *add_info;
107
108   switch (add) {
109     case GAS_INFO_NONE:
110       add_info = "GAS_INFO_NONE";
111       break;
112     case GAS_INFO_FULL:
113       add_info = "GAS_INFO_MLP_FULL";
114       break;
115     case GAS_INFO_UPDATED:
116       add_info = "GAS_INFO_MLP_UPDATED";
117       break;
118     case GAS_INFO_PROP_ALL:
119       add_info = "GAS_INFO_PROP_ALL";
120       break;
121     case GAS_INFO_PROP_SINGLE:
122       add_info = "GAS_INFO_PROP_SINGLE";
123       break;
124     default:
125       add_info = "INVALID";
126       break;
127   }
128   switch (op)
129   {
130   case GAS_OP_SOLVE_START:
131     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
132                 "Solver notifies `%s' with result `%s' `%s'\n",
133                 "GAS_OP_SOLVE_START",
134                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL",
135                 add_info);
136     return;
137   case GAS_OP_SOLVE_STOP:
138     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
139                 "Solver notifies `%s' with result `%s'\n",
140                 "GAS_OP_SOLVE_STOP",
141                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL",
142                 add_info);
143     return;
144   case GAS_OP_SOLVE_SETUP_START:
145     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
146                 "Solver notifies `%s' with result `%s'\n",
147                 "GAS_OP_SOLVE_SETUP_START",
148                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
149     return;
150   case GAS_OP_SOLVE_SETUP_STOP:
151     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
152                 "Solver notifies `%s' with result `%s'\n",
153                 "GAS_OP_SOLVE_SETUP_STOP",
154                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
155     return;
156   case GAS_OP_SOLVE_MLP_LP_START:
157     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
158                 "Solver notifies `%s' with result `%s'\n",
159                 "GAS_OP_SOLVE_LP_START",
160                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
161     return;
162   case GAS_OP_SOLVE_MLP_LP_STOP:
163     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
164                 "Solver notifies `%s' with result `%s'\n",
165                 "GAS_OP_SOLVE_LP_STOP",
166                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
167     return;
168   case GAS_OP_SOLVE_MLP_MLP_START:
169     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
170                 "Solver notifies `%s' with result `%s'\n",
171                 "GAS_OP_SOLVE_MLP_START",
172                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
173     return;
174   case GAS_OP_SOLVE_MLP_MLP_STOP:
175     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
176                "Solver notifies `%s' with result `%s'\n",
177                "GAS_OP_SOLVE_MLP_STOP",
178                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
179     return;
180   case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START:
181     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
182                "Solver notifies `%s' with result `%s'\n",
183                "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",
189                "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP",
190                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
191     return;
192   default:
193     break;
194   }
195 }
196
197
198 /**
199  * Callback for solver to notify about assignment changes
200  *
201  * @param cls NULL
202  * @param address the address with changes
203  */
204 static void
205 bandwidth_changed_cb (void *cls,
206                       struct ATS_Address *address)
207 {
208   uint32_t diff_out;
209   uint32_t diff_in;
210
211   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
212               "Bandwidth assignment changed for peer %s \n",
213               GNUNET_i2s (&address->peer));
214
215   /* Notify performance clients about changes to address */
216   GAS_performance_notify_all_clients (&address->peer,
217                                       address->plugin,
218                                       address->addr,
219                                       address->addr_len,
220                                       address->active,
221                                       address->atsi,
222                                       address->atsi_count,
223                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
224                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
225
226   if ( (0 == address->assigned_bw_in) &&
227        (0 == address->assigned_bw_out) )
228   {
229     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
230                "Telling transport to disconnect peer `%s'\n",
231                 GNUNET_i2s (&address->peer));
232
233     /* Notify scheduling clients about suggestion */
234     GAS_scheduling_transmit_address_suggestion (&address->peer,
235                                                 address->session_id,
236                                                 GNUNET_BANDWIDTH_ZERO,
237                                                 GNUNET_BANDWIDTH_ZERO);
238     return;
239   }
240
241   /* Do bandwidth stability check */
242   diff_out = abs (address->assigned_bw_out - address->last_notified_bw_out);
243   diff_in = abs (address->assigned_bw_in - address->last_notified_bw_in);
244
245   if ( (diff_out < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) &&
246        (diff_in < htonl(GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) )
247     return;
248
249   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
250              "Sending bandwidth update for peer `%s': %u/%u\n",
251              GNUNET_i2s (&address->peer),
252              address->assigned_bw_out,
253              address->assigned_bw_out);
254
255   /* *Notify scheduling clients about suggestion */
256   GAS_scheduling_transmit_address_suggestion (&address->peer,
257                                               address->session_id,
258                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
259                                               GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
260
261   address->last_notified_bw_out = address->assigned_bw_out;
262   address->last_notified_bw_in = address->assigned_bw_in;
263 }
264
265
266 /**
267  * Convert quota from text to numeric value.
268  *
269  * @param quota_str the value found in the configuration
270  * @param direction direction of the quota
271  * @param network network the quota applies to
272  * @return numeric quota value to use
273  */
274 static unsigned long long
275 parse_quota (const char *quota_str,
276              const char *direction,
277              enum GNUNET_ATS_Network_Type network)
278 {
279   int res;
280   unsigned long long ret;
281
282   res = GNUNET_NO;
283   if (0 == strcmp (quota_str, GNUNET_ATS_MaxBandwidthString))
284   {
285     ret = GNUNET_ATS_MaxBandwidth;
286     res = GNUNET_YES;
287   }
288   if ((GNUNET_NO == res) &&
289       (GNUNET_OK ==
290        GNUNET_STRINGS_fancy_size_to_bytes (quota_str,
291                                            &ret)))
292     res = GNUNET_YES;
293   if ((GNUNET_NO == res) &&
294       (1 ==
295        sscanf (quota_str,
296                "%llu",
297                &ret)))
298     res = GNUNET_YES;
299   if (GNUNET_NO == res)
300   {
301     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
302                 _("Could not load %s quota for network `%s':  `%s', assigning default bandwidth %llu\n"),
303                 direction,
304                 GNUNET_ATS_print_network_type (network),
305                 quota_str,
306                 GNUNET_ATS_DefaultBandwidth);
307     ret = GNUNET_ATS_DefaultBandwidth;
308   }
309   else
310   {
311     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
312                 _("%s quota configured for network `%s' is %llu\n"),
313                 direction,
314                 GNUNET_ATS_print_network_type (network),
315                 ret);
316   }
317   return ret;
318 }
319
320
321 /**
322  * Load quota value from the configuration @a cfg for the
323  * given network @a type and @a direction.
324  *
325  * @param cfg configuration to parse
326  * @param type network type to parse for
327  * @param direction traffic direction to parse for
328  * @return quota to apply
329  */
330 static unsigned long long
331 load_quota (const struct GNUNET_CONFIGURATION_Handle *cfg,
332             enum GNUNET_ATS_Network_Type type,
333             const char *direction)
334 {
335   char *entry;
336   char *quota_str;
337   unsigned long long ret;
338
339   GNUNET_asprintf (&entry,
340                    "%s_QUOTA_%s",
341                    GNUNET_ATS_print_network_type (type),
342                    direction);
343   if (GNUNET_OK ==
344       GNUNET_CONFIGURATION_get_value_string (cfg,
345                                              "ats",
346                                              entry,
347                                              &quota_str))
348   {
349     ret = parse_quota (quota_str,
350                        direction,
351                        type);
352     GNUNET_free (quota_str);
353   }
354   else
355   {
356     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
357                 _("No %s-quota configured for network `%s', assigning default bandwidth %llu\n"),
358                 direction,
359                 GNUNET_ATS_print_network_type (type),
360                 GNUNET_ATS_DefaultBandwidth);
361     ret = GNUNET_ATS_DefaultBandwidth;
362   }
363   GNUNET_free (entry);
364   return ret;
365 }
366
367
368 /**
369  * Load quotas for networks from configuration
370  *
371  * @param cfg configuration handle
372  * @param out_dest where to write outbound quotas
373  * @param in_dest where to write inbound quotas
374  * @param dest_length length of inbound and outbound arrays
375  * @return number of networks loaded
376  */
377 static unsigned int
378 load_quotas (const struct GNUNET_CONFIGURATION_Handle *cfg,
379              unsigned long long *out_dest,
380              unsigned long long *in_dest,
381              int dest_length)
382 {
383   unsigned int c;
384
385   for (c = 0; (c < GNUNET_ATS_NetworkTypeCount) && (c < dest_length); c++)
386   {
387     in_dest[c] = load_quota (cfg,
388                              c,
389                              "out");
390     out_dest[c] = load_quota (cfg,
391                               c,
392                               "in");
393     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394                 "Loaded quota for network `%s' (in/out): %llu %llu\n",
395                 GNUNET_ATS_print_network_type (c),
396                 in_dest[c],
397                 out_dest[c]);
398   }
399   return c;
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_plugin_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
412 {
413   char *mode_str;
414
415   /* Figure out configured solution method */
416   if (GNUNET_SYSERR ==
417       GNUNET_CONFIGURATION_get_value_string (cfg,
418                                              "ats",
419                                              "MODE",
420                                              &mode_str))
421   {
422     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
423                 "No resource assignment method configured, using proportional approach\n");
424     mode_str = GNUNET_strdup ("proportional");
425   }
426   env.cls = NULL;
427   env.info_cb = &solver_info_cb;
428   env.bandwidth_changed_cb = &bandwidth_changed_cb;
429   env.get_preferences = &GAS_preference_get_by_peer;
430   env.cfg = cfg;
431   env.stats = GSA_stats;
432   env.addresses = GSA_addresses;
433   env.network_count = GNUNET_ATS_NetworkTypeCount;
434   load_quotas (cfg,
435                env.out_quota,
436                env.in_quota,
437                GNUNET_ATS_NetworkTypeCount);
438   GNUNET_asprintf (&plugin,
439                    "libgnunet_plugin_ats_%s",
440                    mode_str);
441   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
442               "Initializing solver `%s'\n",
443               mode_str);
444   GNUNET_free (mode_str);
445   if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env)))
446   {
447     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
448                 _("Failed to initialize solver `%s'!\n"),
449                 plugin);
450     return GNUNET_SYSERR;
451   }
452   return GNUNET_OK;
453 }
454
455
456 /**
457  * Shutdown address subsystem.
458  */
459 void
460 GAS_plugin_done ()
461 {
462   GNUNET_PLUGIN_unload (plugin,
463                         sf);
464   sf = NULL;
465   GNUNET_free (plugin);
466   plugin = NULL;
467 }
468
469
470 /**
471  * Tell the solver that the given address can now be used
472  * for talking to the respective peer.
473  *
474  * @param new_address the new address
475  * @param addr_net network scope the address is in
476  * @param atsi performance data for the address
477  * @param atsi_count size of the @a atsi array
478  */
479 void
480 GAS_plugin_new_address (struct ATS_Address *new_address,
481                         enum GNUNET_ATS_Network_Type addr_net,
482                         const struct GNUNET_ATS_Information *atsi,
483                         uint32_t atsi_count)
484 {
485   sf->s_add (sf->cls,
486              new_address,
487              addr_net);
488   sf->s_bulk_start (sf->cls);
489   GAS_normalization_update_property (new_address,
490                                         atsi,
491                                         atsi_count);
492   sf->s_bulk_stop (sf->cls);
493 }
494
495
496 /**
497  * Tell the solver that the given address is no longer valid
498  * can cannot be used any longer.
499  *
500  * @param address address that was deleted
501  */
502 void
503 GAS_plugin_delete_address (struct ATS_Address *address)
504 {
505   sf->s_del (sf->cls,
506              address,
507              GNUNET_NO);
508 }
509
510
511 /**
512  * Tell the solver that the given client has expressed its
513  * appreciation for the past performance of a given connection.
514  *
515  * @param application client providing the feedback
516  * @param peer peer the feedback is about
517  * @param scope timeframe the feedback applies to
518  * @param kind performance property the feedback relates to
519  * @param score_abs degree of the appreciation
520  */
521 void
522 GAS_plugin_notify_feedback (struct GNUNET_SERVER_Client *application,
523                             const struct GNUNET_PeerIdentity *peer,
524                             const struct GNUNET_TIME_Relative scope,
525                             enum GNUNET_ATS_PreferenceKind kind,
526                             float score_abs)
527 {
528   sf->s_feedback (sf->cls,
529                   application,
530                   peer,
531                   scope,
532                   kind,
533                   score_abs);
534 }
535
536
537 /**
538  * Stop instant solving, there are many state updates
539  * happening in bulk right now.
540  */
541 void
542 GAS_plugin_solver_lock ()
543 {
544   sf->s_bulk_start (sf->cls);
545 }
546
547
548 /**
549  * Resume instant solving, we are done with the bulk state updates.
550  */
551 void
552 GAS_plugin_solver_unlock ()
553 {
554   sf->s_bulk_start (sf->cls);
555 }
556
557
558 /**
559  * Notify the plugin that a request to connect to
560  * a particular peer was given to us.
561  *
562  * @param pid identity of peer we now care about
563  */
564 void
565 GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid)
566 {
567   const struct ATS_Address *aa;
568
569   aa = sf->s_get (sf->cls, pid);
570   if (NULL == aa)
571   {
572     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
573                 "Cannot suggest address for peer `%s'\n",
574                 GNUNET_i2s (pid));
575     return;
576   }
577   GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
578              "Suggesting address %p for peer `%s'\n",
579              aa,
580              GNUNET_i2s (pid));
581   GAS_scheduling_transmit_address_suggestion (pid,
582                                               aa->session_id,
583                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
584                                               GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
585 }
586
587
588 /**
589  * Notify the plugin that a request to connect to
590  * a particular peer was dropped.
591  *
592  * @param pid identity of peer we care now less about
593  */
594 void
595 GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid)
596 {
597   sf->s_get_stop (sf->cls,
598                   pid);
599 }
600
601
602 /* end of gnunet-service-ats_plugins.c */