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