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