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