paragraph for gnunet devs that don't know how to use the web
[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 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 /**
20  * @file ats/gnunet-service-ats_plugins.c
21  * @brief ats service plugin management
22  * @author Matthias Wachs
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_ats_plugin.h"
27 #include "gnunet-service-ats_connectivity.h"
28 #include "gnunet-service-ats_performance.h"
29 #include "gnunet-service-ats_preferences.h"
30 #include "gnunet-service-ats_plugins.h"
31 #include "gnunet-service-ats_reservations.h"
32 #include "gnunet-service-ats_scheduling.h"
33 #include "gnunet-service-ats_normalization.h"
34
35
36 /**
37  * Solver handle.
38  */
39 static struct GNUNET_ATS_SolverFunctions *sf;
40
41 /**
42  * Solver environment.
43  */
44 static struct GNUNET_ATS_PluginEnvironment env;
45
46 /**
47  * Solver plugin name as string
48  */
49 static char *plugin;
50
51
52 /**
53  * The preference changed for a peer, update solver.
54  *
55  * @param peer the peer
56  * @param kind the ATS kind
57  * @param pref_rel the new relative preference value
58  */
59 void
60 GAS_plugin_notify_preference_changed (const struct GNUNET_PeerIdentity *peer,
61                                       enum GNUNET_ATS_PreferenceKind kind,
62                                       double pref_rel)
63 {
64   sf->s_pref (sf->cls,
65               peer,
66               kind,
67               pref_rel);
68 }
69
70
71 /**
72  * The relative value for a property changed.
73  *
74  * @param address the peer for which a property changed
75  */
76 void
77 GAS_plugin_notify_property_changed (struct ATS_Address *address)
78 {
79   sf->s_address_update_property (sf->cls,
80                                  address);
81 }
82
83
84 /**
85  * Solver information callback
86  *
87  * @param cls the closure
88  * @param op the operation
89  * @param status operation status
90  * @param add additional information
91  */
92 static void
93 solver_info_cb (void *cls,
94                 enum GAS_Solver_Operation op,
95                 enum GAS_Solver_Status status,
96                 enum GAS_Solver_Additional_Information add)
97 {
98   const char *add_info;
99
100   switch (add) {
101     case GAS_INFO_NONE:
102       add_info = "GAS_INFO_NONE";
103       break;
104     case GAS_INFO_FULL:
105       add_info = "GAS_INFO_MLP_FULL";
106       break;
107     case GAS_INFO_UPDATED:
108       add_info = "GAS_INFO_MLP_UPDATED";
109       break;
110     case GAS_INFO_PROP_ALL:
111       add_info = "GAS_INFO_PROP_ALL";
112       break;
113     case GAS_INFO_PROP_SINGLE:
114       add_info = "GAS_INFO_PROP_SINGLE";
115       break;
116     default:
117       add_info = "INVALID";
118       break;
119   }
120   switch (op)
121   {
122   case GAS_OP_SOLVE_START:
123     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
124                 "Solver notifies `%s' with result `%s' `%s'\n",
125                 "GAS_OP_SOLVE_START",
126                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL",
127                 add_info);
128     return;
129   case GAS_OP_SOLVE_STOP:
130     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131                 "Solver notifies `%s' with result `%s'\n",
132                 "GAS_OP_SOLVE_STOP",
133                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
134     return;
135   case GAS_OP_SOLVE_SETUP_START:
136     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
137                 "Solver notifies `%s' with result `%s'\n",
138                 "GAS_OP_SOLVE_SETUP_START",
139                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
140     return;
141   case GAS_OP_SOLVE_SETUP_STOP:
142     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
143                 "Solver notifies `%s' with result `%s'\n",
144                 "GAS_OP_SOLVE_SETUP_STOP",
145                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
146     return;
147   case GAS_OP_SOLVE_MLP_LP_START:
148     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149                 "Solver notifies `%s' with result `%s'\n",
150                 "GAS_OP_SOLVE_LP_START",
151                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
152     return;
153   case GAS_OP_SOLVE_MLP_LP_STOP:
154     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
155                 "Solver notifies `%s' with result `%s'\n",
156                 "GAS_OP_SOLVE_LP_STOP",
157                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
158     return;
159   case GAS_OP_SOLVE_MLP_MLP_START:
160     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
161                 "Solver notifies `%s' with result `%s'\n",
162                 "GAS_OP_SOLVE_MLP_START",
163                 (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
164     return;
165   case GAS_OP_SOLVE_MLP_MLP_STOP:
166     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
167                "Solver notifies `%s' with result `%s'\n",
168                "GAS_OP_SOLVE_MLP_STOP",
169                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
170     return;
171   case GAS_OP_SOLVE_UPDATE_NOTIFICATION_START:
172     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
173                "Solver notifies `%s' with result `%s'\n",
174                "GAS_OP_SOLVE_UPDATE_NOTIFICATION_START",
175                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
176     return;
177   case GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP:
178     GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
179                "Solver notifies `%s' with result `%s'\n",
180                "GAS_OP_SOLVE_UPDATE_NOTIFICATION_STOP",
181                (GAS_STAT_SUCCESS == status) ? "SUCCESS" : "FAIL");
182     return;
183   default:
184     GNUNET_break (0);
185     break;
186   }
187 }
188
189
190 /**
191  * Callback for solver to notify about assignment changes
192  *
193  * @param cls NULL
194  * @param address the address with changes
195  */
196 static void
197 bandwidth_changed_cb (void *cls,
198                       struct ATS_Address *address)
199 {
200   long long diff_out;
201   long long diff_in;
202
203   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
204               "Bandwidth assignment changed for peer %s to %u/%u\n",
205               GNUNET_i2s (&address->peer),
206               (unsigned int) address->assigned_bw_in,
207               (unsigned int) address->assigned_bw_out);
208   GAS_reservations_set_bandwidth (&address->peer,
209                                   GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
210   /* Notify performance clients about changes to address */
211   GAS_performance_notify_all_clients (&address->peer,
212                                       address->plugin,
213                                       address->addr,
214                                       address->addr_len,
215                                       address->active,
216                                       &address->properties,
217                                       address->local_address_info,
218                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_out),
219                                       GNUNET_BANDWIDTH_value_init (address->assigned_bw_in));
220
221   if ( (0 == address->assigned_bw_in) &&
222        (0 == address->assigned_bw_out) )
223   {
224     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
225                "Telling transport to disconnect peer `%s'\n",
226                 GNUNET_i2s (&address->peer));
227
228     /* Notify scheduling clients about suggestion */
229     GAS_scheduling_transmit_address_suggestion (&address->peer,
230                                                 address->session_id,
231                                                 GNUNET_BANDWIDTH_ZERO,
232                                                 GNUNET_BANDWIDTH_ZERO);
233     return;
234   }
235
236   /* Do bandwidth stability check */
237   diff_out = llabs ((long long) address->assigned_bw_out -
238                     (long long) address->last_notified_bw_out);
239   diff_in = llabs ((long long) address->assigned_bw_in -
240                    (long long) address->last_notified_bw_in);
241   if ( (diff_out < htonl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) &&
242        (diff_in < htonl (GNUNET_CONSTANTS_DEFAULT_BW_IN_OUT.value__)) )
243   {
244     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
245                 "Bandwidth change too small, not notifying client\n");
246     return;
247   }
248
249   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
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                 (unsigned long long) 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                 (unsigned long long) 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.get_connectivity = &GAS_connectivity_has_peer;
431   env.cfg = cfg;
432   env.stats = GSA_stats;
433   env.addresses = GSA_addresses;
434   env.network_count = GNUNET_ATS_NetworkTypeCount;
435   load_quotas (cfg,
436                env.out_quota,
437                env.in_quota,
438                GNUNET_ATS_NetworkTypeCount);
439   GNUNET_asprintf (&plugin,
440                    "libgnunet_plugin_ats_%s",
441                    mode_str);
442   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443               "Initializing solver `%s'\n",
444               mode_str);
445   GNUNET_free (mode_str);
446   if (NULL == (sf = GNUNET_PLUGIN_load (plugin, &env)))
447   {
448     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
449                 _("Failed to initialize solver `%s'!\n"),
450                 plugin);
451     return GNUNET_SYSERR;
452   }
453   return GNUNET_OK;
454 }
455
456
457 /**
458  * Shutdown address subsystem.
459  */
460 void
461 GAS_plugin_done ()
462 {
463   GNUNET_PLUGIN_unload (plugin,
464                         sf);
465   sf = NULL;
466   GNUNET_free (plugin);
467   plugin = NULL;
468 }
469
470
471 /**
472  * Tell the solver that the given address can now be used
473  * for talking to the respective peer.
474  *
475  * @param new_address the new address
476  */
477 void
478 GAS_plugin_new_address (struct ATS_Address *new_address)
479 {
480   sf->s_add (sf->cls,
481              new_address,
482              new_address->properties.scope); /* FIXME: remove 3rd arg here! */
483 }
484
485
486 /**
487  * Tell the solver that the given address is no longer valid
488  * can cannot be used any longer.
489  *
490  * @param address address that was deleted
491  */
492 void
493 GAS_plugin_delete_address (struct ATS_Address *address)
494 {
495   sf->s_del (sf->cls,
496              address);
497 }
498
499
500 /**
501  * Tell the solver that the given client has expressed its
502  * appreciation for the past performance of a given connection.
503  *
504  * @param application client providing the feedback
505  * @param peer peer the feedback is about
506  * @param scope timeframe the feedback applies to
507  * @param kind performance property the feedback relates to
508  * @param score_abs degree of the appreciation
509  */
510 void
511 GAS_plugin_notify_feedback (struct GNUNET_SERVICE_Client *application,
512                             const struct GNUNET_PeerIdentity *peer,
513                             const struct GNUNET_TIME_Relative scope,
514                             enum GNUNET_ATS_PreferenceKind kind,
515                             float score_abs)
516 {
517   sf->s_feedback (sf->cls,
518                   application,
519                   peer,
520                   scope,
521                   kind,
522                   score_abs);
523 }
524
525
526 /**
527  * Stop instant solving, there are many state updates
528  * happening in bulk right now.
529  */
530 void
531 GAS_plugin_solver_lock ()
532 {
533   sf->s_bulk_start (sf->cls);
534 }
535
536
537 /**
538  * Resume instant solving, we are done with the bulk state updates.
539  */
540 void
541 GAS_plugin_solver_unlock ()
542 {
543   sf->s_bulk_stop (sf->cls);
544 }
545
546
547 /**
548  * Notify the plugin that a request to connect to
549  * a particular peer was given to us.
550  *
551  * @param pid identity of peer we now care about
552  */
553 void
554 GAS_plugin_request_connect_start (const struct GNUNET_PeerIdentity *pid)
555 {
556   sf->s_get (sf->cls,
557              pid);
558 }
559
560
561 /**
562  * Notify the plugin that a request to connect to
563  * a particular peer was dropped.
564  *
565  * @param pid identity of peer we care now less about
566  */
567 void
568 GAS_plugin_request_connect_stop (const struct GNUNET_PeerIdentity *pid)
569 {
570   sf->s_get_stop (sf->cls,
571                   pid);
572 }
573
574
575 /* end of gnunet-service-ats_plugins.c */