-logging
[oweals/gnunet.git] / src / ats / gnunet-service-ats_addresses.c
1 /*
2  This file is part of GNUnet.
3  Copyright (C) 2011-2015 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_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   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   new_address = create_address (peer,
229                                 plugin_name,
230                                 plugin_addr,
231                                 plugin_addr_len,
232                                 local_address_info,
233                                 session_id);
234   /* Add a new address */
235   new_address->properties = *prop;
236   new_address->t_added = GNUNET_TIME_absolute_get();
237   new_address->t_last_activity = GNUNET_TIME_absolute_get();
238   GNUNET_assert(GNUNET_OK ==
239                 GNUNET_CONTAINER_multipeermap_put (GSA_addresses,
240                                                    peer,
241                                                    new_address,
242                                                    GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE));
243   update_addresses_stat ();
244   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
245               "Adding new address for peer `%s' slot %u\n",
246               GNUNET_i2s (peer),
247               session_id);
248   /* Tell solver about new address */
249   GAS_plugin_solver_lock ();
250   GAS_plugin_new_address (new_address);
251   GAS_normalization_update_property (new_address); // FIXME: needed?
252   GAS_plugin_solver_unlock ();
253   /* Notify performance clients about new address */
254   GAS_performance_notify_all_clients (&new_address->peer,
255                                       new_address->plugin,
256                                       new_address->addr,
257                                       new_address->addr_len,
258                                       new_address->active,
259                                       &new_address->properties,
260                                       new_address->local_address_info,
261                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_out),
262                                       GNUNET_BANDWIDTH_value_init (new_address->assigned_bw_in));
263 }
264
265
266 /**
267  * Update an address with new performance information for a peer.
268  *
269  * @param peer peer
270  * @param session_id session id, never 0
271  * @param prop performance information for this address
272  */
273 void
274 GAS_addresses_update (const struct GNUNET_PeerIdentity *peer,
275                       uint32_t session_id,
276                       const struct GNUNET_ATS_Properties *prop)
277 {
278   struct ATS_Address *aa;
279
280   /* Get existing address */
281   aa = find_exact_address (peer,
282                            session_id);
283   if (NULL == aa)
284   {
285     GNUNET_break (0);
286     return;
287   }
288   if (NULL == aa->solver_information)
289   {
290     GNUNET_break (0);
291     return;
292   }
293   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
294               "Received ADDRESS_UPDATE for peer `%s' slot %u\n",
295               GNUNET_i2s (peer),
296               (unsigned int) session_id);
297
298   /* Update address */
299   aa->t_last_activity = GNUNET_TIME_absolute_get();
300   aa->properties = *prop;
301   /* Notify performance clients about updated address */
302   GAS_performance_notify_all_clients (&aa->peer,
303                                       aa->plugin,
304                                       aa->addr,
305                                       aa->addr_len,
306                                       aa->active,
307                                       prop,
308                                       aa->local_address_info,
309                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_out),
310                                       GNUNET_BANDWIDTH_value_init (aa->assigned_bw_in));
311
312   GAS_normalization_update_property (aa);
313 }
314
315
316 /**
317  * Remove an address for a peer.
318  *
319  * @param peer peer
320  * @param session_id session id, can never be 0
321  */
322 void
323 GAS_addresses_destroy (const struct GNUNET_PeerIdentity *peer,
324                        uint32_t session_id)
325 {
326   struct ATS_Address *ea;
327
328   /* Get existing address */
329   ea = find_exact_address (peer,
330                            session_id);
331   if (NULL == ea)
332   {
333     GNUNET_break (0);
334     return;
335   }
336   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
337               "Received ADDRESS_DESTROYED for peer `%s' session %u\n",
338               GNUNET_i2s (peer),
339               session_id);
340   free_address (ea);
341 }
342
343
344 /**
345  * Initialize address subsystem. The addresses subsystem manages the addresses
346  * known and current performance information. It has a solver component
347  * responsible for the resource allocation. It tells the solver about changes
348  * and receives updates when the solver changes the resource allocation.
349  *
350  * @param server handle to our server
351  */
352 void
353 GAS_addresses_init (struct GNUNET_SERVER_Handle *server)
354 {
355   GSA_addresses = GNUNET_CONTAINER_multipeermap_create (128, GNUNET_NO);
356   update_addresses_stat ();
357 }
358
359
360 /**
361  * Destroy all addresses iterator
362  *
363  * @param cls NULL
364  * @param key peer identity (unused)
365  * @param value the 'struct ATS_Address' to free
366  * @return #GNUNET_OK (continue to iterate)
367  */
368 static int
369 destroy_all_address_it (void *cls,
370                         const struct GNUNET_PeerIdentity *key,
371                         void *value)
372 {
373   struct ATS_Address *aa = value;
374
375   free_address (aa);
376   return GNUNET_OK;
377 }
378
379
380 /**
381  * Remove all addresses
382  */
383 void
384 GAS_addresses_destroy_all ()
385 {
386   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
387               "Destroying all addresses\n");
388   if (0 ==
389       GNUNET_CONTAINER_multipeermap_size (GSA_addresses))
390     return;
391   GAS_plugin_solver_lock ();
392   GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
393                                          &destroy_all_address_it,
394                                          NULL);
395   GAS_plugin_solver_unlock ();
396 }
397
398
399 /**
400  * Shutdown address subsystem.
401  */
402 void
403 GAS_addresses_done ()
404 {
405   GNUNET_log(GNUNET_ERROR_TYPE_INFO,
406              "Shutting down addresses\n");
407   GAS_addresses_destroy_all ();
408   GNUNET_CONTAINER_multipeermap_destroy (GSA_addresses);
409   GSA_addresses = NULL;
410 }
411
412
413 /**
414  * Closure for #peerinfo_it().
415  */
416 struct PeerInfoIteratorContext
417 {
418   /**
419    * Function to call for each address.
420    */
421   GNUNET_ATS_PeerInfo_Iterator it;
422
423   /**
424    * Closure for @e it.
425    */
426   void *it_cls;
427 };
428
429
430 /**
431  * Iterator to iterate over a peer's addresses
432  *
433  * @param cls a `struct PeerInfoIteratorContext`
434  * @param key the peer id
435  * @param value the `struct ATS_address`
436  * @return #GNUNET_OK to continue
437  */
438 static int
439 peerinfo_it (void *cls,
440              const struct GNUNET_PeerIdentity *key,
441              void *value)
442 {
443   struct PeerInfoIteratorContext *pi_ctx = cls;
444   struct ATS_Address *addr = value;
445
446   pi_ctx->it (pi_ctx->it_cls,
447               &addr->peer,
448               addr->plugin,
449               addr->addr,
450               addr->addr_len,
451               addr->active,
452               &addr->properties,
453               addr->local_address_info,
454               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_out),
455               GNUNET_BANDWIDTH_value_init (addr->assigned_bw_in));
456   return GNUNET_OK;
457 }
458
459
460 /**
461  * Return information all peers currently known to ATS
462  *
463  * @param peer the respective peer, NULL for 'all' peers
464  * @param pi_it the iterator to call for every peer
465  * @param pi_it_cls the closure for @a pi_it
466  */
467 void
468 GAS_addresses_get_peer_info (const struct GNUNET_PeerIdentity *peer,
469                              GNUNET_ATS_PeerInfo_Iterator pi_it,
470                              void *pi_it_cls)
471 {
472   struct PeerInfoIteratorContext pi_ctx;
473
474   if (NULL == pi_it)
475   {
476     /* does not make sense without callback */
477     GNUNET_break (0);
478     return;
479   }
480   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
481               "Returning information for %s from a total of %u known addresses\n",
482               (NULL == peer)
483               ? "all peers"
484               : GNUNET_i2s (peer),
485               (unsigned int) GNUNET_CONTAINER_multipeermap_size (GSA_addresses));
486   pi_ctx.it = pi_it;
487   pi_ctx.it_cls = pi_it_cls;
488   if (NULL == peer)
489     GNUNET_CONTAINER_multipeermap_iterate (GSA_addresses,
490                                            &peerinfo_it,
491                                            &pi_ctx);
492   else
493     GNUNET_CONTAINER_multipeermap_get_multiple (GSA_addresses,
494                                                 peer,
495                                                 &peerinfo_it, &pi_ctx);
496   pi_it (pi_it_cls,
497          NULL, NULL, NULL, 0,
498          GNUNET_NO,
499          NULL,
500          GNUNET_HELLO_ADDRESS_INFO_NONE,
501          GNUNET_BANDWIDTH_ZERO,
502          GNUNET_BANDWIDTH_ZERO);
503 }
504
505
506 /**
507  * Information we need for the callbacks to return a list of addresses
508  * back to the client.
509  */
510 struct AddressIteration
511 {
512   /**
513    * Actual handle to the client.
514    */
515   struct GNUNET_SERVER_Client *client;
516
517   /**
518    * Are we sending all addresses, or only those that are active?
519    */
520   int all;
521
522   /**
523    * Which ID should be included in the response?
524    */
525   uint32_t id;
526
527 };
528
529
530 /**
531  * Send a #GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE with the
532  * given address details to the client identified in @a ai.
533  *
534  * @param ai our address information context (identifies the client)
535  * @param id the peer id this address is for
536  * @param plugin_name name of the plugin that supports this address
537  * @param plugin_addr address
538  * @param plugin_addr_len length of @a plugin_addr
539  * @param active #GNUNET_YES if this address is actively used
540  * @param prop performance information
541  * @param local_address_info flags for the address
542  * @param bandwidth_out current outbound bandwidth assigned to address
543  * @param bandwidth_in current inbound bandwidth assigned to address
544  */
545 static void
546 transmit_req_addr (struct AddressIteration *ai,
547                    const struct GNUNET_PeerIdentity *id,
548                    const char *plugin_name,
549                    const void *plugin_addr,
550                    size_t plugin_addr_len,
551                    int active,
552                    const struct GNUNET_ATS_Properties *prop,
553                    enum GNUNET_HELLO_AddressInfo local_address_info,
554                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
555                    struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
556
557 {
558   struct PeerInformationMessage *msg;
559   char *addrp;
560   size_t plugin_name_length;
561   size_t msize;
562   struct GNUNET_SERVER_NotificationContext **uc;
563   struct GNUNET_SERVER_NotificationContext *nc;
564
565   if (NULL != plugin_name)
566     plugin_name_length = strlen (plugin_name) + 1;
567   else
568     plugin_name_length = 0;
569   msize = sizeof (struct PeerInformationMessage) +
570           plugin_addr_len + plugin_name_length;
571   char buf[msize] GNUNET_ALIGN;
572
573   GNUNET_assert (msize < GNUNET_SERVER_MAX_MESSAGE_SIZE);
574   msg = (struct PeerInformationMessage *) buf;
575   msg->header.size = htons (msize);
576   msg->header.type = htons (GNUNET_MESSAGE_TYPE_ATS_ADDRESSLIST_RESPONSE);
577   msg->id = htonl (ai->id);
578   if (NULL != id)
579     msg->peer = *id;
580   else
581     memset (&msg->peer, '\0', sizeof (struct GNUNET_PeerIdentity));
582   msg->address_length = htons (plugin_addr_len);
583   msg->address_active = ntohl (active);
584   msg->plugin_name_length = htons (plugin_name_length);
585   msg->bandwidth_out = bandwidth_out;
586   msg->bandwidth_in = bandwidth_in;
587   if (NULL != prop)
588     GNUNET_ATS_properties_hton (&msg->properties,
589                                 prop);
590   else
591     memset (&msg->properties,
592             0,
593             sizeof (struct GNUNET_ATS_Properties));
594   msg->address_local_info = htonl ((uint32_t) local_address_info);
595   addrp = (char *) &msg[1];
596   if (NULL != plugin_addr)
597     memcpy (addrp, plugin_addr, plugin_addr_len);
598   if (NULL != plugin_name)
599     strcpy (&addrp[plugin_addr_len], plugin_name);
600   uc = GNUNET_SERVER_client_get_user_context (ai->client,
601                                               struct GNUNET_SERVER_NotificationContext *);
602   if (NULL == uc)
603   {
604     GNUNET_break (0);
605     return;
606   }
607   nc = *uc;
608   GNUNET_SERVER_notification_context_unicast (nc,
609                                               ai->client,
610                                               &msg->header,
611                                               GNUNET_NO);
612 }
613
614
615 /**
616  * Iterator for #GAS_addresses_get_peer_info(), called with peer-specific
617  * information to be passed back to the client.
618  *
619  * @param cls closure with our `struct AddressIteration *`
620  * @param id the peer id
621  * @param plugin_name plugin name
622  * @param plugin_addr address
623  * @param plugin_addr_len length of @a plugin_addr
624  * @param active is address actively used
625  * @param prop performance information
626  * @param local_address_info additional local info for the address
627  * @param bandwidth_out current outbound bandwidth assigned to address
628  * @param bandwidth_in current inbound bandwidth assigned to address
629  */
630 static void
631 req_addr_peerinfo_it (void *cls,
632                       const struct GNUNET_PeerIdentity *id,
633                       const char *plugin_name,
634                       const void *plugin_addr,
635                       size_t plugin_addr_len,
636                       int active,
637                       const struct GNUNET_ATS_Properties *prop,
638                       enum GNUNET_HELLO_AddressInfo local_address_info,
639                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_out,
640                       struct GNUNET_BANDWIDTH_Value32NBO bandwidth_in)
641 {
642   struct AddressIteration *ai = cls;
643
644   if ( (NULL == id) &&
645        (NULL == plugin_name) &&
646        (NULL == plugin_addr) )
647   {
648     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
649                 "Address iteration done for one peer\n");
650     return;
651   }
652   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653               "Callback for %s peer `%s' plugin `%s' BW out %u, BW in %u\n",
654               (active == GNUNET_YES) ? "ACTIVE" : "INACTIVE",
655               GNUNET_i2s (id),
656               plugin_name,
657               (unsigned int) ntohl (bandwidth_out.value__),
658               (unsigned int) ntohl (bandwidth_in.value__));
659   /* Transmit result (either if address is active, or if
660      client wanted all addresses) */
661   if ( (GNUNET_YES != ai->all) &&
662        (GNUNET_YES != active))
663     return;
664   transmit_req_addr (ai,
665                      id,
666                      plugin_name,
667                      plugin_addr, plugin_addr_len,
668                      active,
669                      prop,
670                      local_address_info,
671                      bandwidth_out,
672                      bandwidth_in);
673 }
674
675
676 /**
677  * Handle 'address list request' messages from clients.
678  *
679  * @param cls unused, NULL
680  * @param client client that sent the request
681  * @param message the request message
682  */
683 void
684 GAS_handle_request_address_list (void *cls,
685                                  struct GNUNET_SERVER_Client *client,
686                                  const struct GNUNET_MessageHeader *message)
687 {
688   struct AddressIteration ai;
689   const struct AddressListRequestMessage *alrm;
690   struct GNUNET_PeerIdentity allzeros;
691
692   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
693               "Received ADDRESSLIST_REQUEST message\n");
694   alrm = (const struct AddressListRequestMessage *) message;
695   ai.all = ntohl (alrm->all);
696   ai.id = ntohl (alrm->id);
697   ai.client = client;
698
699   memset (&allzeros,
700           '\0',
701           sizeof (struct GNUNET_PeerIdentity));
702   if (0 == memcmp (&alrm->peer,
703                    &allzeros,
704                    sizeof (struct GNUNET_PeerIdentity)))
705   {
706     /* Return addresses for all peers */
707     GAS_addresses_get_peer_info (NULL,
708                                  &req_addr_peerinfo_it,
709                                  &ai);
710   }
711   else
712   {
713     /* Return addresses for a specific peer */
714     GAS_addresses_get_peer_info (&alrm->peer,
715                                  &req_addr_peerinfo_it,
716                                  &ai);
717   }
718   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
719               "Finished handling `%s' message\n",
720               "ADDRESSLIST_REQUEST");
721   transmit_req_addr (&ai,
722                      NULL, NULL, NULL,
723                      0, GNUNET_NO,
724                      NULL,
725                      GNUNET_HELLO_ADDRESS_INFO_NONE,
726                      GNUNET_BANDWIDTH_ZERO,
727                      GNUNET_BANDWIDTH_ZERO);
728   GNUNET_SERVER_receive_done (client,
729                               GNUNET_OK);
730 }
731
732
733
734 /* end of gnunet-service-ats_addresses.c */