src: for every AGPL3.0 file, add SPDX identifier.
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 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_addresses.c
23  * @brief ats service address management
24  * @author Matthias Wachs
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "gnunet-service-ats_addresses.h"
29 #include "gnunet-service-ats_performance.h"
30 #include "gnunet-service-ats_normalization.h"
31 #include "gnunet-service-ats_plugins.h"
32
33
34 /**
35  * A multihashmap to store all addresses
36  */
37 struct GNUNET_CONTAINER_MultiPeerMap *GSA_addresses;
38
39
40 /**
41  * Update statistic on number of addresses.
42  */
43 static void
44 update_addresses_stat ()
45 {
46   GNUNET_STATISTICS_set (GSA_stats,
47                          "# addresses",
48                          GNUNET_CONTAINER_multipeermap_size (GSA_addresses),
49                          GNUNET_NO);
50 }
51
52
53 /**
54  * Free the given address
55  *
56  * @param addr address to destroy
57  */
58 static void
59 free_address (struct ATS_Address *addr)
60 {
61   GNUNET_assert (GNUNET_YES ==
62                  GNUNET_CONTAINER_multipeermap_remove (GSA_addresses,
63                                                        &addr->peer,
64                                                        addr));
65   update_addresses_stat ();
66   GAS_plugin_delete_address (addr);
67   GAS_performance_notify_all_clients (&addr->peer,
68                                       addr->plugin,
69                                       addr->addr,
70                                       addr->addr_len,
71                                       GNUNET_NO,
72                                       NULL,
73                                       addr->local_address_info,
74                                       GNUNET_BANDWIDTH_ZERO,
75                                       GNUNET_BANDWIDTH_ZERO);
76   GNUNET_free (addr->plugin);
77   GNUNET_free (addr);
78 }
79
80
81 /**
82  * Initialize @a norm.  Sets all historic values to undefined.
83  *
84  * @param norm normalization data to initialize
85  */
86 static void
87 init_norm (struct GAS_NormalizationInfo *norm)
88 {
89   unsigned int c;
90
91   for (c = 0; c < GAS_normalization_queue_length; c++)
92     norm->atsi_abs[c] = UINT64_MAX;
93 }
94
95
96 /**
97  * Create a ATS_address with the given information
98  *
99  * @param peer peer
100  * @param plugin_name plugin
101  * @param plugin_addr address
102  * @param plugin_addr_len address length
103  * @param local_address_info additional local info for the address
104  * @param session_id session identifier, can never be 0
105  * @return the ATS_Address
106  */
107 static struct ATS_Address *
108 create_address (const struct GNUNET_PeerIdentity *peer,
109                 const char *plugin_name,
110                 const void *plugin_addr,
111                 size_t plugin_addr_len,
112                 uint32_t local_address_info,
113                 uint32_t session_id)
114 {
115   struct ATS_Address *aa;
116
117   aa = GNUNET_malloc (sizeof (struct ATS_Address) + plugin_addr_len);
118   aa->peer = *peer;
119   aa->addr_len = plugin_addr_len;
120   aa->addr = &aa[1];
121   GNUNET_memcpy (&aa[1],
122           plugin_addr,
123           plugin_addr_len);
124   aa->plugin = GNUNET_strdup (plugin_name);
125   aa->session_id = session_id;
126   aa->local_address_info = local_address_info;
127   init_norm (&aa->norm_delay);
128   init_norm (&aa->norm_distance);
129   init_norm (&aa->norm_utilization_in);
130   init_norm (&aa->norm_utilization_out);
131   return aa;
132 }
133
134
135 /**
136  * Closure for #find_address_cb()
137  */
138 struct FindAddressContext
139 {
140   /**
141    * Session Id to look for.
142    */
143   uint32_t session_id;
144
145   /**
146    * Where to store matching address result.
147    */
148   struct ATS_Address *exact_address;
149
150 };
151
152
153 /**
154  * Find session matching given session ID.
155  *
156  * @param cls a `struct FindAddressContext`
157  * @param key peer id
158  * @param value the address to compare with
159  * @return #GNUNET_YES to continue, #GNUNET_NO if address is found
160  */
161 static int
162 find_address_cb (void *cls,
163                  const struct GNUNET_PeerIdentity *key,
164                  void *value)
165 {
166   struct FindAddressContext *fac = cls;
167   struct ATS_Address *aa = value;
168
169   if (aa->session_id == fac->session_id)
170   {
171     fac->exact_address = aa;
172     return GNUNET_NO;
173   }
174   return GNUNET_YES;
175 }
176
177
178 /**
179  * Find the exact address
180  *
181  * @param peer peer
182  * @param session_id session id, can never be 0
183  * @return an ATS_address or NULL
184  */
185 static struct ATS_Address *
186 find_exact_address (const struct GNUNET_PeerIdentity *peer,
187                     uint32_t session_id)
188 {
189   struct FindAddressContext fac;
190
191   fac.exact_address = NULL;
192   fac.session_id = session_id;
193   GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
194                                               peer,
195                                               &find_address_cb, &fac);
196   return fac.exact_address;
197 }
198
199
200 /**
201  * Add a new address for a peer.
202  *
203  * @param peer peer
204  * @param plugin_name transport plugin name
205  * @param plugin_addr plugin address
206  * @param plugin_addr_len length of the plugin address in @a plugin_addr
207  * @param local_address_info the local address for the address
208  * @param session_id session id, can be 0
209  * @param prop performance information for this address
210  */
211 void
212 GAS_addresses_add (const struct GNUNET_PeerIdentity *peer,
213                    const char *plugin_name,
214                    const void *plugin_addr,
215                    size_t plugin_addr_len,
216                    uint32_t local_address_info,
217                    uint32_t session_id,
218                    const struct GNUNET_ATS_Properties *prop)
219 {
220   struct ATS_Address *new_address;
221
222   if (NULL != find_exact_address (peer,
223                                   session_id))
224   {
225     GNUNET_break (0);
226     return;
227   }
228   GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
229   new_address = create_address (peer,
230                                 plugin_name,
231                                 plugin_addr,
232                                 plugin_addr_len,
233                                 local_address_info,
234                                 session_id);
235   /* Add a new address */
236   new_address->properties = *prop;
237   new_address->t_added = GNUNET_TIME_absolute_get();
238   new_address->t_last_activity = GNUNET_TIME_absolute_get();
239   GNUNET_assert(GNUNET_OK ==
240                 GNUNET_CONTAINER_multipeermap_put (GSA_addresses,
241                                                    peer,
242                                                    new_address,
243                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
244   update_addresses_stat ();
245   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
246               "Adding new address for peer `%s' slot %u\n",
247               GNUNET_i2s (peer),
248               session_id);
249   /* Tell solver about new address */
250   GAS_plugin_solver_lock ();
251   GAS_plugin_new_address (new_address);
252   GAS_normalization_update_property (new_address); // FIXME: needed?
253   GAS_plugin_solver_unlock ();
254   /* Notify performance clients about new address */
255   GAS_performance_notify_all_clients (&new_address->peer,
256                                       new_address->plugin,
257                                       new_address->addr,
258                                       new_address->addr_len,
259                                       new_address->active,
260                                       &new_address->properties,
261                                       new_address->local_address_info,
262                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
263                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
264 }
265
266
267 /**
268  * Update an address with new performance information for a peer.
269  *
270  * @param peer peer
271  * @param session_id session id, never 0
272  * @param prop performance information for this address
273  */
274 void
275 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
276                       uint32_t session_id,
277                       const struct GNUNET_ATS_Properties *prop)
278 {
279   struct ATS_Address *aa;
280
281   /* Get existing address */
282   aa = find_exact_address (peer,
283                            session_id);
284   if (NULL == aa)
285   {
286     GNUNET_break (0);
287     return;
288   }
289   if (NULL == aa->solver_information)
290   {
291     GNUNET_break (0);
292     return;
293   }
294   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
295               "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
296               GNUNET_i2s (peer),
297               (unsigned int) session_id);
298   GNUNET_break (GNUNET_NT_UNSPECIFIED != prop->scope);
299   /* Update address */
300   aa->t_last_activity = GNUNET_TIME_absolute_get();
301   aa->properties = *prop;
302   /* Notify performance clients about updated address */
303   GAS_performance_notify_all_clients (&aa->peer,
304                                       aa->plugin,
305                                       aa->addr,
306                                       aa->addr_len,
307                                       aa->active,
308                                       prop,
309                                       aa->local_address_info,
310                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
311                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
312
313   GAS_normalization_update_property (aa);
314 }
315
316
317 /**
318  * Remove an address for a peer.
319  *
320  * @param peer peer
321  * @param session_id session id, can never be 0
322  */
323 void
324 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
325                        uint32_t session_id)
326 {
327   struct ATS_Address *ea;
328
329   /* Get existing address */
330   ea = find_exact_address (peer,
331                            session_id);
332   if (NULL == ea)
333   {
334     GNUNET_break (0);
335     return;
336   }
337   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
338               "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
339               GNUNET_i2s (peer),
340               session_id);
341   free_address (ea);
342 }
343
344
345 /**
346  * Initialize address subsystem. The addresses subsystem manages the addresses
347  * known and current performance information. It has a solver component
348  * responsible for the resource allocation. It tells the solver about changes
349  * and receives updates when the solver changes the resource allocation.
350  */
351 void
352 GAS_addresses_init ()
353 {
354   GSA_addresses
355     = GNUNET_CONTAINER_multipeermap_create (128,
356                                             GNUNET_NO);
357   update_addresses_stat ();
358 }
359
360
361 /**
362  * Destroy all addresses iterator
363  *
364  * @param cls NULL
365  * @param key peer identity (unused)
366  * @param value the 'struct ATS_Address' to free
367  * @return #GNUNET_OK (continue to iterate)
368  */
369 static int
370 destroy_all_address_it (void *cls,
371                         const struct GNUNET_PeerIdentity *key,
372                         void *value)
373 {
374   struct ATS_Address *aa = value;
375
376   free_address (aa);
377   return GNUNET_OK;
378 }
379
380
381 /**
382  * Remove all addresses
383  */
384 void
385 GAS_addresses_destroy_all ()
386 {
387   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
388               "Destroying all addresses\n");
389   if (0 ==
390       GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
391     return;
392   GAS_plugin_solver_lock ();
393   GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
394                                          &destroy_all_address_it,
395                                          NULL);
396   GAS_plugin_solver_unlock ();
397 }
398
399
400 /**
401  * Shutdown address subsystem.
402  */
403 void
404 GAS_addresses_done ()
405 {
406   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
407              "Shutting down addresses\n");
408   GAS_plugin_solver_lock ();
409   GAS_addresses_destroy_all ();
410   GAS_plugin_solver_unlock ();
411   GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
412   GSA_addresses = NULL;
413 }
414
415
416 /**
417  * Closure for #peerinfo_it().
418  */
419 struct PeerInfoIteratorContext
420 {
421   /**
422    * Function to call for each address.
423    */
424   GNUNET_ATS_PeerInfo_Iterator it;
425
426   /**
427    * Closure for @e it.
428    */
429   void *it_cls;
430 };
431
432
433 /**
434  * Iterator to iterate over a peer's addresses
435  *
436  * @param cls a `struct PeerInfoIteratorContext`
437  * @param key the peer id
438  * @param value the `struct ATS_address`
439  * @return #GNUNET_OK to continue
440  */
441 static int
442 peerinfo_it (void *cls,
443              const struct GNUNET_PeerIdentity *key,
444              void *value)
445 {
446   struct PeerInfoIteratorContext *pi_ctx = cls;
447   struct ATS_Address *addr = value;
448
449   pi_ctx->it (pi_ctx->it_cls,
450               &addr->peer,
451               addr->plugin,
452               addr->addr,
453               addr->addr_len,
454               addr->active,
455               &addr->properties,
456               addr->local_address_info,
457               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
458               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
459   return GNUNET_OK;
460 }
461
462
463 /**
464  * Return information all peers currently known to ATS
465  *
466  * @param peer the respective peer, NULL for 'all' peers
467  * @param pi_it the iterator to call for every peer
468  * @param pi_it_cls the closure for @a pi_it
469  */
470 void
471 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
472                              GNUNET_ATS_PeerInfo_Iterator pi_it,
473                              void *pi_it_cls)
474 {
475   struct PeerInfoIteratorContext pi_ctx;
476
477   if (NULL == pi_it)
478   {
479     /* does not make sense without callback */
480     GNUNET_break (0);
481     return;
482   }
483   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484               "Returning information for %s from a total of %u known addresses\n",
485               (NULL == peer)
486               ? "all peers"
487               : GNUNET_i2s (peer),
488               (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
489   pi_ctx.it = pi_it;
490   pi_ctx.it_cls = pi_it_cls;
491   if (NULL == peer)
492     GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
493                                            &peerinfo_it,
494                                            &pi_ctx);
495   else
496     GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
497                                                 peer,
498                                                 &peerinfo_it, &pi_ctx);
499   pi_it (pi_it_cls,
500          NULL, NULL, NULL, 0,
501          GNUNET_NO,
502          NULL,
503          GNUNET_HELLO_ADDRESS_INFO_NONE,
504          GNUNET_BANDWIDTH_ZERO,
505          GNUNET_BANDWIDTH_ZERO);
506 }
507
508
509 /**
510  * Information we need for the callbacks to return a list of addresses
511  * back to the client.
512  */
513 struct AddressIteration
514 {
515   /**
516    * Actual handle to the client.
517    */
518   struct GNUNET_SERVICE_Client *client;
519
520   /**
521    * Are we sending all addresses, or only those that are active?
522    */
523   int all;
524
525   /**
526    * Which ID should be included in the response?
527    */
528   uint32_t id;
529
530 };
531
532
533 /**
534  * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
535  * given address details to the client identified in @a ai.
536  *
537  * @param ai our address information context (identifies the client)
538  * @param id the peer id this address is for
539  * @param plugin_name name of the plugin that supports this address
540  * @param plugin_addr address
541  * @param plugin_addr_len length of @a plugin_addr
542  * @param active #GNUNET_YES if this address is actively used
543  * @param prop performance information
544  * @param local_address_info flags for the address
545  * @param bandwidth_out current outbound bandwidth assigned to address
546  * @param bandwidth_in current inbound bandwidth assigned to address
547  */
548 static void
549 transmit_req_addr (struct AddressIteration *ai,
550                    const struct GNUNET_PeerIdentity *id,
551                    const char *plugin_name,
552                    const void *plugin_addr,
553                    size_t plugin_addr_len,
554                    int active,
555                    const struct GNUNET_ATS_Properties *prop,
556                    enum GNUNET_HELLO_AddressInfo local_address_info,
557                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
558                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
559
560 {
561   struct GNUNET_MQ_Envelope *env;
562   struct PeerInformationMessage *msg;
563   char *addrp;
564   size_t plugin_name_length;
565   size_t msize;
566
567   if (NULL != plugin_name)
568     plugin_name_length = strlen (plugin_name) + 1;
569   else
570     plugin_name_length = 0;
571   msize = plugin_addr_len + plugin_name_length;
572
573   GNUNET_assert (sizeof (struct PeerInformationMessage) + msize
574                  < GNUNET_MAX_MESSAGE_SIZE);
575   env = GNUNET_MQ_msg_extra (msg,
576                              msize,
577                              GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
578   msg->id = htonl (ai->id);
579   if (NULL != id)
580     msg->peer = *id;
581   msg->address_length = htons (plugin_addr_len);
582   msg->address_active = ntohl (active);
583   msg->plugin_name_length = htons (plugin_name_length);
584   msg->bandwidth_out = bandwidth_out;
585   msg->bandwidth_in = bandwidth_in;
586   if (NULL != prop)
587     GNUNET_ATS_properties_hton (&msg->properties,
588                                 prop);
589   msg->address_local_info = htonl ((uint32_t) local_address_info);
590   addrp = (char *) &msg[1];
591   GNUNET_memcpy (addrp,
592                  plugin_addr,
593                  plugin_addr_len);
594   if (NULL != plugin_name)
595     strcpy (&addrp[plugin_addr_len],
596             plugin_name);
597   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (ai->client),
598                   env);
599 }
600
601
602 /**
603  * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
604  * information to be passed back to the client.
605  *
606  * @param cls closure with our `struct AddressIteration *`
607  * @param id the peer id
608  * @param plugin_name plugin name
609  * @param plugin_addr address
610  * @param plugin_addr_len length of @a plugin_addr
611  * @param active is address actively used
612  * @param prop performance information
613  * @param local_address_info additional local info for the address
614  * @param bandwidth_out current outbound bandwidth assigned to address
615  * @param bandwidth_in current inbound bandwidth assigned to address
616  */
617 static void
618 req_addr_peerinfo_it (void *cls,
619                       const struct GNUNET_PeerIdentity *id,
620                       const char *plugin_name,
621                       const void *plugin_addr,
622                       size_t plugin_addr_len,
623                       int active,
624                       const struct GNUNET_ATS_Properties *prop,
625                       enum GNUNET_HELLO_AddressInfo local_address_info,
626                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
627                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
628 {
629   struct AddressIteration *ai = cls;
630
631   if ( (NULL == id) &&
632        (NULL == plugin_name) &&
633        (NULL == plugin_addr) )
634   {
635     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
636                 "Address iteration done for one peer\n");
637     return;
638   }
639   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
640               "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
641               (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
642               GNUNET_i2s (id),
643               plugin_name,
644               (unsigned int) ntohl (bandwidth_out.value__),
645               (unsigned int) ntohl (bandwidth_in.value__));
646   /* Transmit result (either if address is active, or if
647      client wanted all addresses) */
648   if ( (GNUNET_YES != ai->all) &&
649        (GNUNET_YES != active))
650     return;
651   transmit_req_addr (ai,
652                      id,
653                      plugin_name,
654                      plugin_addr, plugin_addr_len,
655                      active,
656                      prop,
657                      local_address_info,
658                      bandwidth_out,
659                      bandwidth_in);
660 }
661
662
663 /**
664  * Handle 'address list request' messages from clients.
665  *
666  * @param cls client that sent the request
667  * @param alrm the request message
668  */
669 void
670 GAS_handle_request_address_list (struct GNUNET_SERVICE_Client *client,
671                                  const struct AddressListRequestMessage *alrm)
672 {
673   struct AddressIteration ai;
674   struct GNUNET_PeerIdentity allzeros;
675
676   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677               "Received ADDRESSLIST_REQUEST message\n");
678   ai.all = ntohl (alrm->all);
679   ai.id = ntohl (alrm->id);
680   ai.client = client;
681
682   memset (&allzeros,
683           '\0',
684           sizeof (struct GNUNET_PeerIdentity));
685   if (0 == memcmp (&alrm->peer,
686                    &allzeros,
687                    sizeof (struct GNUNET_PeerIdentity)))
688   {
689     /* Return addresses for all peers */
690     GAS_addresses_get_peer_info (NULL,
691                                  &req_addr_peerinfo_it,
692                                  &ai);
693   }
694   else
695   {
696     /* Return addresses for a specific peer */
697     GAS_addresses_get_peer_info (&alrm->peer,
698                                  &req_addr_peerinfo_it,
699                                  &ai);
700   }
701   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
702               "Finished handling `%s' message\n",
703               "ADDRESSLIST_REQUEST");
704   transmit_req_addr (&ai,
705                      NULL, NULL, NULL,
706                      0, GNUNET_NO,
707                      NULL,
708                      GNUNET_HELLO_ADDRESS_INFO_NONE,
709                      GNUNET_BANDWIDTH_ZERO,
710                      GNUNET_BANDWIDTH_ZERO);
711 }
712
713
714
715 /* end of gnunet-service-ats_addresses.c */